From e60d3bb8722561f8536c04c7ec79b5c6520987ff Mon Sep 17 00:00:00 2001 From: Foivos Date: Wed, 8 Nov 2023 16:58:46 +0200 Subject: [PATCH 1/5] added some info in the tokenManagerProxy, which will cause some gas saving and will allow for a more powerfull design in the future. --- contracts/interfaces/ITokenManager.sol | 5 +++ contracts/interfaces/ITokenManagerProxy.sol | 10 ++++++ contracts/proxies/TokenManagerProxy.sol | 9 +++++ contracts/test/HardCodedConstantsTest.sol | 1 - contracts/token-manager/TokenManager.sol | 33 ++++++++----------- .../TokenManagerLiquidityPool.sol | 7 ++-- .../token-manager/TokenManagerLockUnlock.sol | 14 ++------ .../TokenManagerLockUnlockFee.sol | 14 ++------ .../token-manager/TokenManagerMintBurn.sol | 14 ++------ .../TokenManagerMintBurnFrom.sol | 2 +- test/TokenManager.js | 22 +++++++------ 11 files changed, 59 insertions(+), 72 deletions(-) diff --git a/contracts/interfaces/ITokenManager.sol b/contracts/interfaces/ITokenManager.sol index 4bab9af0..347f7d6b 100644 --- a/contracts/interfaces/ITokenManager.sol +++ b/contracts/interfaces/ITokenManager.sol @@ -39,6 +39,11 @@ interface ITokenManager is ITokenManagerType, IOperatable, IFlowLimit, IImplemen */ function implementationType() external pure returns (uint256); + /** + * @notice A function that should return the token address from the init params. + */ + function getTokenAddressFromParams(bytes calldata params) external pure returns (address); + /** * @notice Calls the service to initiate a cross-chain transfer after taking the appropriate amount of tokens from the user. * @param destinationChain the name of the chain to send tokens to. diff --git a/contracts/interfaces/ITokenManagerProxy.sol b/contracts/interfaces/ITokenManagerProxy.sol index 2ec35fc0..806c48aa 100644 --- a/contracts/interfaces/ITokenManagerProxy.sol +++ b/contracts/interfaces/ITokenManagerProxy.sol @@ -20,4 +20,14 @@ interface ITokenManagerProxy is IProxy { * @notice Returns token ID of the token manager. */ function interchainTokenId() external view returns (bytes32); + + /** + * @notice Returns token address that this token manager manages. + */ + function tokenAddress() external view returns (address); + + /** + * @notice Returns implementation type and token address in one call. + */ + function getInfo() external view returns (uint256, address); } diff --git a/contracts/proxies/TokenManagerProxy.sol b/contracts/proxies/TokenManagerProxy.sol index c896664d..400cf4e5 100644 --- a/contracts/proxies/TokenManagerProxy.sol +++ b/contracts/proxies/TokenManagerProxy.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.0; import { IProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IProxy.sol'; import { BaseProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/BaseProxy.sol'; +import { ITokenManager } from '../interfaces/ITokenManager.sol'; import { ITokenManagerProxy } from '../interfaces/ITokenManagerProxy.sol'; import { ITokenManagerImplementation } from '../interfaces/ITokenManagerImplementation.sol'; @@ -19,6 +20,7 @@ contract TokenManagerProxy is BaseProxy, ITokenManagerProxy { address public immutable interchainTokenService; uint256 public immutable implementationType; bytes32 public immutable interchainTokenId; + address public immutable tokenAddress; /** * @dev Constructs the TokenManagerProxy contract. @@ -39,6 +41,8 @@ contract TokenManagerProxy is BaseProxy, ITokenManagerProxy { (bool success, ) = implementation_.delegatecall(abi.encodeWithSelector(IProxy.setup.selector, params)); if (!success) revert SetupFailed(); + + tokenAddress = ITokenManager(implementation_).getTokenAddressFromParams(params); } /** @@ -49,6 +53,11 @@ contract TokenManagerProxy is BaseProxy, ITokenManagerProxy { return CONTRACT_ID; } + function getInfo() external view returns (uint256 implementationType_, address tokenAddress_) { + implementationType_ = implementationType; + tokenAddress_ = tokenAddress; + } + /** * @dev Returns the address of the current implementation. * @return implementation_ The address of the current implementation diff --git a/contracts/test/HardCodedConstantsTest.sol b/contracts/test/HardCodedConstantsTest.sol index a5037749..9c4bad32 100644 --- a/contracts/test/HardCodedConstantsTest.sol +++ b/contracts/test/HardCodedConstantsTest.sol @@ -14,7 +14,6 @@ contract TestTokenManager is TokenManagerLiquidityPool { string public constant NAME = 'TestTokenManager'; constructor(address interchainTokenService_) TokenManagerLiquidityPool(interchainTokenService_) { - if (TOKEN_ADDRESS_SLOT != uint256(keccak256('token-address')) - 1) revert Invalid(); if (LIQUIDITY_POOL_SLOT != uint256(keccak256('liquidity-pool-slot')) - 1) revert Invalid(); } } diff --git a/contracts/token-manager/TokenManager.sol b/contracts/token-manager/TokenManager.sol index 4d94666a..fb10a153 100644 --- a/contracts/token-manager/TokenManager.sol +++ b/contracts/token-manager/TokenManager.sol @@ -23,9 +23,6 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen bytes32 private constant CONTRACT_ID = keccak256('token-manager'); - // uint256(keccak256('token-address')) - 1 - uint256 internal constant TOKEN_ADDRESS_SLOT = 0xc4e632779a6a7838736dd7e5e6a0eadf171dd37dfb6230720e265576dfcf42ba; - /** * @notice Constructs the TokenManager contract. * @param interchainTokenService_ The address of the interchain token service @@ -48,7 +45,7 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen * @dev A modifier that allows only the token to execute the function. */ modifier onlyToken() { - if (msg.sender != tokenAddress()) revert NotToken(msg.sender); + if (msg.sender != this.tokenAddress()) revert NotToken(msg.sender); _; } @@ -60,13 +57,12 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen } /** - * @dev Reads the stored token address from the predetermined storage slot + * @dev Reads the token address from the proxy * @return tokenAddress_ The address of the token */ - function tokenAddress() public view virtual returns (address tokenAddress_) { - assembly { - tokenAddress_ := sload(TOKEN_ADDRESS_SLOT) - } + function tokenAddress() external view virtual returns (address tokenAddress_) { + // Ask the proxy what the token address is. + tokenAddress_ = this.tokenAddress(); } /** @@ -78,6 +74,13 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen return this.interchainTokenId(); } + /** + * @notice A function that should return the token address from the setup params. + */ + function getTokenAddressFromParams(bytes calldata params) external pure returns (address tokenAddress_) { + (, tokenAddress_) = abi.decode(params, (uint256, address)); + } + /** * @dev This function should only be called by the proxy, and only once from the proxy constructor * @param params the parameters to be used to initialize the TokenManager. The exact format depends @@ -251,16 +254,6 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen _setFlowLimit(flowLimit_, interchainTokenId()); } - /** - * @dev Stores the token address in the predetermined storage slot - * @param tokenAddress_ The address of the token to store - */ - function _setTokenAddress(address tokenAddress_) internal { - assembly { - sstore(TOKEN_ADDRESS_SLOT, tokenAddress_) - } - } - /** * @notice Transfers tokens from a specific address to this contract. * Must be overridden in the inheriting contract. @@ -284,5 +277,5 @@ abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implemen * Must be overridden in the inheriting contract. * @param params The setup parameters */ - function _setup(bytes calldata params) internal virtual; + function _setup(bytes calldata params) internal virtual {} } diff --git a/contracts/token-manager/TokenManagerLiquidityPool.sol b/contracts/token-manager/TokenManagerLiquidityPool.sol index 3b652734..a82b56c6 100644 --- a/contracts/token-manager/TokenManagerLiquidityPool.sol +++ b/contracts/token-manager/TokenManagerLiquidityPool.sol @@ -41,8 +41,7 @@ contract TokenManagerLiquidityPool is TokenManager, ReentrancyGuard, ITokenManag */ function _setup(bytes calldata params_) internal override { // The first argument is reserved for the operator. - (, address tokenAddress_, address liquidityPool_) = abi.decode(params_, (bytes, address, address)); - _setTokenAddress(tokenAddress_); + (, , address liquidityPool_) = abi.decode(params_, (uint256, address, address)); _setLiquidityPool(liquidityPool_); } @@ -81,7 +80,7 @@ contract TokenManagerLiquidityPool is TokenManager, ReentrancyGuard, ITokenManag * @return uint The actual amount of tokens transferred. This allows support for fee-on-transfer tokens. */ function _takeToken(address from, uint256 amount) internal override noReEntrancy returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); address liquidityPool_ = liquidityPool(); uint256 balance = token.balanceOf(liquidityPool_); @@ -101,7 +100,7 @@ contract TokenManagerLiquidityPool is TokenManager, ReentrancyGuard, ITokenManag * @return uint The actual amount of tokens transferred */ function _giveToken(address to, uint256 amount) internal override noReEntrancy returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); uint256 balance = token.balanceOf(to); // slither-disable-next-line arbitrary-send-erc20 diff --git a/contracts/token-manager/TokenManagerLockUnlock.sol b/contracts/token-manager/TokenManagerLockUnlock.sol index b3ac99a3..cd3c5f76 100644 --- a/contracts/token-manager/TokenManagerLockUnlock.sol +++ b/contracts/token-manager/TokenManagerLockUnlock.sol @@ -29,16 +29,6 @@ contract TokenManagerLockUnlock is TokenManager, ITokenManagerLockUnlock { return uint256(TokenManagerType.LOCK_UNLOCK); } - /** - * @dev Sets up the token address. - * @param params_ The setup parameters in bytes. Should be encoded with the token address. - */ - function _setup(bytes calldata params_) internal override { - // The first argument is reserved for the operator. - (, address tokenAddress_) = abi.decode(params_, (bytes, address)); - _setTokenAddress(tokenAddress_); - } - /** * @dev Transfers a specified amount of tokens from a specified address to this contract. * @param from The address to transfer tokens from @@ -46,7 +36,7 @@ contract TokenManagerLockUnlock is TokenManager, ITokenManagerLockUnlock { * @return uint The actual amount of tokens transferred. This allows support for fee-on-transfer tokens. */ function _takeToken(address from, uint256 amount) internal override returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); token.safeTransferFrom(from, address(this), amount); @@ -60,7 +50,7 @@ contract TokenManagerLockUnlock is TokenManager, ITokenManagerLockUnlock { * @return uint The actual amount of tokens transferred */ function _giveToken(address to, uint256 amount) internal override returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); token.safeTransfer(to, amount); diff --git a/contracts/token-manager/TokenManagerLockUnlockFee.sol b/contracts/token-manager/TokenManagerLockUnlockFee.sol index 6ac1060f..63e2b881 100644 --- a/contracts/token-manager/TokenManagerLockUnlockFee.sol +++ b/contracts/token-manager/TokenManagerLockUnlockFee.sol @@ -30,16 +30,6 @@ contract TokenManagerLockUnlockFee is TokenManager, ReentrancyGuard, ITokenManag return uint256(TokenManagerType.LOCK_UNLOCK_FEE); } - /** - * @dev Sets up the token address. - * @param params_ The setup parameters in bytes. Should be encoded with the token address. - */ - function _setup(bytes calldata params_) internal override { - // The first argument is reserved for the operator. - (, address tokenAddress_) = abi.decode(params_, (bytes, address)); - _setTokenAddress(tokenAddress_); - } - /** * @dev Transfers a specified amount of tokens from a specified address to this contract. * @param from The address to transfer tokens from @@ -47,7 +37,7 @@ contract TokenManagerLockUnlockFee is TokenManager, ReentrancyGuard, ITokenManag * @return uint The actual amount of tokens transferred. This allows support for fee-on-transfer tokens. */ function _takeToken(address from, uint256 amount) internal override noReEntrancy returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); uint256 balanceBefore = token.balanceOf(address(this)); token.safeTransferFrom(from, address(this), amount); @@ -67,7 +57,7 @@ contract TokenManagerLockUnlockFee is TokenManager, ReentrancyGuard, ITokenManag * @return uint The actual amount of tokens transferred */ function _giveToken(address to, uint256 amount) internal override noReEntrancy returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); uint256 balanceBefore = token.balanceOf(to); token.safeTransfer(to, amount); diff --git a/contracts/token-manager/TokenManagerMintBurn.sol b/contracts/token-manager/TokenManagerMintBurn.sol index 5cf2a42c..203f9513 100644 --- a/contracts/token-manager/TokenManagerMintBurn.sol +++ b/contracts/token-manager/TokenManagerMintBurn.sol @@ -29,16 +29,6 @@ contract TokenManagerMintBurn is TokenManager, ITokenManagerMintBurn { return uint256(TokenManagerType.MINT_BURN); } - /** - * @dev Sets up the token address. - * @param params_ The setup parameters in bytes. Should be encoded with the token address. - */ - function _setup(bytes calldata params_) internal override { - // The first argument is reserved for the operator. - (, address tokenAddress_) = abi.decode(params_, (bytes, address)); - _setTokenAddress(tokenAddress_); - } - /** * @dev Burns the specified amount of tokens from a particular address. * @param from Address to burn tokens from @@ -46,7 +36,7 @@ contract TokenManagerMintBurn is TokenManager, ITokenManagerMintBurn { * @return uint Amount of tokens burned */ function _takeToken(address from, uint256 amount) internal virtual override returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); token.safeCall(abi.encodeWithSelector(IERC20MintableBurnable.burn.selector, from, amount)); @@ -60,7 +50,7 @@ contract TokenManagerMintBurn is TokenManager, ITokenManagerMintBurn { * @return uint Amount of tokens minted */ function _giveToken(address to, uint256 amount) internal override returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); token.safeCall(abi.encodeWithSelector(IERC20MintableBurnable.mint.selector, to, amount)); diff --git a/contracts/token-manager/TokenManagerMintBurnFrom.sol b/contracts/token-manager/TokenManagerMintBurnFrom.sol index bd89a013..ddabaeb1 100644 --- a/contracts/token-manager/TokenManagerMintBurnFrom.sol +++ b/contracts/token-manager/TokenManagerMintBurnFrom.sol @@ -35,7 +35,7 @@ contract TokenManagerMintBurnFrom is TokenManagerMintBurn { * @return uint Amount of tokens burned */ function _takeToken(address from, uint256 amount) internal override returns (uint256) { - IERC20 token = IERC20(tokenAddress()); + IERC20 token = IERC20(this.tokenAddress()); token.safeCall(abi.encodeWithSelector(IERC20BurnableFrom.burnFrom.selector, from, amount)); diff --git a/test/TokenManager.js b/test/TokenManager.js index 972855a6..14b3ce39 100644 --- a/test/TokenManager.js +++ b/test/TokenManager.js @@ -5,10 +5,11 @@ const { ethers } = require('hardhat'); const { utils: { toUtf8Bytes, defaultAbiCoder }, constants: { AddressZero }, + getContractAt, } = ethers; const { expect } = chai; -const { expectRevert } = require('./utils'); -const { deployContract } = require('../scripts/deploy'); +const { expectRevert, getRandomBytes32 } = require('./utils'); +const { deployContract, deployAll } = require('../scripts/deploy'); describe('Token Manager', () => { const FLOW_LIMITER_ROLE = 2; @@ -42,6 +43,14 @@ describe('Token Manager', () => { }); it('Should revert on transmitInterchainTransfer if not called by the token', async () => { + const [service] = await deployAll(owner, 'Test'); + const salt = getRandomBytes32(); + const LOCK_UNLCOK = 2; + const params = defaultAbiCoder.encode(['bytes', 'address'], [owner.address, token.address]); + await await service.deployTokenManager(salt, '', LOCK_UNLCOK, params, 0); + const tokenManagerAddress = await service.tokenManagerAddress(await service.interchainTokenId(owner.address, salt)); + const tokenManager = await getContractAt('ITokenManager', tokenManagerAddress, owner); + const sender = owner.address; const destinationChain = 'Dest Chain'; const destinationAddress = toUtf8Bytes(user.address); @@ -50,14 +59,7 @@ describe('Token Manager', () => { await expectRevert( (gasOptions) => - tokenManagerLockUnlock.transmitInterchainTransfer( - sender, - destinationChain, - destinationAddress, - amount, - metadata, - gasOptions, - ), + tokenManager.transmitInterchainTransfer(sender, destinationChain, destinationAddress, amount, metadata, gasOptions), tokenManagerLockUnlock, 'NotToken', [sender], From 05c75365c12a1a0d47cb80d2612ca134dc700acf Mon Sep 17 00:00:00 2001 From: Foivos Date: Wed, 8 Nov 2023 18:41:13 +0200 Subject: [PATCH 2/5] add service approval on appropriate token managers --- .../token-manager/TokenManagerLiquidityPool.sol | 7 +++++-- contracts/token-manager/TokenManagerLockUnlock.sol | 14 +++++++++++++- .../token-manager/TokenManagerLockUnlockFee.sol | 14 +++++++++++++- test/TokenManager.js | 5 +++-- test/TokenService.js | 13 ++++--------- 5 files changed, 38 insertions(+), 15 deletions(-) diff --git a/contracts/token-manager/TokenManagerLiquidityPool.sol b/contracts/token-manager/TokenManagerLiquidityPool.sol index a82b56c6..a9b45eec 100644 --- a/contracts/token-manager/TokenManagerLiquidityPool.sol +++ b/contracts/token-manager/TokenManagerLiquidityPool.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol'; -import { SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol'; +import { SafeTokenTransferFrom, SafeTokenCall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol'; import { ReentrancyGuard } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/ReentrancyGuard.sol'; import { ITokenManagerLiquidityPool } from '../interfaces/ITokenManagerLiquidityPool.sol'; @@ -18,6 +18,7 @@ import { TokenManager } from './TokenManager.sol'; */ contract TokenManagerLiquidityPool is TokenManager, ReentrancyGuard, ITokenManagerLiquidityPool { using SafeTokenTransferFrom for IERC20; + using SafeTokenCall for IERC20; error NotSupported(); @@ -41,8 +42,10 @@ contract TokenManagerLiquidityPool is TokenManager, ReentrancyGuard, ITokenManag */ function _setup(bytes calldata params_) internal override { // The first argument is reserved for the operator. - (, , address liquidityPool_) = abi.decode(params_, (uint256, address, address)); + (, address tokenAddress_, address liquidityPool_) = abi.decode(params_, (uint256, address, address)); _setLiquidityPool(liquidityPool_); + + IERC20(tokenAddress_).safeCall(abi.encodeWithSelector(IERC20.approve.selector, interchainTokenService, type(uint256).max)); } /** diff --git a/contracts/token-manager/TokenManagerLockUnlock.sol b/contracts/token-manager/TokenManagerLockUnlock.sol index cd3c5f76..8a64e32e 100644 --- a/contracts/token-manager/TokenManagerLockUnlock.sol +++ b/contracts/token-manager/TokenManagerLockUnlock.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol'; -import { SafeTokenTransfer, SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol'; +import { SafeTokenTransfer, SafeTokenTransferFrom, SafeTokenCall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol'; import { ITokenManagerLockUnlock } from '..//interfaces/ITokenManagerLockUnlock.sol'; import { TokenManager } from './TokenManager.sol'; @@ -17,6 +17,7 @@ import { TokenManager } from './TokenManager.sol'; contract TokenManagerLockUnlock is TokenManager, ITokenManagerLockUnlock { using SafeTokenTransfer for IERC20; using SafeTokenTransferFrom for IERC20; + using SafeTokenCall for IERC20; /** * @dev Constructs an instance of TokenManagerLockUnlock. Calls the constructor @@ -29,6 +30,17 @@ contract TokenManagerLockUnlock is TokenManager, ITokenManagerLockUnlock { return uint256(TokenManagerType.LOCK_UNLOCK); } + /** + * @dev Sets up the token address and liquidity pool address. + * @param params_ The setup parameters in bytes. Should be encoded with the token address and the liquidity pool address. + */ + function _setup(bytes calldata params_) internal override { + // The first argument is reserved for the operator. + (, address tokenAddress_) = abi.decode(params_, (uint256, address)); + + IERC20(tokenAddress_).safeCall(abi.encodeWithSelector(IERC20.approve.selector, interchainTokenService, type(uint256).max)); + } + /** * @dev Transfers a specified amount of tokens from a specified address to this contract. * @param from The address to transfer tokens from diff --git a/contracts/token-manager/TokenManagerLockUnlockFee.sol b/contracts/token-manager/TokenManagerLockUnlockFee.sol index 63e2b881..f7150f6e 100644 --- a/contracts/token-manager/TokenManagerLockUnlockFee.sol +++ b/contracts/token-manager/TokenManagerLockUnlockFee.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol'; -import { SafeTokenTransferFrom, SafeTokenTransfer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol'; +import { SafeTokenTransferFrom, SafeTokenTransfer, SafeTokenCall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol'; import { ReentrancyGuard } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/ReentrancyGuard.sol'; import { ITokenManagerLockUnlock } from '../interfaces/ITokenManagerLockUnlock.sol'; @@ -18,6 +18,7 @@ import { TokenManager } from './TokenManager.sol'; contract TokenManagerLockUnlockFee is TokenManager, ReentrancyGuard, ITokenManagerLockUnlock { using SafeTokenTransfer for IERC20; using SafeTokenTransferFrom for IERC20; + using SafeTokenCall for IERC20; /** * @dev Constructs an instance of TokenManagerLockUnlock. Calls the constructor @@ -30,6 +31,17 @@ contract TokenManagerLockUnlockFee is TokenManager, ReentrancyGuard, ITokenManag return uint256(TokenManagerType.LOCK_UNLOCK_FEE); } + /** + * @dev Sets up the token address and liquidity pool address. + * @param params_ The setup parameters in bytes. Should be encoded with the token address and the liquidity pool address. + */ + function _setup(bytes calldata params_) internal override { + // The first argument is reserved for the operator. + (, address tokenAddress_) = abi.decode(params_, (uint256, address)); + + IERC20(tokenAddress_).safeCall(abi.encodeWithSelector(IERC20.approve.selector, interchainTokenService, type(uint256).max)); + } + /** * @dev Transfers a specified amount of tokens from a specified address to this contract. * @param from The address to transfer tokens from diff --git a/test/TokenManager.js b/test/TokenManager.js index 14b3ce39..2aa7f7d3 100644 --- a/test/TokenManager.js +++ b/test/TokenManager.js @@ -45,9 +45,10 @@ describe('Token Manager', () => { it('Should revert on transmitInterchainTransfer if not called by the token', async () => { const [service] = await deployAll(owner, 'Test'); const salt = getRandomBytes32(); - const LOCK_UNLCOK = 2; + const MINT_BURN = 0; + const params = defaultAbiCoder.encode(['bytes', 'address'], [owner.address, token.address]); - await await service.deployTokenManager(salt, '', LOCK_UNLCOK, params, 0); + await await service.deployTokenManager(salt, '', MINT_BURN, params, 0); const tokenManagerAddress = await service.tokenManagerAddress(await service.interchainTokenId(owner.address, salt)); const tokenManager = await getContractAt('ITokenManager', tokenManagerAddress, owner); diff --git a/test/TokenService.js b/test/TokenService.js index ee8eb0b0..c47567e1 100644 --- a/test/TokenService.js +++ b/test/TokenService.js @@ -374,12 +374,7 @@ describe('Interchain Token Service', () => { const salt = getRandomBytes32(); const tokenId = await service.interchainTokenId(wallet.address, salt); const validParams = defaultAbiCoder.encode(['bytes', 'address'], ['0x', wallet.address]); - const tokenManagerProxy = await deployContract(wallet, `TokenManagerProxy`, [ - service.address, - LOCK_UNLOCK, - tokenId, - validParams, - ]); + const tokenManagerProxy = await deployContract(wallet, `TokenManagerProxy`, [service.address, MINT_BURN, tokenId, validParams]); const invalidParams = '0x1234'; await expectRevert( @@ -486,7 +481,7 @@ describe('Interchain Token Service', () => { await service.deployTokenManager( salt, '', - LOCK_UNLOCK, + MINT_BURN, defaultAbiCoder.encode(['bytes', 'address'], ['0x', wallet.address]), 0, ) @@ -811,7 +806,7 @@ describe('Interchain Token Service', () => { await service.deployTokenManager( salt, '', - LOCK_UNLOCK, + MINT_BURN, defaultAbiCoder.encode(['bytes', 'address'], ['0x', wallet.address]), 0, ) @@ -885,7 +880,7 @@ describe('Interchain Token Service', () => { await service.deployTokenManager( salt, '', - LOCK_UNLOCK, + MINT_BURN, defaultAbiCoder.encode(['bytes', 'address'], ['0x', wallet.address]), 0, ) From f6610c2a74cf207ec708477bfb673fa0fb286611 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Fri, 10 Nov 2023 12:56:28 -0500 Subject: [PATCH 3/5] address comment --- contracts/interfaces/ITokenManagerProxy.sol | 4 ++-- contracts/proxies/TokenManagerProxy.sol | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/interfaces/ITokenManagerProxy.sol b/contracts/interfaces/ITokenManagerProxy.sol index 2e71ebd6..4737ff9c 100644 --- a/contracts/interfaces/ITokenManagerProxy.sol +++ b/contracts/interfaces/ITokenManagerProxy.sol @@ -29,7 +29,7 @@ interface ITokenManagerProxy is IProxy { function tokenAddress() external view returns (address); /** - * @notice Returns implementation type and token address in one call. + * @notice Returns implementation type and token address. */ - function getInfo() external view returns (uint256, address); + function getImplementationTypeAndTokenAddress() external view returns (uint256, address); } diff --git a/contracts/proxies/TokenManagerProxy.sol b/contracts/proxies/TokenManagerProxy.sol index d891bc26..5795e83f 100644 --- a/contracts/proxies/TokenManagerProxy.sol +++ b/contracts/proxies/TokenManagerProxy.sol @@ -53,7 +53,10 @@ contract TokenManagerProxy is BaseProxy, ITokenManagerProxy { return CONTRACT_ID; } - function getInfo() external view returns (uint256 implementationType_, address tokenAddress_) { + /** + * @notice Returns implementation type and token address. + */ + function getImplementationTypeAndTokenAddress() external view returns (uint256 implementationType_, address tokenAddress_) { implementationType_ = implementationType; tokenAddress_ = tokenAddress; } From 6349526a544d593e4b2197b12b3d5152581ecb50 Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Fri, 10 Nov 2023 13:06:25 -0500 Subject: [PATCH 4/5] refactor imports --- contracts/interfaces/IBaseTokenManager.sol | 31 ++++++++++++++++++++++ contracts/interfaces/ITokenManager.sol | 26 ++---------------- contracts/token-manager/TokenManager.sol | 3 ++- 3 files changed, 35 insertions(+), 25 deletions(-) create mode 100644 contracts/interfaces/IBaseTokenManager.sol diff --git a/contracts/interfaces/IBaseTokenManager.sol b/contracts/interfaces/IBaseTokenManager.sol new file mode 100644 index 00000000..5db77340 --- /dev/null +++ b/contracts/interfaces/IBaseTokenManager.sol @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.8.0; + +/** + * @title IBaseTokenManager + * @notice This contract is defines the base token manager interface implemented by all token managers. + */ +interface IBaseTokenManager { + /** + * @notice A function that returns the token id. + */ + function interchainTokenId() external view returns (bytes32); + + /** + * @notice A function that should return the address of the token. + * Must be overridden in the inheriting contract. + * @return address address of the token. + */ + function tokenAddress() external view returns (address); + + /** + * @notice A function that should return the implementation type of the token manager. + */ + function implementationType() external pure returns (uint256); + + /** + * @notice A function that should return the token address from the init params. + */ + function getTokenAddressFromParams(bytes calldata params) external pure returns (address); +} diff --git a/contracts/interfaces/ITokenManager.sol b/contracts/interfaces/ITokenManager.sol index 347f7d6b..07c600a6 100644 --- a/contracts/interfaces/ITokenManager.sol +++ b/contracts/interfaces/ITokenManager.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.0; import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol'; -import { ITokenManagerType } from './ITokenManagerType.sol'; +import { IBaseTokenManager } from './IBaseTokenManager.sol'; import { IOperatable } from './IOperatable.sol'; import { IFlowLimit } from './IFlowLimit.sol'; @@ -12,7 +12,7 @@ import { IFlowLimit } from './IFlowLimit.sol'; * @title ITokenManager * @notice This contract is responsible for handling tokens before initiating a cross chain token transfer, or after receiving one. */ -interface ITokenManager is ITokenManagerType, IOperatable, IFlowLimit, IImplementation { +interface ITokenManager is IBaseTokenManager, IOperatable, IFlowLimit, IImplementation { error TokenLinkerZeroAddress(); error NotService(address caller); error TakeTokenFailed(); @@ -22,28 +22,6 @@ interface ITokenManager is ITokenManagerType, IOperatable, IFlowLimit, IImplemen error AlreadyFlowLimiter(address flowLimiter); error NotFlowLimiter(address flowLimiter); - /** - * @notice A function that returns the token id. - */ - function interchainTokenId() external view returns (bytes32); - - /** - * @notice A function that should return the address of the token. - * Must be overridden in the inheriting contract. - * @return address address of the token. - */ - function tokenAddress() external view returns (address); - - /** - * @notice A function that should return the implementation type of the token manager. - */ - function implementationType() external pure returns (uint256); - - /** - * @notice A function that should return the token address from the init params. - */ - function getTokenAddressFromParams(bytes calldata params) external pure returns (address); - /** * @notice Calls the service to initiate a cross-chain transfer after taking the appropriate amount of tokens from the user. * @param destinationChain the name of the chain to send tokens to. diff --git a/contracts/token-manager/TokenManager.sol b/contracts/token-manager/TokenManager.sol index fb10a153..442eb633 100644 --- a/contracts/token-manager/TokenManager.sol +++ b/contracts/token-manager/TokenManager.sol @@ -7,6 +7,7 @@ import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contrac import { Implementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Implementation.sol'; import { ITokenManager } from '../interfaces/ITokenManager.sol'; +import { ITokenManagerType } from '../interfaces/ITokenManagerType.sol'; import { IInterchainTokenService } from '../interfaces/IInterchainTokenService.sol'; import { Operatable } from '../utils/Operatable.sol'; @@ -16,7 +17,7 @@ import { FlowLimit } from '../utils/FlowLimit.sol'; * @title The main functionality of TokenManagers. * @notice This contract is responsible for handling tokens before initiating a cross chain token transfer, or after receiving one. */ -abstract contract TokenManager is ITokenManager, Operatable, FlowLimit, Implementation { +abstract contract TokenManager is ITokenManager, ITokenManagerType, Operatable, FlowLimit, Implementation { using AddressBytes for bytes; IInterchainTokenService public immutable interchainTokenService; From 21515403d8bf1652107f296c413eb0a71d7c386d Mon Sep 17 00:00:00 2001 From: Milap Sheth Date: Fri, 10 Nov 2023 13:07:05 -0500 Subject: [PATCH 5/5] update import --- contracts/proxies/TokenManagerProxy.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/proxies/TokenManagerProxy.sol b/contracts/proxies/TokenManagerProxy.sol index 5795e83f..9ba38898 100644 --- a/contracts/proxies/TokenManagerProxy.sol +++ b/contracts/proxies/TokenManagerProxy.sol @@ -5,7 +5,7 @@ pragma solidity ^0.8.0; import { IProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IProxy.sol'; import { BaseProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/BaseProxy.sol'; -import { ITokenManager } from '../interfaces/ITokenManager.sol'; +import { IBaseTokenManager } from '../interfaces/IBaseTokenManager.sol'; import { ITokenManagerProxy } from '../interfaces/ITokenManagerProxy.sol'; import { ITokenManagerImplementation } from '../interfaces/ITokenManagerImplementation.sol'; @@ -42,7 +42,7 @@ contract TokenManagerProxy is BaseProxy, ITokenManagerProxy { (bool success, ) = implementation_.delegatecall(abi.encodeWithSelector(IProxy.setup.selector, params)); if (!success) revert SetupFailed(); - tokenAddress = ITokenManager(implementation_).getTokenAddressFromParams(params); + tokenAddress = IBaseTokenManager(implementation_).getTokenAddressFromParams(params); } /**