Skip to content

Commit

Permalink
refactor(Factory): removing token transfer functions
Browse files Browse the repository at this point in the history
  • Loading branch information
re1ro committed Dec 5, 2023
1 parent 68ab282 commit c7b6a01
Show file tree
Hide file tree
Showing 4 changed files with 11 additions and 251 deletions.
77 changes: 1 addition & 76 deletions contracts/InterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
bytes32 private constant CONTRACT_ID = keccak256('interchain-token-factory');
bytes32 internal constant PREFIX_CANONICAL_TOKEN_SALT = keccak256('canonical-token-salt');
bytes32 internal constant PREFIX_INTERCHAIN_TOKEN_SALT = keccak256('interchain-token-salt');
bytes32 internal constant PREFIX_DEPLOYER_BALANCE = keccak256('deployer-balance');
address private constant TOKEN_FACTORY_DEPLOYER = address(0);

/**
Expand Down Expand Up @@ -105,13 +104,6 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
tokenAddress = service.interchainTokenAddress(interchainTokenId(deployer, salt));
}

function deployerTokenBalance(bytes32 tokenId, address deployer) public view returns (uint256 deployerBalance) {
bytes32 balanceKey = keccak256(abi.encode(PREFIX_DEPLOYER_BALANCE, tokenId, deployer));
assembly {
deployerBalance := sload(balanceKey)
}
}

/**
* @notice Deploys a new interchain token with specified parameters.
* @dev Creates a new token and optionally mints an initial amount to a specified minter.
Expand Down Expand Up @@ -147,11 +139,9 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
IInterchainToken token = IInterchainToken(service.interchainTokenAddress(tokenId));
ITokenManager tokenManager = ITokenManager(service.tokenManagerAddress(tokenId));

_setDeployerTokenBalance(tokenId, sender, initialSupply);
token.mint(address(this), initialSupply);
token.mint(minter != address(0) ? minter : sender, initialSupply);

token.transferMintership(minter);

tokenManager.removeFlowLimiter(address(this));

// If minter == address(0), we still set it as a flow limiter for consistency with the remote token manager.
Expand Down Expand Up @@ -283,64 +273,6 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
_deployInterchainToken(salt, destinationChain, tokenName, tokenSymbol, tokenDecimals, '', gasValue);
}

/**
* @notice Transfers an interchain token to a specified destination chain and address.
* @param tokenId The identifier of the interchain token.
* @param destinationChain The name of the destination chain.
* @param destinationAddress The address on the destination chain to receive the token.
* @param amount The amount of tokens to transfer.
* @param gasValue The amount of gas to send for the transfer.
*/
function interchainTransfer(
bytes32 tokenId,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
uint256 gasValue
) external payable {
address sender = msg.sender;
uint256 balance = deployerTokenBalance(tokenId, sender);

if (amount > balance) revert InsufficientBalance(tokenId, sender, balance);

_setDeployerTokenBalance(tokenId, sender, balance - amount);

if (bytes(destinationChain).length == 0) {
address tokenAddress = service.interchainTokenAddress(tokenId);
IInterchainToken token = IInterchainToken(tokenAddress);
token.safeTransfer(destinationAddress.toAddress(), amount);
} else {
// slither-disable-next-line arbitrary-send-eth
service.interchainTransfer{ value: gasValue }(tokenId, destinationChain, destinationAddress, amount, new bytes(0));
}
}

/**
* @notice Allows tokens to be transferred from the sender to the contract.
* @param tokenId The identifier of the interchain token.
* @param amount The amount of tokens to transfer.
*/
function tokenTransferFrom(bytes32 tokenId, uint256 amount) external payable {
address tokenAddress = service.validTokenAddress(tokenId);
IInterchainToken token = IInterchainToken(tokenAddress);

_setDeployerTokenBalance(tokenId, msg.sender, deployerTokenBalance(tokenId, msg.sender) + amount);

token.safeTransferFrom(msg.sender, address(this), amount);
}

/**
* @notice Approves a specified amount of tokens to the service.
* @param tokenId The identifier of the interchain token.
* @param amount The amount of tokens to approve.
*/
function tokenApprove(bytes32 tokenId, uint256 amount) external payable {
address tokenAddress = service.validTokenAddress(tokenId);
IInterchainToken token = IInterchainToken(tokenAddress);

token.safeCall(abi.encodeWithSelector(token.approve.selector, service, amount));
}

