Skip to content

Commit

Permalink
feat: canonical tokens cannot be gateway tokens (#169)
Browse files Browse the repository at this point in the history
* reverting on gateway canoical tokens

* prettier
  • Loading branch information
Foivos authored Nov 8, 2023
1 parent 45172ac commit 4e49bed
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 3 deletions.
12 changes: 11 additions & 1 deletion contracts/InterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { AddressBytes } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/
import { SafeTokenTransfer, SafeTokenTransferFrom, SafeTokenCall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
import { Multicall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Multicall.sol';
import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol';
import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';

import { IInterchainTokenService } from './interfaces/IInterchainTokenService.sol';
import { IInterchainTokenFactory } from './interfaces/IInterchainTokenFactory.sol';
Expand All @@ -22,6 +23,7 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M

IInterchainTokenService public immutable service;
bytes32 public immutable chainNameHash;
IAxelarGateway public immutable gateway;

bytes32 private constant CONTRACT_ID = keccak256('interchain-token-factory');
bytes32 internal constant PREFIX_CANONICAL_TOKEN_SALT = keccak256('canonical-token-salt');
Expand All @@ -33,6 +35,7 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
service = IInterchainTokenService(interchainTokenServiceAddress);

chainNameHash = service.chainNameHash();
gateway = service.gateway();
}

/**
Expand Down Expand Up @@ -162,6 +165,9 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M

function registerCanonicalInterchainToken(address tokenAddress) external payable returns (bytes32 tokenId) {
bytes memory params = abi.encode('', tokenAddress);

if (_isGatewayToken(tokenAddress)) revert GatewayToken(tokenAddress);

bytes32 salt = canonicalInterchainTokenSalt(chainNameHash, tokenAddress);
tokenId = service.deployTokenManager(salt, '', TokenManagerType.LOCK_UNLOCK, params, 0);
}
Expand Down Expand Up @@ -222,7 +228,6 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M

/**
* @dev Allow any token to be approved to the token manager.
* TODO: Move this into a dedicated approve + transfer method to prevent unused approvals to be created that some tokens don't like.
*/
function tokenApprove(bytes32 tokenId, uint256 amount) external payable {
address tokenAddress = service.validTokenAddress(tokenId);
Expand All @@ -231,4 +236,9 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M

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

function _isGatewayToken(address token) internal view returns (bool) {
string memory symbol = IInterchainToken(token).symbol();
return token == gateway.tokenAddresses(symbol);
}
}
1 change: 1 addition & 0 deletions contracts/interfaces/IInterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface IInterchainTokenFactory {
error NotOperator(address operator);
error NonZeroMintAmount();
error ApproveFailed();
error GatewayToken(address tokenAddress);

function chainNameHash() external view returns (bytes32);

Expand Down
45 changes: 43 additions & 2 deletions test/InterchainTokenFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const {
} = ethers;

const { deployAll, deployContract } = require('../scripts/deploy');
const { getRandomBytes32 } = require('./utils');
const { getRandomBytes32, expectRevert } = require('./utils');

// const MESSAGE_TYPE_INTERCHAIN_TRANSFER_WITH_DATA = 1;
const MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN = 2;
Expand All @@ -33,7 +33,7 @@ describe('InterchainTokenFactory', () => {
const decimals = 18;
const destinationChain = 'destination chain';

before(async () => {
beforeEach(async () => {
const wallets = await ethers.getSigners();
wallet = wallets[0];
[service, gateway, gasService, tokenFactory] = await deployAll(wallet, chainName, [destinationChain]);
Expand Down Expand Up @@ -165,6 +165,47 @@ describe('InterchainTokenFactory', () => {
.and.to.emit(service, 'InterchainTransfer')
.withArgs(tokenId, destinationChain, destinationAddress, amount);
});

it('Should revert when trying to register a canonical lock/unlock gateway token', async () => {
await deployToken();

const tokenCap = 0;
const mintLimit = 0;
const tokenAddress = token.address;

const params = defaultAbiCoder.encode(
['string', 'string', 'uint8', 'uint256', 'address', 'uint256'],
[name, symbol, decimals, tokenCap, tokenAddress, mintLimit],
);
await (await gateway.deployToken(params, getRandomBytes32())).wait();

await expectRevert(
(gasOptions) => tokenFactory.registerCanonicalInterchainToken(tokenAddress, gasOptions),
tokenFactory,
'GatewayToken',
[tokenAddress],
);
});

it('Should revert when trying to register a canonical mint/burn gateway token', async () => {
const tokenCap = 0;
let tokenAddress = AddressZero;
const mintLimit = 0;
const params = defaultAbiCoder.encode(
['string', 'string', 'uint8', 'uint256', 'address', 'uint256'],
[name, symbol, decimals, tokenCap, tokenAddress, mintLimit],
);
await (await gateway.deployToken(params, getRandomBytes32())).wait();

tokenAddress = await gateway.tokenAddresses(symbol);

await expectRevert(
(gasOptions) => tokenFactory.registerCanonicalInterchainToken(tokenAddress, gasOptions),
tokenFactory,
'GatewayToken',
[tokenAddress],
);
});
});

describe('Interchain Token Factory', async () => {
Expand Down

0 comments on commit 4e49bed

Please sign in to comment.