Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into feat/merge-new-token-…
Browse files Browse the repository at this point in the history
…managers
  • Loading branch information
Foivos committed Apr 15, 2024
2 parents a62244c + ba744ca commit aea48a7
Show file tree
Hide file tree
Showing 15 changed files with 669 additions and 108 deletions.
7 changes: 6 additions & 1 deletion .github/workflows/slither.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,9 @@ jobs:
run: npm ci

- name: Run Slither
uses: crytic/[email protected]
uses: crytic/[email protected]
env:
NO_OVERRIDES: true
with:
node-version: 18
slither-version: 0.10.1
6 changes: 3 additions & 3 deletions contracts/InterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ contract InterchainTokenFactory is IInterchainTokenFactory, ITokenManagerType, M
* @param chainNameHash_ The hash of the chain name.
* @param deployer The address of the deployer.
* @param salt A unique identifier to generate the salt.
* @return bytes32 The calculated salt for the interchain token.
* @return tokenSalt The calculated salt for the interchain token.
*/
function interchainTokenSalt(bytes32 chainNameHash_, address deployer, bytes32 salt) public pure returns (bytes32) {
return keccak256(abi.encode(PREFIX_INTERCHAIN_TOKEN_SALT, chainNameHash_, deployer, salt));
function interchainTokenSalt(bytes32 chainNameHash_, address deployer, bytes32 salt) public pure returns (bytes32 tokenSalt) {
tokenSalt = keccak256(abi.encode(PREFIX_INTERCHAIN_TOKEN_SALT, chainNameHash_, deployer, salt));
}

/**
Expand Down
142 changes: 111 additions & 31 deletions contracts/InterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contr
import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import { ExpressExecutorTracker } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressExecutorTracker.sol';
import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol';
import { Create3Address } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3Address.sol';
import { AddressBytes } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressBytes.sol';
import { Multicall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Multicall.sol';
import { Pausable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Pausable.sol';
Expand All @@ -20,6 +19,7 @@ import { IInterchainTokenDeployer } from './interfaces/IInterchainTokenDeployer.
import { IInterchainTokenExecutable } from './interfaces/IInterchainTokenExecutable.sol';
import { IInterchainTokenExpressExecutable } from './interfaces/IInterchainTokenExpressExecutable.sol';
import { ITokenManager } from './interfaces/ITokenManager.sol';
import { Create3AddressFixed } from './utils/Create3AddressFixed.sol';

import { Operator } from './utils/Operator.sol';

Expand All @@ -35,7 +35,7 @@ contract InterchainTokenService is
Operator,
Pausable,
Multicall,
Create3Address,
Create3AddressFixed,
ExpressExecutorTracker,
InterchainAddressTracker,
IInterchainTokenService
Expand Down Expand Up @@ -346,13 +346,7 @@ contract InterchainTokenService is
string calldata sourceAddress,
bytes calldata payload
) public view virtual onlyRemoteService(sourceChain, sourceAddress) whenNotPaused returns (address, uint256) {
(uint256 messageType, bytes32 tokenId, , uint256 amount) = abi.decode(payload, (uint256, bytes32, bytes, uint256));

if (messageType != MESSAGE_TYPE_INTERCHAIN_TRANSFER) {
revert InvalidExpressMessageType(messageType);
}

return (validTokenAddress(tokenId), amount);
return _contractCallValue(payload);
}

/**
Expand Down Expand Up @@ -619,32 +613,59 @@ contract InterchainTokenService is
* @param sourceAddress The address of the remote ITS where the transaction originates from.
* @param payload The encoded data payload for the transaction.
*/
function execute(bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload) public {
function execute(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload
) external onlyRemoteService(sourceChain, sourceAddress) whenNotPaused {
bytes32 payloadHash = keccak256(payload);

if (!gateway.validateContractCall(commandId, sourceChain, sourceAddress, payloadHash)) revert NotApprovedByGateway();

_execute(commandId, sourceChain, sourceAddress, payload, payloadHash);
}

/**
* @notice Returns the amount of token that this call is worth.
* @dev If `tokenAddress` is `0`, then value is in terms of the native token, otherwise it's in terms of the token address.
* @param sourceChain The source chain.
* @param sourceAddress The source address on the source chain.
* @param payload The payload sent with the call.
* @param symbol The symbol symbol for the call.
* @param amount The amount for the call.
* @return address The token address.
* @return uint256 The value the call is worth.
*/
function contractCallWithTokenValue(
string calldata /*sourceChain*/,
string calldata /*sourceAddress*/,
bytes calldata /*payload*/,
string calldata /*symbol*/,
uint256 /*amount*/
) public view virtual returns (address, uint256) {
revert ExecuteWithTokenNotSupported();
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload,
string calldata symbol,
uint256 amount
) public view virtual onlyRemoteService(sourceChain, sourceAddress) whenNotPaused returns (address, uint256) {
_checkPayloadAgainstGatewayData(payload, symbol, amount);
return _contractCallValue(payload);
}

/**
* @notice Express executes with a gateway token operations based on the payload and selector.
* @param commandId The unique message id.
* @param sourceChain The chain where the transaction originates from.
* @param sourceAddress The address of the remote ITS where the transaction originates from.
* @param payload The encoded data payload for the transaction.
* @param tokenSymbol The symbol symbol for the call.
* @param amount The amount for the call.
*/
function expressExecuteWithToken(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload,
string calldata /*tokenSymbol*/,
uint256 /*amount*/
string calldata tokenSymbol,
uint256 amount
) external payable {
_checkPayloadAgainstGatewayData(payload, tokenSymbol, amount);
// It should be ok to ignore the symbol and amount since this info exists on the payload.
expressExecute(commandId, sourceChain, sourceAddress, payload);
}
Expand All @@ -656,13 +677,22 @@ contract InterchainTokenService is
bytes calldata payload,
string calldata tokenSymbol,
uint256 amount
) external {
bytes32 payloadHash = keccak256(payload);
) external onlyRemoteService(sourceChain, sourceAddress) whenNotPaused {
_executeWithToken(commandId, sourceChain, sourceAddress, payload, tokenSymbol, amount);
}

if (!gateway.validateContractCallAndMint(commandId, sourceChain, sourceAddress, payloadHash, tokenSymbol, amount))
revert NotApprovedByGateway();
/**
* @notice Check that the tokenId from the payload is a token that is registered in the gateway with the proper tokenSymbol, with the right amount from the payload.
* Also check that the amount in the payload matches the one for the call.
* @param payload The payload for the call contract with token.
* @param tokenSymbol The tokenSymbol for the call contract with token.
* @param amount The amount for the call contract with token.
*/
function _checkPayloadAgainstGatewayData(bytes calldata payload, string calldata tokenSymbol, uint256 amount) internal view {
(, bytes32 tokenId, , , uint256 amountInPayload) = abi.decode(payload, (uint256, bytes32, uint256, uint256, uint256));

_execute(commandId, sourceChain, sourceAddress, payload, payloadHash);
if (validTokenAddress(tokenId) != gateway.tokenAddresses(tokenSymbol) || amount != amountInPayload)
revert InvalidGatewayTokenTransfer(tokenId, payload, tokenSymbol, amount);
}

/**
Expand Down Expand Up @@ -851,15 +881,10 @@ contract InterchainTokenService is
string calldata sourceAddress,
bytes calldata payload,
bytes32 payloadHash
) internal onlyRemoteService(sourceChain, sourceAddress) whenNotPaused {
) internal {
uint256 messageType = abi.decode(payload, (uint256));
if (messageType == MESSAGE_TYPE_INTERCHAIN_TRANSFER) {
address expressExecutor = _popExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash);

if (expressExecutor != address(0)) {
emit ExpressExecutionFulfilled(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor);
}

address expressExecutor = _getExpressExecutorAndEmitEvent(commandId, sourceChain, sourceAddress, payloadHash);
_processInterchainTransferPayload(commandId, expressExecutor, sourceChain, payload);
} else if (messageType == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) {
_processDeployTokenManagerPayload(payload);
Expand All @@ -870,6 +895,32 @@ contract InterchainTokenService is
}
}

function _executeWithToken(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes calldata payload,
string calldata tokenSymbol,
uint256 amount
) internal {
bytes32 payloadHash = keccak256(payload);

if (!gateway.validateContractCallAndMint(commandId, sourceChain, sourceAddress, payloadHash, tokenSymbol, amount))
revert NotApprovedByGateway();

uint256 messageType = abi.decode(payload, (uint256));
if (messageType != MESSAGE_TYPE_INTERCHAIN_TRANSFER) {
revert InvalidMessageType(messageType);
}

_checkPayloadAgainstGatewayData(payload, tokenSymbol, amount);

// slither-disable-next-line reentrancy-events
address expressExecutor = _getExpressExecutorAndEmitEvent(commandId, sourceChain, sourceAddress, payloadHash);

_processInterchainTransferPayload(commandId, expressExecutor, sourceChain, payload);
}

/**
* @notice Deploys a token manager on a destination chain.
* @param tokenId The ID of the token.
Expand Down Expand Up @@ -1088,4 +1139,33 @@ contract InterchainTokenService is

return (amount, tokenAddress);
}

/**
* @notice Returns the amount of token that this call is worth.
* @dev If `tokenAddress` is `0`, then value is in terms of the native token, otherwise it's in terms of the token address.
* @param payload The payload sent with the call.
* @return address The token address.
* @return uint256 The value the call is worth.
*/
function _contractCallValue(bytes calldata payload) internal view returns (address, uint256) {
(uint256 messageType, bytes32 tokenId, , , uint256 amount) = abi.decode(payload, (uint256, bytes32, bytes, bytes, uint256));
if (messageType != MESSAGE_TYPE_INTERCHAIN_TRANSFER) {
revert InvalidExpressMessageType(messageType);
}

return (validTokenAddress(tokenId), amount);
}

function _getExpressExecutorAndEmitEvent(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash
) internal returns (address expressExecutor) {
expressExecutor = _popExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash);

if (expressExecutor != address(0)) {
emit ExpressExecutionFulfilled(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor);
}
}
}
45 changes: 17 additions & 28 deletions contracts/TokenHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { ITokenHandler } from './interfaces/ITokenHandler.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { SafeTokenTransfer, 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 { Create3Address } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/deploy/Create3Address.sol';
import { Create3AddressFixed } from './utils/Create3AddressFixed.sol';