/**
* @notice Checks if a given token is a gateway token.
* @param token The address of the token to check.
Expand All @@ -350,11 +282,4 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
string memory symbol = IInterchainToken(token).symbol();
return token == gateway.tokenAddresses(symbol);
}

function _setDeployerTokenBalance(bytes32 tokenId, address deployer, uint256 deployerBalance) internal {
bytes32 balanceKey = keccak256(abi.encode(PREFIX_DEPLOYER_BALANCE, tokenId, deployer));
assembly {
sstore(balanceKey, deployerBalance)
}
}
}
32 changes: 0 additions & 32 deletions contracts/interfaces/IInterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ interface IInterchainTokenFactory {
error InvalidChainName();
error NotMinter(address minter);
error NotOperator(address operator);
error InsufficientBalance(bytes32 tokenId, address deployer, uint256 balance);
error ApproveFailed();
error GatewayToken(address tokenAddress);

/**
Expand Down Expand Up @@ -115,34 +113,4 @@ interface IInterchainTokenFactory {
string calldata destinationChain,
uint256 gasValue
) external payable;

/**
* @notice Transfers an interchain token to a specified destination chain and address.
* @param tokenId The identifier of the interchain token.
* @param destinationChain The name of the destination chain.
* @param destinationAddress The address on the destination chain to receive the token.
* @param amount The amount of tokens to transfer.
* @param gasValue The amount of gas to send for the transfer.
*/
function interchainTransfer(
bytes32 tokenId,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
uint256 gasValue
) external payable;

/**
* @notice Allows tokens to be transferred from the sender to the contract.
* @param tokenId The identifier of the interchain token.
* @param amount The amount of tokens to transfer.
*/
function tokenTransferFrom(bytes32 tokenId, uint256 amount) external payable;

/**
* @notice Approves a specified amount of tokens to the token manager.
* @param tokenId The identifier of the interchain token.
* @param amount The amount of tokens to approve.
*/
function tokenApprove(bytes32 tokenId, uint256 amount) external payable;
}
85 changes: 6 additions & 79 deletions test/InterchainTokenFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const { ethers } = require('hardhat');
const {
getContractAt,
Wallet,
constants: { AddressZero, HashZero },
constants: { AddressZero },
utils: { defaultAbiCoder, keccak256, toUtf8Bytes },
} = ethers;

Expand Down Expand Up @@ -133,80 +133,13 @@ describe('InterchainTokenFactory', () => {
});

it('Should transfer some tokens to the factory', async () => {
const amount = 123456;

await deployToken();

const params = defaultAbiCoder.encode(['bytes', 'address'], ['0x', token.address]);

await expect(tokenFactory.registerCanonicalInterchainToken(token.address))
.to.emit(service, 'TokenManagerDeployed')
.withArgs(tokenId, tokenManagerAddress, LOCK_UNLOCK, params);

await token.approve(tokenFactory.address, amount).then((tx) => tx.wait());

await expect(tokenFactory.tokenTransferFrom(tokenId, amount))
.to.emit(token, 'Transfer')
.withArgs(wallet.address, tokenFactory.address, amount);
});

it('Should approve some tokens from the factory to the token manager', async () => {
const amount = 123456;

await deployToken();

const params = defaultAbiCoder.encode(['bytes', 'address'], ['0x', token.address]);

await expect(tokenFactory.registerCanonicalInterchainToken(token.address))
.to.emit(service, 'TokenManagerDeployed')
.withArgs(tokenId, tokenManagerAddress, LOCK_UNLOCK, params);

tokenManagerAddress = await service.validTokenManagerAddress(tokenId);

await expect(tokenFactory.tokenApprove(tokenId, amount))
.to.emit(token, 'Approval')
.withArgs(tokenFactory.address, service.address, amount);
});

it('Should transfer some tokens through the factory as the deployer', async () => {
const amount = 123456;
const destinationAddress = '0x57689403';
const gasValue = 45960;

await deployToken();

const params = defaultAbiCoder.encode(['bytes', 'address'], ['0x', token.address]);

await expect(tokenFactory.registerCanonicalInterchainToken(token.address))
.to.emit(service, 'TokenManagerDeployed')
.withArgs(tokenId, tokenManagerAddress, LOCK_UNLOCK, params);

await token.approve(tokenFactory.address, amount).then((tx) => tx.wait());

tokenManagerAddress = await service.validTokenManagerAddress(tokenId);

const txs = [];

txs.push(await tokenFactory.populateTransaction.tokenTransferFrom(tokenId, amount));
txs.push(await tokenFactory.populateTransaction.tokenApprove(tokenId, amount));
txs.push(
await tokenFactory.populateTransaction.interchainTransfer(tokenId, destinationChain, destinationAddress, amount, gasValue),
);

await expect(
tokenFactory.multicall(
txs.map((tx) => tx.data),
{ value: gasValue },
),
)
.to.emit(token, 'Transfer')
.withArgs(wallet.address, tokenFactory.address, amount)
.and.to.emit(token, 'Approval')
.withArgs(tokenFactory.address, service.address, amount)
.and.to.emit(token, 'Transfer')
.withArgs(tokenFactory.address, tokenManagerAddress, amount)
.and.to.emit(service, 'InterchainTransfer')
.withArgs(tokenId, tokenFactory.address, destinationChain, destinationAddress, amount, HashZero);
});

