diff --git a/contracts/InterchainTokenService.sol b/contracts/InterchainTokenService.sol index e018b8f8..923f1ad5 100644 --- a/contracts/InterchainTokenService.sol +++ b/contracts/InterchainTokenService.sol @@ -1042,6 +1042,8 @@ contract InterchainTokenService is string memory symbol, uint256 gasValue ) internal { + if (amount == 0) revert ZeroAmount(); + // slither-disable-next-line reentrancy-events emit InterchainTransfer( tokenId, diff --git a/contracts/interfaces/IInterchainTokenService.sol b/contracts/interfaces/IInterchainTokenService.sol index 6913b827..d069871e 100644 --- a/contracts/interfaces/IInterchainTokenService.sol +++ b/contracts/interfaces/IInterchainTokenService.sol @@ -47,6 +47,7 @@ interface IInterchainTokenService is error TokenHandlerFailed(bytes data); error EmptyData(); error PostDeployFailed(bytes data); + error ZeroAmount(); event InterchainTransfer( bytes32 indexed tokenId, diff --git a/test/InterchainTokenService.js b/test/InterchainTokenService.js index d99cbddd..868b7f0e 100644 --- a/test/InterchainTokenService.js +++ b/test/InterchainTokenService.js @@ -1258,6 +1258,18 @@ describe('Interchain Token Service', () => { ); }); + it(`Should revert on initiate interchain token transfer with zero amount`, async () => { + await expectRevert( + (gasOptions) => + service.interchainTransfer(tokenId, destinationChain, destAddress, 0, '0x', gasValue, { + ...gasOptions, + value: gasValue, + }), + service, + 'ZeroAmount', + ); + }); + it(`Should revert on initiate interchain token transfer when service is paused`, async () => { await service.setPauseStatus(true).then((tx) => tx.wait); @@ -1638,6 +1650,16 @@ describe('Interchain Token Service', () => { .withArgs(tokenId, sourceAddress, destinationChain, destAddress, sendAmount, HashZero); }); + it(`Should revert on callContractWithInterchainToken function on the service if amount is 0`, async () => { + const [, , tokenId] = await deployFunctions.lockUnlock(`Test Token`, 'TT', 12, amount); + + await expectRevert( + (gasOptions) => service.callContractWithInterchainToken(tokenId, destinationChain, destAddress, 0, data, 0, gasOptions), + service, + 'ZeroAmount', + ); + }); + for (const type of ['lockUnlock', 'lockUnlockFee']) { it(`Should be able to initiate an interchain token transfer via the interchainTransfer function on the service when the service is approved as well [${type}]`, async () => { const [token, tokenManager, tokenId] = await deployFunctions[type](`Test Token ${type}`, 'TT', 12, amount); @@ -2030,6 +2052,22 @@ describe('Interchain Token Service', () => { .withArgs(tokenId, sender.address, destinationChain, destAddress, sendAmount, HashZero); }); + it(`Should revert using interchainTransferFrom with zero amount`, async () => { + const sender = wallet; + const spender = otherWallet; + await token.approve(spender.address, MaxUint256).then((tx) => tx.wait); + + await expectRevert( + (gasOptions) => + token.connect(spender).interchainTransferFrom(sender.address, destinationChain, destAddress, 0, metadata, { + value: gasValue, + ...gasOptions, + }), + service, + 'ZeroAmount', + ); + }); + it(`Should be able to initiate an interchain token transfer via interchainTransfer & interchainTransferFrom [gateway]`, async () => { const symbol = 'TT2'; [token, tokenManager, tokenId] = await deployFunctions.gateway(`Test Token gateway`, symbol, 12, amount * 3, true);