Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions chains/evm/contracts/TokenPoolFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {RegistryModuleOwnerCustom} from "./tokenAdminRegistry/RegistryModuleOwne
import {CrossChainToken} from "./tokens/CrossChainToken.sol";
import {AuthorizedCallers} from "@chainlink/contracts/src/v0.8/shared/access/AuthorizedCallers.sol";

import {SafeERC20} from "@openzeppelin/contracts@5.3.0/token/ERC20/utils/SafeERC20.sol";
import {Create2} from "@openzeppelin/contracts@5.3.0/utils/Create2.sol";

/// @notice A contract for deploying new tokens and token pools, and configuring them with the token admin registry.
Expand All @@ -20,6 +21,7 @@ import {Create2} from "@openzeppelin/contracts@5.3.0/utils/Create2.sol";
/// @dev The address prediction mechanism is only capable of deploying and predicting addresses for EVM based chains.
/// adding compatibility for other chains will require additional offchain computation.
contract TokenPoolFactory is ITypeAndVersion {
using SafeERC20 for CrossChainToken;
using Create2 for bytes32;

error InvalidZeroAddress();
Expand Down Expand Up @@ -160,6 +162,12 @@ contract TokenPoolFactory is ITypeAndVersion {
crossChainToken.renounceRole(crossChainToken.BURN_MINT_ADMIN_ROLE(), address(this));
}

// If any tokens were sent to this contract (e.g. preMintRecipient was set to the factory), transfer them to the future owner.
uint256 factoryTokenBalance = crossChainToken.balanceOf(address(this));
if (factoryTokenBalance > 0) {
crossChainToken.safeTransfer(futureOwner, factoryTokenBalance);
}

// Set the token pool for token in the token admin registry since this contract is the ccipAdmin.
_setTokenPoolInTokenAdminRegistry(token, pool, futureOwner);

Expand Down
16 changes: 14 additions & 2 deletions chains/evm/contracts/test/Proxy/Proxy.withdrawFeeTokens.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ contract Proxy_withdrawFeeTokens is ProxySetup {
s_feeToken = address(
new CrossChainToken(
BaseERC20.ConstructorParams({
name: "Chainlink Token", symbol: "LINK", decimals: 18, maxSupply: 0, preMint: 0, ccipAdmin: OWNER
name: "Chainlink Token",
symbol: "LINK",
decimals: 18,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: OWNER
}),
OWNER,
OWNER
Expand Down Expand Up @@ -63,7 +69,13 @@ contract Proxy_withdrawFeeTokens is ProxySetup {
address token2 = address(
new CrossChainToken(
BaseERC20.ConstructorParams({
name: "Token2", symbol: "TK2", decimals: 18, maxSupply: 0, preMint: 0, ccipAdmin: OWNER
name: "Token2",
symbol: "TK2",
decimals: 18,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: OWNER
}),
OWNER,
OWNER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: OWNER
}),
address(0),
Expand Down Expand Up @@ -263,6 +264,9 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
address(0)
);

assertEq(PREMINT_AMOUNT, IERC20Metadata(tokenAddress).totalSupply(), "Total supply should match premint amount");
assertEq(PREMINT_AMOUNT, IERC20Metadata(tokenAddress).balanceOf(OWNER), "The OWNER should have the tokens");

assertEq(address(TokenPool(poolAddress).getToken()), tokenAddress, "Token Address should have been set locally");

assertEq(
Expand Down Expand Up @@ -376,6 +380,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: OWNER
}),
address(0),
Expand Down Expand Up @@ -441,6 +446,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: OWNER
}),
address(0),
Expand All @@ -454,6 +460,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: OWNER
}),
address(0),
Expand Down Expand Up @@ -611,6 +618,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: 6,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: OWNER
}),
address(0),
Expand Down Expand Up @@ -715,13 +723,20 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
vm.stopPrank();
address deployer = makeAddr("deployer");
address futureOwner = makeAddr("futureOwner");
address preMintRecipient = makeAddr("preMintRecipient");
address factory = address(s_tokenPoolFactory);

