Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(its): validate token manager address from token id #284

Closed
wants to merge 2 commits into from
Closed
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
19 changes: 16 additions & 3 deletions contracts/TokenHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
*/
// slither-disable-next-line locked-ether
function giveToken(bytes32 tokenId, address to, uint256 amount) external payable returns (uint256, address) {
address tokenManager = _create3Address(tokenId);
address tokenManager = _validTokenManagerAddress(tokenId);

(uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress();

Expand Down Expand Up @@ -94,7 +94,8 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
address from,
uint256 amount
) external payable returns (uint256, string memory symbol) {
address tokenManager = _create3Address(tokenId);
address tokenManager = _validTokenManagerAddress(tokenId);

(uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress();

if (tokenOnly && msg.sender != tokenAddress) revert NotToken(msg.sender, tokenAddress);
Expand Down Expand Up @@ -133,7 +134,8 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
*/
// slither-disable-next-line locked-ether
function transferTokenFrom(bytes32 tokenId, address from, address to, uint256 amount) external payable returns (uint256, address) {
address tokenManager = _create3Address(tokenId);
address tokenManager = _validTokenManagerAddress(tokenId);

(uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress();

if (
Expand Down Expand Up @@ -228,4 +230,15 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20.approve.selector, gateway, amount));
}
}

/**
* @notice Returns the address of a TokenManager from a specific tokenId.
* @dev The TokenManager needs to exist already.
* @param tokenId The tokenId.
* @return tokenManagerAddress_ The deployment address of the TokenManager.
*/
function _validTokenManagerAddress(bytes32 tokenId) internal view returns (address tokenManagerAddress_) {
tokenManagerAddress_ = _create3Address(tokenId);
if (tokenManagerAddress_.code.length == 0) revert TokenManagerDoesNotExist(tokenId);
}
}
1 change: 1 addition & 0 deletions contracts/interfaces/ITokenHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface ITokenHandler {
error UnsupportedTokenManagerType(uint256 tokenManagerType);
error AddressZero();
error NotToken(address caller, address token);
error TokenManagerDoesNotExist(bytes32 tokenId);

/**
* @notice Returns the address of the axelar gateway on this chain.
Expand Down
59 changes: 59 additions & 0 deletions test/InterchainTokenService.js
Original file line number Diff line number Diff line change
Expand Up @@ -1289,6 +1289,21 @@ describe('Interchain Token Service', () => {
);
});

it('Should revert on initiating an interchain token transfer for lockUnlock with invalid token id', async () => {
const [, , tokenId] = await deployFunctions.lockUnlock(service, 'Test Token lockUnlock', 'TT', 12, amount);
const invalidTokenId = tokenId.slice(0, -4) + '1111';

await expectRevert(
(gasOptions) =>
service.interchainTransfer(invalidTokenId, destinationChain, destAddress, amount, '0x', gasValue, {
...gasOptions,
value: gasValue,
}),
service,
'TakeTokenFailed',
);
});

it('Should revert on initiate interchain token transfer with zero amount', async () => {
await expectRevert(
(gasOptions) =>
Expand Down Expand Up @@ -1698,6 +1713,35 @@ describe('Interchain Token Service', () => {
.and.to.emit(service, 'InterchainTransferReceived')
.withArgs(commandId, tokenId, sourceChain, hexlify(wallet.address), destAddress, amount, HashZero);
});

it('Should revert with invalid token id', async () => {
const symbol = 'TT6';
const [token, , tokenId] = await deployFunctions.gateway(service, 'Test Token Lock Unlock', symbol, 12, amount);
await token.transfer(gateway.address, amount).then((tx) => tx.wait);
const invalidTokenId = tokenId.slice(0, -4) + '1111';

const payload = defaultAbiCoder.encode(
['uint256', 'bytes32', 'bytes', 'bytes', 'uint256', 'bytes'],
[MESSAGE_TYPE_INTERCHAIN_TRANSFER, invalidTokenId, hexlify(wallet.address), destAddress, amount, '0x'],
);

const commandId = await approveContractCallWithMint(
gateway,
sourceChain,
sourceAddress,
service.address,
payload,
symbol,
amount,
);

await expectRevert(
(gasOptions) => service.executeWithToken(commandId, sourceChain, sourceAddress, payload, symbol, amount, gasOptions),
service,
'TokenManagerDoesNotExist',
[invalidTokenId],
);
});
});

describe('Send Token With Data', () => {
Expand Down Expand Up @@ -2560,6 +2604,21 @@ describe('Interchain Token Service', () => {
.withArgs(wallet.address, destinationAddress, amount);
});

it('Should revert on express execute with invalid token id', async () => {
const invalidTokenId = tokenId.slice(0, -4) + '1111';

const payload = defaultAbiCoder.encode(
['uint256', 'bytes32', 'bytes', 'bytes', 'uint256', ' bytes'],
[MESSAGE_TYPE_INTERCHAIN_TRANSFER, invalidTokenId, sourceAddress, AddressZero, amount, data],
);

await expectRevert(
(gasOptions) => service.expressExecute(commandId, sourceChain, sourceAddress, payload, gasOptions),
service,
'TokenHandlerFailed',
);
});

it('Should revert on express execute if token handler transfer token from fails', async () => {
const payload = defaultAbiCoder.encode(
['uint256', 'bytes32', 'bytes', 'bytes', 'uint256', ' bytes'],
Expand Down
Loading