import { ITokenManagerType } from './interfaces/ITokenManagerType.sol';
import { ITokenManager } from './interfaces/ITokenManager.sol';
Expand All @@ -19,7 +19,7 @@ import { IERC20Named } from './interfaces/IERC20Named.sol';
* @title TokenHandler
* @notice This interface is responsible for handling tokens before initiating an interchain token transfer, or after receiving one.
*/
contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Create3Address {
contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Create3AddressFixed {
using SafeTokenTransferFrom for IERC20;
using SafeTokenCall for IERC20;
using SafeTokenTransfer for IERC20;
Expand Down Expand Up @@ -81,7 +81,7 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea
/**
* @notice This function takes token from a specified address to the token manager.
* @param tokenId The tokenId for the token.
* @param tokenOnly can onky be called from the token.
* @param tokenOnly can only be called from the token.
* @param from The address to take tokens from.
* @param amount The amount of token to take.
* @return uint256 The amount of token actually taken, which could be different for certain token type.
Expand All @@ -104,36 +104,25 @@ contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Crea

if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN)) {
_takeInterchainToken(tokenAddress, from, amount);
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.MINT_BURN)) {
_burnToken(tokenManager, tokenAddress, from, amount);
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)) {
_burnTokenFrom(tokenAddress, from, amount);
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
} else if (tokenManagerType == uint256(TokenManagerType.MINT_BURN)) {
_takeTokenMintBurn(tokenAddress, from, amount);
} else if (tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)) {
_takeTokenMintBurnFrom(tokenAddress, from, amount);
} else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
_transferTokenFrom(tokenAddress, from, tokenManager, amount);
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
} else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
amount = _transferTokenFromWithFee(tokenAddress, from, tokenManager, amount);
return (amount, symbol);
}

if (tokenManagerType == uint256(TokenManagerType.GATEWAY)) {
_transferTokenFrom(tokenAddress, from, address(this), amount);
} else if (tokenManagerType == uint256(TokenManagerType.GATEWAY)) {
symbol = IERC20Named(tokenAddress).symbol();
return (amount, symbol);
_transferTokenFrom(tokenAddress, from, address(this), amount);
} else {
revert UnsupportedTokenManagerType(tokenManagerType);
}

revert UnsupportedTokenManagerType(tokenManagerType);
/// @dev Track the flow amount being sent out as a message
ITokenManager(tokenManager).addFlowOut(amount);

return (amount, symbol);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions contracts/interfaces/IInterchainTokenFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ interface IInterchainTokenFactory is IUpgradable, IMulticall {
* @param chainNameHash_ The hash of the chain name.
* @param deployer The address of the deployer.
* @param salt A unique identifier to generate the salt.
* @return bytes32 The calculated salt for the interchain token.
* @return tokenSalt The calculated salt for the interchain token.
*/
function interchainTokenSalt(bytes32 chainNameHash_, address deployer, bytes32 salt) external view returns (bytes32);
function interchainTokenSalt(bytes32 chainNameHash_, address deployer, bytes32 salt) external view returns (bytes32 tokenSalt);

/**
* @notice Computes the ID for an interchain token based on the deployer and a salt.
Expand Down Expand Up @@ -97,9 +97,9 @@ interface IInterchainTokenFactory is IUpgradable, IMulticall {
* @notice Calculates the salt for a canonical interchain token.
* @param chainNameHash_ The hash of the chain name.
* @param tokenAddress The address of the token.
* @return salt The calculated salt for the interchain token.
* @return tokenSalt The calculated salt for the interchain token.
*/
function canonicalInterchainTokenSalt(bytes32 chainNameHash_, address tokenAddress) external view returns (bytes32 salt);
function canonicalInterchainTokenSalt(bytes32 chainNameHash_, address tokenAddress) external view returns (bytes32 tokenSalt);

/**
* @notice Computes the ID for a canonical interchain token based on its address.
Expand Down
1 change: 1 addition & 0 deletions contracts/interfaces/IInterchainTokenService.sol
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ interface IInterchainTokenService is
error PostDeployFailed(bytes data);
error ZeroAmount();
error CannotDeploy(TokenManagerType);
error InvalidGatewayTokenTransfer(bytes32 tokenId, bytes payload, string tokenSymbol, uint256 amount);

event InterchainTransfer(
bytes32 indexed tokenId,
Expand Down
2 changes: 1 addition & 1 deletion contracts/interfaces/ITokenHandler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ interface ITokenHandler {
/**
* @notice This function takes token from a specified address to the token manager.
* @param tokenId The tokenId for the token.
* @param tokenOnly can onky be called from the token.
* @param tokenOnly can only be called from the token.
* @param from The address to take tokens from.
* @param amount The amount of token to take.
* @return uint256 The amount of token actually taken, which could be different for certain token type.
Expand Down
19 changes: 19 additions & 0 deletions contracts/test/utils/TestCreate3Fixed.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import { Create3Fixed } from '../../utils/Create3Fixed.sol';

contract TestCreate3Fixed is Create3Fixed {
event Deployed(address addr);

function deploy(bytes memory code, bytes32 salt) public payable returns (address addr) {
addr = _create3(code, salt);

emit Deployed(addr);
}

function deployedAddress(bytes32 salt) public view returns (address addr) {
addr = _create3Address(salt);
}
}
Loading

0 comments on commit aea48a7

Please sign in to comment.