it('Should revert when trying to register a canonical lock/unlock gateway token', async () => {
Expand Down Expand Up @@ -332,7 +265,7 @@ describe('InterchainTokenFactory', () => {
.and.to.emit(service, 'TokenManagerDeployed')
.withArgs(tokenId, tokenManager.address, MINT_BURN, params)
.and.to.emit(token, 'Transfer')
.withArgs(AddressZero, tokenFactory.address, mintAmount)
.withArgs(AddressZero, minter, mintAmount)
.and.to.emit(tokenManager, 'RolesAdded')
.withArgs(minter, 1 << FLOW_LIMITER_ROLE)
.and.to.emit(tokenManager, 'RolesAdded')
Expand All @@ -346,10 +279,6 @@ describe('InterchainTokenFactory', () => {
.and.to.emit(tokenManager, 'RolesRemoved')
.withArgs(tokenFactory.address, 1 << FLOW_LIMITER_ROLE);

await expect(tokenFactory.interchainTransfer(tokenId, '', minter, mintAmount, 0))
.to.emit(token, 'Transfer')
.withArgs(tokenFactory.address, minter, mintAmount);

expect(await token.balanceOf(tokenFactory.address)).to.equal(0);
expect(await token.balanceOf(minter)).to.equal(mintAmount);

Expand All @@ -363,7 +292,7 @@ describe('InterchainTokenFactory', () => {
const salt = keccak256('0x12');
tokenId = await tokenFactory.interchainTokenId(wallet.address, salt);
const tokenAddress = await tokenFactory.interchainTokenAddress(wallet.address, salt);
let params = defaultAbiCoder.encode(['bytes', 'address'], [tokenFactory.address, tokenAddress]);
const params = defaultAbiCoder.encode(['bytes', 'address'], [tokenFactory.address, tokenAddress]);
const tokenManager = await getContractAt('TokenManager', await service.tokenManagerAddress(tokenId), wallet);
const token = await getContractAt('InterchainToken', tokenAddress, wallet);

Expand All @@ -373,7 +302,7 @@ describe('InterchainTokenFactory', () => {
.and.to.emit(service, 'TokenManagerDeployed')
.withArgs(tokenId, tokenManager.address, MINT_BURN, params)
.and.to.emit(token, 'Transfer')
.withArgs(AddressZero, tokenFactory.address, mintAmount)
.withArgs(AddressZero, wallet.address, mintAmount)
.and.to.emit(token, 'RolesAdded')
.withArgs(wallet.address, 1 << MINTER_ROLE)
.and.to.emit(tokenManager, 'RolesAdded')
Expand All @@ -387,7 +316,6 @@ describe('InterchainTokenFactory', () => {
.and.to.emit(tokenManager, 'RolesRemoved')
.withArgs(tokenFactory.address, 1 << FLOW_LIMITER_ROLE);

params = defaultAbiCoder.encode(['bytes', 'address'], ['0x', token.address]);
const payload = defaultAbiCoder.encode(
['uint256', 'bytes32', 'string', 'string', 'uint8', 'bytes'],
[MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, tokenId, name, symbol, decimals, wallet.address.toLowerCase()],
Expand Down Expand Up @@ -435,7 +363,7 @@ describe('InterchainTokenFactory', () => {
const salt = keccak256('0x1245');
tokenId = await tokenFactory.interchainTokenId(wallet.address, salt);
const tokenAddress = await tokenFactory.interchainTokenAddress(wallet.address, salt);
let params = defaultAbiCoder.encode(['bytes', 'address'], [tokenFactory.address, tokenAddress]);
const params = defaultAbiCoder.encode(['bytes', 'address'], [tokenFactory.address, tokenAddress]);
const tokenManager = await getContractAt('TokenManager', await service.tokenManagerAddress(tokenId), wallet);
const token = await getContractAt('InterchainToken', tokenAddress, wallet);

Expand All @@ -445,7 +373,7 @@ describe('InterchainTokenFactory', () => {
.and.to.emit(service, 'TokenManagerDeployed')
.withArgs(tokenId, tokenManager.address, MINT_BURN, params)
.and.to.emit(token, 'Transfer')
.withArgs(AddressZero, tokenFactory.address, mintAmount)
.withArgs(AddressZero, wallet.address, mintAmount)
.and.to.emit(token, 'RolesAdded')
.withArgs(wallet.address, 1 << MINTER_ROLE)
.and.to.emit(tokenManager, 'RolesAdded')
Expand All @@ -459,7 +387,6 @@ describe('InterchainTokenFactory', () => {
.and.to.emit(tokenManager, 'RolesRemoved')
.withArgs(tokenFactory.address, 1 << FLOW_LIMITER_ROLE);

params = defaultAbiCoder.encode(['bytes', 'address'], ['0x', token.address]);
const payload = defaultAbiCoder.encode(
['uint256', 'bytes32', 'string', 'string', 'uint8', 'bytes'],
[MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, tokenId, name, symbol, decimals, '0x'],
Expand Down
Loading

0 comments on commit c7b6a01

Please sign in to comment.