bytes memory tokenInitCode = abi.encodePacked(
type(CrossChainToken).creationCode,
abi.encode(
BaseERC20.ConstructorParams({
name: "TestToken", symbol: "TT", decimals: LOCAL_TOKEN_DECIMALS, maxSupply: 0, preMint: 0, ccipAdmin: factory
name: "TestToken",
symbol: "TT",
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: 0,
preMint: PREMINT_AMOUNT,
preMintRecipient: preMintRecipient,
ccipAdmin: factory
}),
factory,
futureOwner
Expand All @@ -741,6 +756,13 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
futureOwner
);

assertEq(PREMINT_AMOUNT, IERC20Metadata(tokenAddress).totalSupply(), "Total supply should match premint amount");
assertEq(
PREMINT_AMOUNT,
IERC20Metadata(tokenAddress).balanceOf(preMintRecipient),
"All tokens should be minted to preMintRecipient"
);

// futureOwner accepts the 2-step transfers
vm.startPrank(futureOwner);
s_tokenAdminRegistry.acceptAdminRole(tokenAddress);
Expand Down Expand Up @@ -798,6 +820,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: deployer
}),
deployer,
Expand Down Expand Up @@ -859,6 +882,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: address(s_tokenPoolFactory)
}),
address(s_tokenPoolFactory),
Expand Down Expand Up @@ -941,6 +965,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: address(s_tokenPoolFactory)
}),
address(s_tokenPoolFactory),
Expand Down Expand Up @@ -1052,6 +1077,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: OWNER
}),
address(0),
Expand All @@ -1064,6 +1090,7 @@ contract TokenPoolFactory_deployTokenAndTokenPool is TokenPoolFactorySetup {
decimals: LOCAL_TOKEN_DECIMALS,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: OWNER
}),
address(0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,13 @@ contract TokenPoolFactorySetup is TokenAdminRegistrySetup {
) internal pure returns (bytes memory) {
bytes memory tokenCreationParams = abi.encode(
BaseERC20.ConstructorParams({
name: "TestToken", symbol: "TT", decimals: 18, maxSupply: type(uint256).max, preMint: 0, ccipAdmin: factory
name: "TestToken",
symbol: "TT",
decimals: 18,
maxSupply: type(uint256).max,
preMint: PREMINT_AMOUNT,
preMintRecipient: OWNER,
ccipAdmin: factory
}),
factory,
OWNER
Expand Down
8 changes: 7 additions & 1 deletion chains/evm/contracts/test/TokenSetup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ contract TokenSetup is BaseTest {
) internal returns (CrossChainToken) {
return new CrossChainToken(
BaseERC20.ConstructorParams({
name: tokenName, symbol: tokenName, decimals: decimals, maxSupply: 0, preMint: 0, ccipAdmin: OWNER
name: tokenName,
symbol: tokenName,
decimals: decimals,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: OWNER
}),
OWNER,
OWNER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,13 @@ contract EtherSenderReceiverTestSetup is Test {
address(
new CrossChainToken(
BaseERC20.ConstructorParams({
name: "Chainlink Token", symbol: "LINK", decimals: 18, maxSupply: 0, preMint: 0, ccipAdmin: OWNER
name: "Chainlink Token",
symbol: "LINK",
decimals: 18,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: OWNER
}),
OWNER,
OWNER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ contract CCTPVerifier_withdrawFeeTokens is CCTPVerifierSetup {
address token2 = address(
new CrossChainToken(
BaseERC20.ConstructorParams({
name: "Token2", symbol: "TK2", decimals: 18, maxSupply: 0, preMint: 0, ccipAdmin: OWNER
name: "Token2",
symbol: "TK2",
decimals: 18,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: OWNER
}),
OWNER,
OWNER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ contract CCTPVerifierSetup is BaseVerifierSetup {

CrossChainToken usdcToken = new CrossChainToken(
BaseERC20.ConstructorParams({
name: "USD Coin", symbol: "USDC", decimals: 6, maxSupply: 0, preMint: 0, ccipAdmin: OWNER
name: "USD Coin",
symbol: "USDC",
decimals: 6,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: OWNER
}),
OWNER,
OWNER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,13 @@ contract CommitteeVerifier_withdrawFeeTokens is CommitteeVerifierSetup {
address token2 = address(
new CrossChainToken(
BaseERC20.ConstructorParams({
name: "Token2", symbol: "TK2", decimals: 18, maxSupply: 0, preMint: 0, ccipAdmin: OWNER
name: "Token2",
symbol: "TK2",
decimals: 18,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: OWNER
}),
OWNER,
OWNER
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,13 @@ contract LombardVerifier_forwardToVerifier is LombardVerifierSetup {
address tokenWithAdapter = address(
new CrossChainToken(
BaseERC20.ConstructorParams({
name: "Token With Adapter", symbol: "TWA", decimals: 18, maxSupply: 0, preMint: 0, ccipAdmin: OWNER
name: "Token With Adapter",
symbol: "TWA",
decimals: 18,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: OWNER
}),
OWNER,
OWNER
Expand Down Expand Up @@ -136,7 +142,13 @@ contract LombardVerifier_forwardToVerifier is LombardVerifierSetup {
address tokenWithAdapter = address(
new CrossChainToken(
BaseERC20.ConstructorParams({
name: "Token With Adapter", symbol: "TWA", decimals: 18, maxSupply: 0, preMint: 0, ccipAdmin: address(0)
name: "Token With Adapter",
symbol: "TWA",
decimals: 18,
maxSupply: 0,
preMint: 0,
preMintRecipient: address(0),
ccipAdmin: address(0)
}),
address(0),
address(0)
Expand Down
Loading
Loading