// Source: contracts/InterchainTokenService.sol pragma solidity ^0.8.0; // SPDX-License-Identifier: MIT // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol@v6.0.4 /** * @title IAxelarGateway * @dev Interface for the Axelar Gateway that supports general message passing and contract call execution. */ interface IAxelarGateway { /** * @notice Emitted when a contract call is made through the gateway. * @dev Logs the attempt to call a contract on another chain. * @param sender The address of the sender who initiated the contract call. * @param destinationChain The name of the destination chain. * @param destinationContractAddress The address of the contract on the destination chain. * @param payloadHash The keccak256 hash of the sent payload data. * @param payload The payload data used for the contract call. */ event ContractCall( address indexed sender, string destinationChain, string destinationContractAddress, bytes32 indexed payloadHash, bytes payload ); /** * @notice Sends a contract call to another chain. * @dev Initiates a cross-chain contract call through the gateway to the specified destination chain and contract. * @param destinationChain The name of the destination chain. * @param contractAddress The address of the contract on the destination chain. * @param payload The payload data to be used in the contract call. */ function callContract(string calldata destinationChain, string calldata contractAddress, bytes calldata payload) external; /** * @notice Checks if a contract call is approved. * @dev Determines whether a given contract call, identified by the commandId and payloadHash, is approved. * @param commandId The identifier of the command to check. * @param sourceChain The name of the source chain. * @param sourceAddress The address of the sender on the source chain. * @param contractAddress The address of the contract where the call will be executed. * @param payloadHash The keccak256 hash of the payload data. * @return True if the contract call is approved, false otherwise. */ function isContractCallApproved( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, address contractAddress, bytes32 payloadHash ) external view returns (bool); /** * @notice Validates and approves a contract call. * @dev Validates the given contract call information and marks it as approved if valid. * @param commandId The identifier of the command to validate. * @param sourceChain The name of the source chain. * @param sourceAddress The address of the sender on the source chain. * @param payloadHash The keccak256 hash of the payload data. * @return True if the contract call is validated and approved, false otherwise. */ function validateContractCall( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash ) external returns (bool); /** * @notice Checks if a command has been executed. * @dev Determines whether a command, identified by the commandId, has been executed. * @param commandId The identifier of the command to check. * @return True if the command has been executed, false otherwise. */ function isCommandExecuted(bytes32 commandId) external view returns (bool); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarExecutable.sol@v6.0.4 /** * @title IAxelarExecutable * @dev Interface for a contract that is executable by Axelar Gateway's cross-chain message passing. * It defines a standard interface to execute commands sent from another chain. */ interface IAxelarExecutable { /** * @dev Thrown when a function is called with an invalid address. */ error InvalidAddress(); /** * @dev Thrown when the call is not approved by the Axelar Gateway. */ error NotApprovedByGateway(); /** * @notice Returns the address of the AxelarGateway contract. * @return The Axelar Gateway contract associated with this executable contract. */ function gateway() external view returns (IAxelarGateway); /** * @notice Executes the specified command sent from another chain. * @dev This function is called by the Axelar Gateway to carry out cross-chain commands. * Reverts if the call is not approved by the gateway or other checks fail. * @param commandId The identifier of the command to execute. * @param sourceChain The name of the source chain from where the command originated. * @param sourceAddress The address on the source chain that sent the command. * @param payload The payload of the command to be executed. This typically includes the function selector and encoded arguments. */ function execute(bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload) external; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarExpressExecutable.sol@v6.0.4 /** * @title IAxelarExpressExecutable * @notice Interface for the Axelar Express Executable contract. */ interface IAxelarExpressExecutable is IAxelarExecutable { // Custom errors error AlreadyExecuted(); error InsufficientValue(); /** * @notice Emitted when an express execution is successfully performed. * @param commandId The unique identifier for the command. * @param sourceChain The source chain. * @param sourceAddress The source address. * @param payloadHash The hash of the payload. * @param expressExecutor The address of the express executor. */ event ExpressExecuted( bytes32 indexed commandId, string sourceChain, string sourceAddress, bytes32 payloadHash, address indexed expressExecutor ); /** * @notice Emitted when an express execution is fulfilled. * @param commandId The commandId for the contractCall. * @param sourceChain The source chain. * @param sourceAddress The source address. * @param payloadHash The hash of the payload. * @param expressExecutor The address of the express executor. */ event ExpressExecutionFulfilled( bytes32 indexed commandId, string sourceChain, string sourceAddress, bytes32 payloadHash, address indexed expressExecutor ); /** * @notice Returns the express executor for a given command. * @param commandId The commandId for the contractCall. * @param sourceChain The source chain. * @param sourceAddress The source address. * @param payloadHash The hash of the payload. * @return expressExecutor The address of the express executor. */ function getExpressExecutor( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash ) external view returns (address expressExecutor); /** * @notice Express executes a contract call. * @param commandId The commandId for the contractCall. * @param sourceChain The source chain. * @param sourceAddress The source address. * @param payload The payload data. */ function expressExecute( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) external payable; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/types/GasEstimationTypes.sol@v6.0.4 /** * @title GasEstimationType * @notice This enum represents the gas estimation types for different chains. */ enum GasEstimationType { Default, OptimismEcotone, OptimismBedrock, Arbitrum, Scroll } /** * @title GasInfo * @notice This struct represents the gas pricing information for a specific chain. * @dev Smaller uint types are used for efficient struct packing to save storage costs. */ struct GasInfo { /// @dev Custom gas pricing rule, such as L1 data fee on L2s uint64 gasEstimationType; /// @dev Scalar value needed for specific gas estimation types, expected to be less than 1e10 uint64 l1FeeScalar; /// @dev Axelar base fee for cross-chain message approval on destination, in terms of source native gas token uint128 axelarBaseFee; /// @dev Gas price of destination chain, in terms of the source chain token, i.e dest_gas_price * dest_token_market_price / src_token_market_price uint128 relativeGasPrice; /// @dev Needed for specific gas estimation types. Blob base fee of destination chain, in terms of the source chain token, i.e dest_blob_base_fee * dest_token_market_price / src_token_market_price uint128 relativeBlobBaseFee; /// @dev Axelar express fee for express execution, in terms of source chain token uint128 expressFee; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IInterchainGasEstimation.sol@v6.0.4 /** * @title IInterchainGasEstimation Interface * @notice This is an interface for the InterchainGasEstimation contract * which allows for estimating gas fees for cross-chain communication on the Axelar network. */ interface IInterchainGasEstimation { error UnsupportedEstimationType(GasEstimationType gasEstimationType); /** * @notice Event emitted when the gas price for a specific chain is updated. * @param chain The name of the chain * @param info The gas info for the chain */ event GasInfoUpdated(string chain, GasInfo info); /** * @notice Returns the gas price for a specific chain. * @param chain The name of the chain * @return gasInfo The gas info for the chain */ function getGasInfo(string calldata chain) external view returns (GasInfo memory); /** * @notice Estimates the gas fee for a cross-chain contract call. * @param destinationChain Axelar registered name of the destination chain * @param destinationAddress Destination contract address being called * @param executionGasLimit The gas limit to be used for the destination contract execution, * e.g. pass in 200k if your app consumes needs upto 200k for this contract call * @param params Additional parameters for the gas estimation * @return gasEstimate The cross-chain gas estimate, in terms of source chain's native gas token that should be forwarded to the gas service. */ function estimateGasFee( string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, uint256 executionGasLimit, bytes calldata params ) external view returns (uint256 gasEstimate); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IContractIdentifier.sol@v6.0.4 // General interface for upgradable contracts interface IContractIdentifier { /** * @notice Returns the contract ID. It can be used as a check during upgrades. * @dev Meant to be overridden in derived contracts. * @return bytes32 The contract ID */ function contractId() external pure returns (bytes32); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol@v6.0.4 interface IImplementation is IContractIdentifier { error NotProxy(); function setup(bytes calldata data) external; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IOwnable.sol@v6.0.4 /** * @title IOwnable Interface * @notice IOwnable is an interface that abstracts the implementation of a * contract with ownership control features. It's commonly used in upgradable * contracts and includes the functionality to get current owner, transfer * ownership, and propose and accept ownership. */ interface IOwnable { error NotOwner(); error InvalidOwner(); error InvalidOwnerAddress(); event OwnershipTransferStarted(address indexed newOwner); event OwnershipTransferred(address indexed newOwner); /** * @notice Returns the current owner of the contract. * @return address The address of the current owner */ function owner() external view returns (address); /** * @notice Returns the address of the pending owner of the contract. * @return address The address of the pending owner */ function pendingOwner() external view returns (address); /** * @notice Transfers ownership of the contract to a new address * @param newOwner The address to transfer ownership to */ function transferOwnership(address newOwner) external; /** * @notice Proposes to transfer the contract's ownership to a new address. * The new owner needs to accept the ownership explicitly. * @param newOwner The address to transfer ownership to */ function proposeOwnership(address newOwner) external; /** * @notice Transfers ownership to the pending owner. * @dev Can only be called by the pending owner */ function acceptOwnership() external; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IUpgradable.sol@v6.0.4 // General interface for upgradable contracts interface IUpgradable is IOwnable, IImplementation { error InvalidCodeHash(); error InvalidImplementation(); error SetupFailed(); event Upgraded(address indexed newImplementation); function implementation() external view returns (address); function upgrade(address newImplementation, bytes32 newImplementationCodeHash, bytes calldata params) external; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol@v6.0.4 /** * @title IAxelarGasService Interface * @notice This is an interface for the AxelarGasService contract which manages gas payments * and refunds for cross-chain communication on the Axelar network. * @dev This interface inherits IUpgradable */ interface IAxelarGasService is IInterchainGasEstimation, IUpgradable { error InvalidAddress(); error NotCollector(); error InvalidAmounts(); error InvalidGasUpdates(); error InvalidParams(); error InsufficientGasPayment(uint256 required, uint256 provided); event GasPaidForContractCall( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, address gasToken, uint256 gasFeeAmount, address refundAddress ); event GasPaidForContractCallWithToken( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, address gasToken, uint256 gasFeeAmount, address refundAddress ); event NativeGasPaidForContractCall( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, uint256 gasFeeAmount, address refundAddress ); event NativeGasPaidForContractCallWithToken( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, uint256 gasFeeAmount, address refundAddress ); event GasPaidForExpressCall( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, address gasToken, uint256 gasFeeAmount, address refundAddress ); event GasPaidForExpressCallWithToken( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, address gasToken, uint256 gasFeeAmount, address refundAddress ); event NativeGasPaidForExpressCall( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, uint256 gasFeeAmount, address refundAddress ); event NativeGasPaidForExpressCallWithToken( address indexed sourceAddress, string destinationChain, string destinationAddress, bytes32 indexed payloadHash, string symbol, uint256 amount, uint256 gasFeeAmount, address refundAddress ); event GasAdded(bytes32 indexed txHash, uint256 indexed logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress); event NativeGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress); event ExpressGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress); event NativeExpressGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress); event Refunded(bytes32 indexed txHash, uint256 indexed logIndex, address payable receiver, address token, uint256 amount); /** * @notice Pay for gas for any type of contract execution on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @dev If estimateOnChain is true, the function will estimate the gas cost and revert if the payment is insufficient. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param executionGasLimit The gas limit for the contract call * @param estimateOnChain Flag to enable on-chain gas estimation * @param refundAddress The address where refunds, if any, should be sent * @param params Additional parameters for gas payment. This can be left empty for normal contract call payments. */ function payGas( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, uint256 executionGasLimit, bool estimateOnChain, address refundAddress, bytes calldata params ) external payable; /** * @notice Pay for gas using ERC20 tokens for a contract call on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param gasToken The address of the ERC20 token used to pay for gas * @param gasFeeAmount The amount of tokens to pay for gas * @param refundAddress The address where refunds, if any, should be sent */ function payGasForContractCall( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Pay for gas using ERC20 tokens for a contract call with tokens on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call with tokens will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call with tokens * @param symbol The symbol of the token to be sent with the call * @param amount The amount of tokens to be sent with the call * @param gasToken The address of the ERC20 token used to pay for gas * @param gasFeeAmount The amount of tokens to pay for gas * @param refundAddress The address where refunds, if any, should be sent */ function payGasForContractCallWithToken( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, string calldata symbol, uint256 amount, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Pay for gas using native currency for a contract call on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param refundAddress The address where refunds, if any, should be sent */ function payNativeGasForContractCall( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, address refundAddress ) external payable; /** * @notice Pay for gas using native currency for a contract call with tokens on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call with tokens will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call with tokens * @param symbol The symbol of the token to be sent with the call * @param amount The amount of tokens to be sent with the call * @param refundAddress The address where refunds, if any, should be sent */ function payNativeGasForContractCallWithToken( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, string calldata symbol, uint256 amount, address refundAddress ) external payable; /** * @notice Pay for gas using ERC20 tokens for an express contract call on a destination chain. * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param gasToken The address of the ERC20 token used to pay for gas * @param gasFeeAmount The amount of tokens to pay for gas * @param refundAddress The address where refunds, if any, should be sent */ function payGasForExpressCall( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Pay for gas using ERC20 tokens for an express contract call with tokens on a destination chain. * @dev This function is called on the source chain before calling the gateway to express execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call with tokens will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call with tokens * @param symbol The symbol of the token to be sent with the call * @param amount The amount of tokens to be sent with the call * @param gasToken The address of the ERC20 token used to pay for gas * @param gasFeeAmount The amount of tokens to pay for gas * @param refundAddress The address where refunds, if any, should be sent */ function payGasForExpressCallWithToken( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, string calldata symbol, uint256 amount, address gasToken, uint256 gasFeeAmount, address refundAddress ) external; /** * @notice Pay for gas using native currency for an express contract call on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call * @param refundAddress The address where refunds, if any, should be sent */ function payNativeGasForExpressCall( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, address refundAddress ) external payable; /** * @notice Pay for gas using native currency for an express contract call with tokens on a destination chain. * @dev This function is called on the source chain before calling the gateway to execute a remote contract. * @param sender The address making the payment * @param destinationChain The target chain where the contract call with tokens will be made * @param destinationAddress The target address on the destination chain * @param payload Data payload for the contract call with tokens * @param symbol The symbol of the token to be sent with the call * @param amount The amount of tokens to be sent with the call * @param refundAddress The address where refunds, if any, should be sent */ function payNativeGasForExpressCallWithToken( address sender, string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, string calldata symbol, uint256 amount, address refundAddress ) external payable; /** * @notice Add additional gas payment using ERC20 tokens after initiating a cross-chain call. * @dev This function can be called on the source chain after calling the gateway to execute a remote contract. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param gasToken The ERC20 token address used to add gas * @param gasFeeAmount The amount of tokens to add as gas * @param refundAddress The address where refunds, if any, should be sent */ function addGas(bytes32 txHash, uint256 logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress) external; /** * @notice Add additional gas payment using native currency after initiating a cross-chain call. * @dev This function can be called on the source chain after calling the gateway to execute a remote contract. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param refundAddress The address where refunds, if any, should be sent */ function addNativeGas(bytes32 txHash, uint256 logIndex, address refundAddress) external payable; /** * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call. * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param gasToken The ERC20 token address used to add gas * @param gasFeeAmount The amount of tokens to add as gas * @param refundAddress The address where refunds, if any, should be sent */ function addExpressGas(bytes32 txHash, uint256 logIndex, address gasToken, uint256 gasFeeAmount, address refundAddress) external; /** * @notice Add additional gas payment using native currency after initiating an express cross-chain call. * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param refundAddress The address where refunds, if any, should be sent */ function addNativeExpressGas(bytes32 txHash, uint256 logIndex, address refundAddress) external payable; /** * @notice Updates the gas price for a specific chain. * @dev This function is called by the gas oracle to update the gas prices for a specific chains. * @param chains Array of chain names * @param gasUpdates Array of gas updates */ function updateGasInfo(string[] calldata chains, GasInfo[] calldata gasUpdates) external; /** * @notice Allows the gasCollector to collect accumulated fees from the contract. * @dev Use address(0) as the token address for native currency. * @param receiver The address to receive the collected fees * @param tokens Array of token addresses to be collected * @param amounts Array of amounts to be collected for each respective token address */ function collectFees(address payable receiver, address[] calldata tokens, uint256[] calldata amounts) external; /** * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction. * @dev Only callable by the gasCollector. * @dev Use address(0) as the token address to refund native currency. * @param txHash The transaction hash of the cross-chain call * @param logIndex The log index for the cross-chain call * @param receiver The address to receive the refund * @param token The token address to be refunded * @param amount The amount to refund */ function refund(bytes32 txHash, uint256 logIndex, address payable receiver, address token, uint256 amount) external; /** * @notice Returns the address of the designated gas collector. * @return address of the gas collector */ function gasCollector() external returns (address); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarValuedExpressExecutable.sol@v6.0.4 /** * @title IAxelarValuedExpressExecutable * @dev Interface for the Axelar Valued Express Executable contract. */ interface IAxelarValuedExpressExecutable is IAxelarExpressExecutable { /** * @dev Returns the value (token address and amount) associated with a contract call * @param sourceChain The source chain. * @param sourceAddress The source address. * @param payload The payload data. * @return tokenAddress The address of the token used. * @return value The value associated with the contract call. */ function contractCallValue( string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) external view returns (address tokenAddress, uint256 value); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Implementation.sol@v6.0.4 /** * @title Implementation * @notice This contract serves as a base for other contracts and enforces a proxy-first access restriction. * @dev Derived contracts must implement the setup function. */ abstract contract Implementation is IImplementation { address private immutable implementationAddress; /** * @dev Contract constructor that sets the implementation address to the address of this contract. */ constructor() { implementationAddress = address(this); } /** * @dev Modifier to require the caller to be the proxy contract. * Reverts if the caller is the current contract (i.e., the implementation contract itself). */ modifier onlyProxy() { if (implementationAddress == address(this)) revert NotProxy(); _; } /** * @notice Initializes contract parameters. * This function is intended to be overridden by derived contracts. * The overriding function must have the onlyProxy modifier. * @param params The parameters to be used for initialization */ function setup(bytes calldata params) external virtual; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Ownable.sol@v6.0.4 /** * @title Ownable * @notice A contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * The owner account is set through ownership transfer. This module makes * it possible to transfer the ownership of the contract to a new account in one * step, as well as to an interim pending owner. In the second flow the ownership does not * change until the pending owner accepts the ownership transfer. */ abstract contract Ownable is IOwnable { // keccak256('owner') bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0; // keccak256('ownership-transfer') bytes32 internal constant _OWNERSHIP_TRANSFER_SLOT = 0x9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d1; /** * @notice Initializes the contract by transferring ownership to the owner parameter. * @param _owner Address to set as the initial owner of the contract */ constructor(address _owner) { _transferOwnership(_owner); } /** * @notice Modifier that throws an error if called by any account other than the owner. */ modifier onlyOwner() { if (owner() != msg.sender) revert NotOwner(); _; } /** * @notice Returns the current owner of the contract. * @return owner_ The current owner of the contract */ function owner() public view returns (address owner_) { assembly { owner_ := sload(_OWNER_SLOT) } } /** * @notice Returns the pending owner of the contract. * @return owner_ The pending owner of the contract */ function pendingOwner() public view returns (address owner_) { assembly { owner_ := sload(_OWNERSHIP_TRANSFER_SLOT) } } /** * @notice Transfers ownership of the contract to a new account `newOwner`. * @dev Can only be called by the current owner. * @param newOwner The address to transfer ownership to */ function transferOwnership(address newOwner) external virtual onlyOwner { _transferOwnership(newOwner); } /** * @notice Propose to transfer ownership of the contract to a new account `newOwner`. * @dev Can only be called by the current owner. The ownership does not change * until the new owner accepts the ownership transfer. * @param newOwner The address to transfer ownership to */ function proposeOwnership(address newOwner) external virtual onlyOwner { if (newOwner == address(0)) revert InvalidOwnerAddress(); emit OwnershipTransferStarted(newOwner); assembly { sstore(_OWNERSHIP_TRANSFER_SLOT, newOwner) } } /** * @notice Accepts ownership of the contract. * @dev Can only be called by the pending owner */ function acceptOwnership() external virtual { address newOwner = pendingOwner(); if (newOwner != msg.sender) revert InvalidOwner(); _transferOwnership(newOwner); } /** * @notice Internal function to transfer ownership of the contract to a new account `newOwner`. * @dev Called in the constructor to set the initial owner. * @param newOwner The address to transfer ownership to */ function _transferOwnership(address newOwner) internal virtual { if (newOwner == address(0)) revert InvalidOwnerAddress(); emit OwnershipTransferred(newOwner); assembly { sstore(_OWNER_SLOT, newOwner) sstore(_OWNERSHIP_TRANSFER_SLOT, 0) } } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol@v6.0.4 /** * @title Upgradable Contract * @notice This contract provides an interface for upgradable smart contracts and includes the functionality to perform upgrades. */ abstract contract Upgradable is Ownable, Implementation, IUpgradable { // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @notice Constructor sets the implementation address to the address of the contract itself * @dev This is used in the onlyProxy modifier to prevent certain functions from being called directly * on the implementation contract itself. * @dev The owner is initially set as address(1) because the actual owner is set within the proxy. It is not * set as the zero address because Ownable is designed to throw an error for ownership transfers to the zero address. */ constructor() Ownable(address(1)) {} /** * @notice Returns the address of the current implementation * @return implementation_ Address of the current implementation */ function implementation() public view returns (address implementation_) { assembly { implementation_ := sload(_IMPLEMENTATION_SLOT) } } /** * @notice Upgrades the contract to a new implementation * @param newImplementation The address of the new implementation contract * @param newImplementationCodeHash The codehash of the new implementation contract * @param params Optional setup parameters for the new implementation contract * @dev This function is only callable by the owner. */ function upgrade(address newImplementation, bytes32 newImplementationCodeHash, bytes calldata params) external override onlyOwner { if (IUpgradable(newImplementation).contractId() != IUpgradable(implementation()).contractId()) revert InvalidImplementation(); if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash(); assembly { sstore(_IMPLEMENTATION_SLOT, newImplementation) } emit Upgraded(newImplementation); if (params.length > 0) { // slither-disable-next-line controlled-delegatecall (bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(this.setup.selector, params)); if (!success) revert SetupFailed(); } } /** * @notice Sets up the contract with initial data * @param data Initialization data for the contract * @dev This function is only callable by the proxy contract. */ function setup(bytes calldata data) external override(IImplementation, Implementation) onlyProxy { _setup(data); } /** * @notice Internal function to set up the contract with initial data * @param data Initialization data for the contract * @dev This function should be implemented in derived contracts. */ function _setup(bytes calldata data) internal virtual; } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IInterchainAddressTracker.sol@v6.0.4 /** * @title IInterchainAddressTracker * @dev Manages trusted addresses by chain, keeps track of addresses supported by the Axelar gateway contract */ interface IInterchainAddressTracker { error ZeroAddress(); error LengthMismatch(); error ZeroStringLength(); error UntrustedChain(); event TrustedAddressSet(string chain, string address_); event TrustedAddressRemoved(string chain); /** * @dev Gets the name of the chain this is deployed at */ function chainName() external view returns (string memory); /** * @dev Gets the trusted address at a remote chain * @param chain Chain name of the remote chain * @return trustedAddress_ The trusted address for the chain. Returns '' if the chain is untrusted */ function trustedAddress(string memory chain) external view returns (string memory trustedAddress_); /** * @dev Gets the trusted address hash for a chain * @param chain Chain name * @return trustedAddressHash_ the hash of the trusted address for that chain */ function trustedAddressHash(string memory chain) external view returns (bytes32 trustedAddressHash_); /** * @dev Checks whether the interchain sender is a trusted address * @param chain Chain name of the sender * @param address_ Address of the sender * @return bool true if the sender chain/address are trusted, false otherwise */ function isTrustedAddress(string calldata chain, string calldata address_) external view returns (bool); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/libs/StringStorage.sol@v6.0.4 library StringStorage { struct Wrapper { string value; } function set(bytes32 slot, string memory value) internal { _getStorageStruct(slot).value = value; } function get(bytes32 slot) internal view returns (string memory value) { value = _getStorageStruct(slot).value; } function clear(bytes32 slot) internal { delete _getStorageStruct(slot).value; } function _getStorageStruct(bytes32 slot) internal pure returns (Wrapper storage wrapper) { assembly { wrapper.slot := slot } } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/utils/InterchainAddressTracker.sol@v6.0.4 /** * @title InterchainAddressTracker * @dev Manages and validates trusted interchain addresses of an application. */ contract InterchainAddressTracker is IInterchainAddressTracker { bytes32 internal constant PREFIX_ADDRESS_MAPPING = keccak256('interchain-address-tracker-address-mapping'); bytes32 internal constant PREFIX_ADDRESS_HASH_MAPPING = keccak256('interchain-address-tracker-address-hash-mapping'); // bytes32(uint256(keccak256('interchain-address-tracker-chain-name')) - 1) bytes32 internal constant _CHAIN_NAME_SLOT = 0x0e2c162a1f4b5cff9fdbd6b34678a9bcb9898a0b9fbca695b112d61688d8b2ac; function _setChainName(string memory chainName_) internal { StringStorage.set(_CHAIN_NAME_SLOT, chainName_); } /** * @dev Gets the name of the chain this is deployed at */ function chainName() public view returns (string memory chainName_) { chainName_ = StringStorage.get(_CHAIN_NAME_SLOT); } /** * @dev Gets the trusted address at a remote chain * @param chain Chain name of the remote chain * @return trustedAddress_ The trusted address for the chain. Returns '' if the chain is untrusted */ function trustedAddress(string memory chain) public view returns (string memory trustedAddress_) { trustedAddress_ = StringStorage.get(_getTrustedAddressSlot(chain)); } /** * @dev Gets the trusted address hash for a chain * @param chain Chain name * @return trustedAddressHash_ the hash of the trusted address for that chain */ function trustedAddressHash(string memory chain) public view returns (bytes32 trustedAddressHash_) { bytes32 slot = _getTrustedAddressHashSlot(chain); assembly { trustedAddressHash_ := sload(slot) } } /** * @dev Checks whether the interchain sender is a trusted address * @param chain Chain name of the sender * @param address_ Address of the sender * @return bool true if the sender chain/address are trusted, false otherwise */ function isTrustedAddress(string calldata chain, string calldata address_) public view returns (bool) { bytes32 addressHash = keccak256(bytes(address_)); return addressHash == trustedAddressHash(chain); } /** * @dev Gets the key for the trusted address at a remote chain * @param chain Chain name of the remote chain * @return slot the slot to store the trusted address in */ function _getTrustedAddressSlot(string memory chain) internal pure returns (bytes32 slot) { slot = keccak256(abi.encode(PREFIX_ADDRESS_MAPPING, chain)); } /** * @dev Gets the key for the trusted address at a remote chain * @param chain Chain name of the remote chain * @return slot the slot to store the trusted address hash in */ function _getTrustedAddressHashSlot(string memory chain) internal pure returns (bytes32 slot) { slot = keccak256(abi.encode(PREFIX_ADDRESS_HASH_MAPPING, chain)); } /** * @dev Sets the trusted address and its hash for a remote chain * @param chain Chain name of the remote chain * @param address_ the string representation of the trusted address */ function _setTrustedAddress(string memory chain, string memory address_) internal { if (bytes(chain).length == 0) revert ZeroStringLength(); if (bytes(address_).length == 0) revert ZeroStringLength(); StringStorage.set(_getTrustedAddressSlot(chain), address_); bytes32 slot = _getTrustedAddressHashSlot(chain); bytes32 addressHash = keccak256(bytes(address_)); assembly { sstore(slot, addressHash) } emit TrustedAddressSet(chain, address_); } /** * @dev Remove the trusted address of the chain. * @param chain Chain name that should be made untrusted */ function _removeTrustedAddress(string memory chain) internal { if (bytes(chain).length == 0) revert ZeroStringLength(); StringStorage.clear(_getTrustedAddressSlot(chain)); bytes32 slot = _getTrustedAddressHashSlot(chain); assembly { sstore(slot, 0) } emit TrustedAddressRemoved(chain); } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IMulticall.sol@v6.0.4 /** * @title IMulticall * @notice This contract is a multi-functional smart contract which allows for multiple * contract calls in a single transaction. */ interface IMulticall { error MulticallFailed(); /** * @notice Performs multiple delegate calls and returns the results of all calls as an array * @dev This function requires that the contract has sufficient balance for the delegate calls. * If any of the calls fail, the function will revert with the failure message. * @param data An array of encoded function calls * @return results An bytes array with the return data of each function call */ function multicall(bytes[] calldata data) external payable returns (bytes[] memory results); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Multicall.sol@v6.0.4 /** * @title Multicall * @notice This contract is a multi-functional smart contract which allows for multiple * contract calls in a single transaction. */ contract Multicall is IMulticall { /** * @notice Performs multiple delegate calls and returns the results of all calls as an array * @dev This function requires that the contract has sufficient balance for the delegate calls. * If any of the calls fail, the function will revert with the failure message. * @param data An array of encoded function calls * @return results An bytes array with the return data of each function call */ function multicall(bytes[] calldata data) public payable returns (bytes[] memory results) { results = new bytes[](data.length); bool success; bytes memory result; for (uint256 i = 0; i < data.length; ++i) { // slither-disable-next-line calls-loop,delegatecall-loop (success, result) = address(this).delegatecall(data[i]); if (!success) { if (result.length == 0) revert MulticallFailed(); assembly { revert(add(32, result), mload(result)) } } results[i] = result; } } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IPausable.sol@v6.0.4 /** * @title Pausable * @notice This contract provides a mechanism to halt the execution of specific functions * if a pause condition is activated. */ interface IPausable { event Paused(address indexed account); event Unpaused(address indexed account); error Pause(); error NotPaused(); /** * @notice Check if the contract is paused * @return paused A boolean representing the pause status. True if paused, false otherwise. */ function paused() external view returns (bool); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Pausable.sol@v6.0.4 /** * @title Pausable * @notice This contract provides a mechanism to halt the execution of specific functions * if a pause condition is activated. */ contract Pausable is IPausable { // uint256(keccak256('paused')) - 1 uint256 internal constant PAUSE_SLOT = 0xee35723ac350a69d2a92d3703f17439cbaadf2f093a21ba5bf5f1a53eb2a14d8; /** * @notice A modifier that throws a Paused custom error if the contract is paused * @dev This modifier should be used with functions that can be paused */ modifier whenNotPaused() { if (paused()) revert Pause(); _; } modifier whenPaused() { if (!paused()) revert NotPaused(); _; } /** * @notice Check if the contract is paused * @return paused_ A boolean representing the pause status. True if paused, false otherwise. */ function paused() public view returns (bool paused_) { assembly { paused_ := sload(PAUSE_SLOT) } } /** * @notice Pauses the contract * @dev This function should be callable by the owner/governance. */ function _pause() internal { _setPaused(true); emit Paused(msg.sender); } /** * @notice Unpauses the contract * @dev This function should be callable by the owner/governance. */ function _unpause() internal { _setPaused(false); emit Unpaused(msg.sender); } /** * @notice Sets the pause status of the contract * @dev This is an internal function, meaning it can only be called from within the contract itself * or from derived contracts. * @param paused_ The new pause status */ function _setPaused(bool paused_) internal { assembly { sstore(PAUSE_SLOT, paused_) } } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.sol@v6.0.4 /** * @title IRolesBase Interface * @notice IRolesBase is an interface that abstracts the implementation of a * contract with role control internal functions. */ interface IRolesBase { error MissingRole(address account, uint8 role); error MissingAllRoles(address account, uint256 accountRoles); error MissingAnyOfRoles(address account, uint256 accountRoles); error InvalidProposedRoles(address fromAccount, address toAccount, uint256 accountRoles); event RolesProposed(address indexed fromAccount, address indexed toAccount, uint256 accountRoles); event RolesAdded(address indexed account, uint256 accountRoles); event RolesRemoved(address indexed account, uint256 accountRoles); /** * @notice Checks if an account has a role. * @param account The address to check * @param role The role to check * @return True if the account has the role, false otherwise */ function hasRole(address account, uint8 role) external view returns (bool); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/utils/RolesBase.sol@v6.0.4 /** * @title RolesBase * @notice A contract module which provides a set if internal functions * for implementing role control features. */ contract RolesBase is IRolesBase { bytes32 internal constant ROLES_PREFIX = keccak256('roles'); bytes32 internal constant PROPOSE_ROLES_PREFIX = keccak256('propose-roles'); /** * @notice Modifier that throws an error if called by any account missing the role. */ modifier onlyRole(uint8 role) { if (!_hasRole(_getRoles(msg.sender), role)) revert MissingRole(msg.sender, role); _; } /** * @notice Modifier that throws an error if called by an account without all the roles. */ modifier withEveryRole(uint8[] memory roles) { uint256 accountRoles = _toAccountRoles(roles); if (!_hasAllTheRoles(_getRoles(msg.sender), accountRoles)) revert MissingAllRoles(msg.sender, accountRoles); _; } /** * @notice Modifier that throws an error if called by an account without any of the roles. */ modifier withAnyRole(uint8[] memory roles) { uint256 accountRoles = _toAccountRoles(roles); if (!_hasAnyOfRoles(_getRoles(msg.sender), accountRoles)) revert MissingAnyOfRoles(msg.sender, accountRoles); _; } /** * @notice Checks if an account has a role. * @param account The address to check * @param role The role to check * @return True if the account has the role, false otherwise */ function hasRole(address account, uint8 role) public view returns (bool) { return _hasRole(_getRoles(account), role); } /** * @notice Internal function to convert an array of roles to a uint256. * @param roles The roles to convert * @return accountRoles The roles in uint256 format */ function _toAccountRoles(uint8[] memory roles) internal pure returns (uint256) { uint256 length = roles.length; uint256 accountRoles; for (uint256 i = 0; i < length; ++i) { accountRoles |= (1 << roles[i]); } return accountRoles; } /** * @notice Internal function to get the key of the roles mapping. * @param account The address to get the key for * @return key The key of the roles mapping */ function _rolesKey(address account) internal view virtual returns (bytes32 key) { return keccak256(abi.encodePacked(ROLES_PREFIX, account)); } /** * @notice Internal function to get the roles of an account. * @param account The address to get the roles for * @return accountRoles The roles of the account in uint256 format */ function _getRoles(address account) internal view returns (uint256 accountRoles) { bytes32 key = _rolesKey(account); assembly { accountRoles := sload(key) } } /** * @notice Internal function to set the roles of an account. * @param account The address to set the roles for * @param accountRoles The roles to set */ function _setRoles(address account, uint256 accountRoles) private { bytes32 key = _rolesKey(account); assembly { sstore(key, accountRoles) } } /** * @notice Internal function to get the key of the proposed roles mapping. * @param fromAccount The address of the current role * @param toAccount The address of the pending role * @return key The key of the proposed roles mapping */ function _proposalKey(address fromAccount, address toAccount) internal view virtual returns (bytes32 key) { return keccak256(abi.encodePacked(PROPOSE_ROLES_PREFIX, fromAccount, toAccount)); } /** * @notice Internal function to get the proposed roles of an account. * @param fromAccount The address of the current role * @param toAccount The address of the pending role * @return proposedRoles_ The proposed roles of the account in uint256 format */ function _getProposedRoles(address fromAccount, address toAccount) internal view returns (uint256 proposedRoles_) { bytes32 key = _proposalKey(fromAccount, toAccount); assembly { proposedRoles_ := sload(key) } } /** * @notice Internal function to set the proposed roles of an account. * @param fromAccount The address of the current role * @param toAccount The address of the pending role * @param proposedRoles_ The proposed roles to set in uint256 format */ function _setProposedRoles(address fromAccount, address toAccount, uint256 proposedRoles_) private { bytes32 key = _proposalKey(fromAccount, toAccount); assembly { sstore(key, proposedRoles_) } } /** * @notice Internal function to add a role to an account. * @dev emits a RolesAdded event. * @param account The address to add the role to * @param role The role to add */ function _addRole(address account, uint8 role) internal { _addAccountRoles(account, 1 << role); } /** * @notice Internal function to add roles to an account. * @dev emits a RolesAdded event. * @dev Called in the constructor to set the initial roles. * @param account The address to add roles to * @param roles The roles to add */ function _addRoles(address account, uint8[] memory roles) internal { _addAccountRoles(account, _toAccountRoles(roles)); } /** * @notice Internal function to add roles to an account. * @dev emits a RolesAdded event. * @dev Called in the constructor to set the initial roles. * @param account The address to add roles to * @param accountRoles The roles to add */ function _addAccountRoles(address account, uint256 accountRoles) internal { uint256 newAccountRoles = _getRoles(account) | accountRoles; _setRoles(account, newAccountRoles); emit RolesAdded(account, accountRoles); } /** * @notice Internal function to remove a role from an account. * @dev emits a RolesRemoved event. * @param account The address to remove the role from * @param role The role to remove */ function _removeRole(address account, uint8 role) internal { _removeAccountRoles(account, 1 << role); } /** * @notice Internal function to remove roles from an account. * @dev emits a RolesRemoved event. * @param account The address to remove roles from * @param roles The roles to remove */ function _removeRoles(address account, uint8[] memory roles) internal { _removeAccountRoles(account, _toAccountRoles(roles)); } /** * @notice Internal function to remove roles from an account. * @dev emits a RolesRemoved event. * @param account The address to remove roles from * @param accountRoles The roles to remove */ function _removeAccountRoles(address account, uint256 accountRoles) internal { uint256 newAccountRoles = _getRoles(account) & ~accountRoles; _setRoles(account, newAccountRoles); emit RolesRemoved(account, accountRoles); } /** * @notice Internal function to check if an account has a role. * @param accountRoles The roles of the account in uint256 format * @param role The role to check * @return True if the account has the role, false otherwise */ function _hasRole(uint256 accountRoles, uint8 role) internal pure returns (bool) { return accountRoles & (1 << role) != 0; } /** * @notice Internal function to check if an account has all the roles. * @param hasAccountRoles The roles of the account in uint256 format * @param mustHaveAccountRoles The roles the account must have * @return True if the account has all the roles, false otherwise */ function _hasAllTheRoles(uint256 hasAccountRoles, uint256 mustHaveAccountRoles) internal pure returns (bool) { return (hasAccountRoles & mustHaveAccountRoles) == mustHaveAccountRoles; } /** * @notice Internal function to check if an account has any of the roles. * @param hasAccountRoles The roles of the account in uint256 format * @param mustHaveAnyAccountRoles The roles to check in uint256 format * @return True if the account has any of the roles, false otherwise */ function _hasAnyOfRoles(uint256 hasAccountRoles, uint256 mustHaveAnyAccountRoles) internal pure returns (bool) { return (hasAccountRoles & mustHaveAnyAccountRoles) != 0; } /** * @notice Internal function to propose to transfer roles of message sender to a new account. * @dev Original account must have all the proposed roles. * @dev Emits a RolesProposed event. * @dev Roles are not transferred until the new role accepts the role transfer. * @param fromAccount The address of the current roles * @param toAccount The address to transfer roles to * @param role The role to transfer */ function _proposeRole(address fromAccount, address toAccount, uint8 role) internal { _proposeAccountRoles(fromAccount, toAccount, 1 << role); } /** * @notice Internal function to propose to transfer roles of message sender to a new account. * @dev Original account must have all the proposed roles. * @dev Emits a RolesProposed event. * @dev Roles are not transferred until the new role accepts the role transfer. * @param fromAccount The address of the current roles * @param toAccount The address to transfer roles to * @param roles The roles to transfer */ function _proposeRoles(address fromAccount, address toAccount, uint8[] memory roles) internal { _proposeAccountRoles(fromAccount, toAccount, _toAccountRoles(roles)); } /** * @notice Internal function to propose to transfer roles of message sender to a new account. * @dev Original account must have all the proposed roles. * @dev Emits a RolesProposed event. * @dev Roles are not transferred until the new role accepts the role transfer. * @param fromAccount The address of the current roles * @param toAccount The address to transfer roles to * @param accountRoles The account roles to transfer */ function _proposeAccountRoles(address fromAccount, address toAccount, uint256 accountRoles) internal { if (!_hasAllTheRoles(_getRoles(fromAccount), accountRoles)) revert MissingAllRoles(fromAccount, accountRoles); _setProposedRoles(fromAccount, toAccount, accountRoles); emit RolesProposed(fromAccount, toAccount, accountRoles); } /** * @notice Internal function to accept roles transferred from another account. * @dev Pending account needs to pass all the proposed roles. * @dev Emits RolesRemoved and RolesAdded events. * @param fromAccount The address of the current role * @param role The role to accept */ function _acceptRole(address fromAccount, address toAccount, uint8 role) internal virtual { _acceptAccountRoles(fromAccount, toAccount, 1 << role); } /** * @notice Internal function to accept roles transferred from another account. * @dev Pending account needs to pass all the proposed roles. * @dev Emits RolesRemoved and RolesAdded events. * @param fromAccount The address of the current role * @param roles The roles to accept */ function _acceptRoles(address fromAccount, address toAccount, uint8[] memory roles) internal virtual { _acceptAccountRoles(fromAccount, toAccount, _toAccountRoles(roles)); } /** * @notice Internal function to accept roles transferred from another account. * @dev Pending account needs to pass all the proposed roles. * @dev Emits RolesRemoved and RolesAdded events. * @param fromAccount The address of the current role * @param accountRoles The account roles to accept */ function _acceptAccountRoles(address fromAccount, address toAccount, uint256 accountRoles) internal virtual { if (_getProposedRoles(fromAccount, toAccount) != accountRoles) { revert InvalidProposedRoles(fromAccount, toAccount, accountRoles); } _setProposedRoles(fromAccount, toAccount, 0); _transferAccountRoles(fromAccount, toAccount, accountRoles); } /** * @notice Internal function to transfer roles from one account to another. * @dev Original account must have all the proposed roles. * @param fromAccount The address of the current role * @param toAccount The address to transfer role to * @param role The role to transfer */ function _transferRole(address fromAccount, address toAccount, uint8 role) internal { _transferAccountRoles(fromAccount, toAccount, 1 << role); } /** * @notice Internal function to transfer roles from one account to another. * @dev Original account must have all the proposed roles. * @param fromAccount The address of the current role * @param toAccount The address to transfer role to * @param roles The roles to transfer */ function _transferRoles(address fromAccount, address toAccount, uint8[] memory roles) internal { _transferAccountRoles(fromAccount, toAccount, _toAccountRoles(roles)); } /** * @notice Internal function to transfer roles from one account to another. * @dev Original account must have all the proposed roles. * @param fromAccount The address of the current role * @param toAccount The address to transfer role to * @param accountRoles The account roles to transfer */ function _transferAccountRoles(address fromAccount, address toAccount, uint256 accountRoles) internal { if (!_hasAllTheRoles(_getRoles(fromAccount), accountRoles)) revert MissingAllRoles(fromAccount, accountRoles); _removeAccountRoles(fromAccount, accountRoles); _addAccountRoles(toAccount, accountRoles); } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressExecutorTracker.sol@v6.0.4 abstract contract ExpressExecutorTracker { error ExpressExecutorAlreadySet(); bytes32 internal constant PREFIX_EXPRESS_EXECUTE = keccak256('express-execute'); bytes32 internal constant PREFIX_EXPRESS_EXECUTE_WITH_TOKEN = keccak256('express-execute-with-token'); function _expressExecuteSlot( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash ) internal pure returns (bytes32 slot) { slot = keccak256(abi.encode(PREFIX_EXPRESS_EXECUTE, commandId, sourceChain, sourceAddress, payloadHash)); } function _expressExecuteWithTokenSlot( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash, string calldata symbol, uint256 amount ) internal pure returns (bytes32 slot) { slot = keccak256(abi.encode(PREFIX_EXPRESS_EXECUTE_WITH_TOKEN, commandId, sourceChain, sourceAddress, payloadHash, symbol, amount)); } function _getExpressExecutor( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash ) internal view returns (address expressExecutor) { bytes32 slot = _expressExecuteSlot(commandId, sourceChain, sourceAddress, payloadHash); assembly { expressExecutor := sload(slot) } } function _getExpressExecutorWithToken( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash, string calldata symbol, uint256 amount ) internal view returns (address expressExecutor) { bytes32 slot = _expressExecuteWithTokenSlot(commandId, sourceChain, sourceAddress, payloadHash, symbol, amount); assembly { expressExecutor := sload(slot) } } function _setExpressExecutor( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash, address expressExecutor ) internal { bytes32 slot = _expressExecuteSlot(commandId, sourceChain, sourceAddress, payloadHash); address currentExecutor; assembly { currentExecutor := sload(slot) } if (currentExecutor != address(0)) revert ExpressExecutorAlreadySet(); assembly { sstore(slot, expressExecutor) } } function _setExpressExecutorWithToken( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash, string calldata symbol, uint256 amount, address expressExecutor ) internal { bytes32 slot = _expressExecuteWithTokenSlot(commandId, sourceChain, sourceAddress, payloadHash, symbol, amount); address currentExecutor; assembly { currentExecutor := sload(slot) } if (currentExecutor != address(0)) revert ExpressExecutorAlreadySet(); assembly { sstore(slot, expressExecutor) } } function _popExpressExecutor( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash ) internal returns (address expressExecutor) { bytes32 slot = _expressExecuteSlot(commandId, sourceChain, sourceAddress, payloadHash); assembly { expressExecutor := sload(slot) if expressExecutor { sstore(slot, 0) } } } function _popExpressExecutorWithToken( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash, string calldata symbol, uint256 amount ) internal returns (address expressExecutor) { bytes32 slot = _expressExecuteWithTokenSlot(commandId, sourceChain, sourceAddress, payloadHash, symbol, amount); assembly { expressExecutor := sload(slot) if expressExecutor { sstore(slot, 0) } } } } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol@v6.0.4 /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { error InvalidAccount(); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `recipient`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address recipient, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `sender` to `recipient` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); } // File @axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressBytes.sol@v6.0.4 /** * @title AddressBytesUtils * @dev This library provides utility functions to convert between `address` and `bytes`. */ library AddressBytes { error InvalidBytesLength(bytes bytesAddress); /** * @dev Converts a bytes address to an address type. * @param bytesAddress The bytes representation of an address * @return addr The converted address */ function toAddress(bytes memory bytesAddress) internal pure returns (address addr) { if (bytesAddress.length != 20) revert InvalidBytesLength(bytesAddress); assembly { addr := mload(add(bytesAddress, 20)) } } /** * @dev Converts an address to bytes. * @param addr The address to be converted * @return bytesAddress The bytes representation of the address */ function toBytes(address addr) internal pure returns (bytes memory bytesAddress) { bytesAddress = new bytes(20); // we can test if using a single 32 byte variable that is the address with the length together and using one mstore would be slightly cheaper. assembly { mstore(add(bytesAddress, 20), addr) mstore(bytesAddress, 20) } } } // File contracts/interfaces/IERC20Named.sol /** * @title IERC20Named Interface * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Named is IERC20 { /** * @notice Getter for the name of the token. * @return string Name of the token. */ function name() external view returns (string memory); /** * @notice Getter for the symbol of the token. * @return string The symbol of the token. */ function symbol() external view returns (string memory); /** * @notice Getter for the decimals of the token. * @return uint8 The decimals of the token. */ function decimals() external view returns (uint8); } // File contracts/interfaces/IGatewayCaller.sol /** * @title IGatewayCaller interface * @dev Interface for the GatewayCaller contract */ interface IGatewayCaller { /** * @dev Enum representing different metadata versions */ enum MetadataVersion { CONTRACT_CALL, EXPRESS_CALL } /** * @dev Error thrown when an invalid metadata version is provided */ error InvalidMetadataVersion(uint32 metadataVersion); /** * @notice Call the Axelar gateway to send a payload to a destination contract on a specific destination chain * @param destinationChain The target chain where the contract will be called * @param destinationAddress The address of the contract to be called on the destination chain * @param payload The data payload for the transaction * @param metadataVersion The version of metadata to be used * @param gasValue The amount of gas to be paid for the cross-chain message. If this is 0, then gas payment is skipped. `msg.value` must be at least gasValue. */ function callContract( string calldata destinationChain, string calldata destinationAddress, bytes calldata payload, MetadataVersion metadataVersion, uint256 gasValue ) external payable; } // File contracts/interfaces/IInterchainTokenDeployer.sol /** * @title IInterchainTokenDeployer * @notice This interface is used to deploy new instances of the InterchainTokenProxy contract. */ interface IInterchainTokenDeployer { error AddressZero(); error TokenDeploymentFailed(); /** * @notice Returns the interchain token implementation address. * @return address The interchain token implementation address. */ function implementationAddress() external view returns (address); /** * @notice Returns the interchain token deployment address. * @param salt The deployment salt. * @return tokenAddress The token address. */ function deployedAddress(bytes32 salt) external view returns (address tokenAddress); /** * @notice Deploys a new instance of the InterchainTokenProxy contract. * @param salt The salt used by Create3Deployer. * @param tokenId tokenId of the token. * @param minter Address of the minter. * @param name Name of the token. * @param symbol Symbol of the token. * @param decimals Decimals of the token. * @return tokenAddress Address of the deployed token. */ function deployInterchainToken( bytes32 salt, bytes32 tokenId, address minter, string calldata name, string calldata symbol, uint8 decimals ) external returns (address tokenAddress); } // File contracts/interfaces/IInterchainTokenExecutable.sol /** * @title IInterchainTokenExecutable * @notice Contracts should implement this interface to accept calls from the InterchainTokenService. */ interface IInterchainTokenExecutable { /** * @notice This will be called after the tokens are sent to this contract. * @dev Execution should revert unless the msg.sender is the InterchainTokenService * @param commandId The unique message id for the call. * @param sourceChain The name of the source chain. * @param sourceAddress The address that sent the contract call. * @param data The data to be processed. * @param tokenId The tokenId of the token manager managing the token. * @param token The address of the token. * @param amount The amount of tokens that were sent. * @return bytes32 Hash indicating success of the execution. */ function executeWithInterchainToken( bytes32 commandId, string calldata sourceChain, bytes calldata sourceAddress, bytes calldata data, bytes32 tokenId, address token, uint256 amount ) external returns (bytes32); } // File contracts/interfaces/IInterchainTokenExpressExecutable.sol /** * @title IInterchainTokenExpressExecutable * @notice Contracts should implement this interface to accept express calls from the InterchainTokenService. */ interface IInterchainTokenExpressExecutable is IInterchainTokenExecutable { /** * @notice Executes express logic in the context of an interchain token transfer. * @dev Only callable by the interchain token service. * @param commandId The unique message id for the call. * @param sourceChain The source chain of the token transfer. * @param sourceAddress The source address of the token transfer. * @param data The data associated with the token transfer. * @param tokenId The token ID. * @param token The token address. * @param amount The amount of tokens to be transferred. * @return bytes32 Hash indicating success of the express execution. */ function expressExecuteWithInterchainToken( bytes32 commandId, string calldata sourceChain, bytes calldata sourceAddress, bytes calldata data, bytes32 tokenId, address token, uint256 amount ) external returns (bytes32); } // File contracts/interfaces/IAddressTracker.sol /** * @title IAddressTracker Interface * @notice This interface allows setting and removing a trusted address for a specific chain. * @dev Extends the IInterchainAddressTracker interface. */ interface IAddressTracker is IInterchainAddressTracker { /** * @notice Sets the trusted address for the specified chain. * @param chain Chain name to be trusted. * @param address_ Trusted address to be added for the chain. */ function setTrustedAddress(string memory chain, string memory address_) external; /** * @notice Remove the trusted address of the chain. * @param chain Chain name to remove the trusted address for. */ function removeTrustedAddress(string calldata chain) external; } // File contracts/interfaces/IOperator.sol /** * @title IOperator Interface * @notice An interface for a contract module which provides a basic access control mechanism, where * there is an account (a operator) that can be granted exclusive access to specific functions. */ interface IOperator is IRolesBase { /** * @notice Change the operator of the contract. * @dev Can only be called by the current operator. * @param operator_ The address of the new operator. */ function transferOperatorship(address operator_) external; /** * @notice Proposed a change of the operator of the contract. * @dev Can only be called by the current operator. * @param operator_ The address of the new operator. */ function proposeOperatorship(address operator_) external; /** * @notice Accept a proposed change of operatorship. * @dev Can only be called by the proposed operator. * @param fromOperator The previous operator of the contract. */ function acceptOperatorship(address fromOperator) external; /** * @notice Query if an address is a operator. * @param addr The address to query for. * @return bool Boolean value representing whether or not the address is an operator. */ function isOperator(address addr) external view returns (bool); } // File contracts/interfaces/IBaseTokenManager.sol /** * @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 token address from the init params. */ function getTokenAddressFromParams(bytes calldata params) external pure returns (address); } // File contracts/interfaces/IFlowLimit.sol /** * @title FlowLimit Interface * @notice Interface for flow limit logic for interchain token transfers. */ interface IFlowLimit { error FlowLimitExceeded(uint256 limit, uint256 flowAmount, address tokenManager); error FlowAdditionOverflow(uint256 flowAmount, uint256 flowToAdd, address tokenManager); error FlowLimitOverflow(uint256 flowLimit, uint256 flowToCompare, address tokenManager); event FlowLimitSet(bytes32 indexed tokenId, address operator, uint256 flowLimit_); /** * @notice Returns the current flow limit. * @return flowLimit_ The current flow limit value. */ function flowLimit() external view returns (uint256 flowLimit_); /** * @notice Returns the current flow out amount. * @return flowOutAmount_ The current flow out amount. */ function flowOutAmount() external view returns (uint256 flowOutAmount_); /** * @notice Returns the current flow in amount. * @return flowInAmount_ The current flow in amount. */ function flowInAmount() external view returns (uint256 flowInAmount_); } // File contracts/interfaces/ITokenManager.sol /** * @title ITokenManager Interface * @notice This contract is responsible for managing tokens, such as setting locking token balances, or setting flow limits, for interchain transfers. */ interface ITokenManager is IBaseTokenManager, IOperator, IFlowLimit, IImplementation { error TokenLinkerZeroAddress(); error NotService(address caller); error TakeTokenFailed(); error GiveTokenFailed(); error NotToken(address caller); error ZeroAddress(); error AlreadyFlowLimiter(address flowLimiter); error NotFlowLimiter(address flowLimiter); error NotSupported(); /** * @notice Returns implementation type of this token manager. * @return uint256 The implementation type of this token manager. */ function implementationType() external view returns (uint256); function addFlowIn(uint256 amount) external; function addFlowOut(uint256 amount) external; /** * @notice This function adds a flow limiter for this TokenManager. * @dev Can only be called by the operator. * @param flowLimiter the address of the new flow limiter. */ function addFlowLimiter(address flowLimiter) external; /** * @notice This function removes a flow limiter for this TokenManager. * @dev Can only be called by the operator. * @param flowLimiter the address of an existing flow limiter. */ function removeFlowLimiter(address flowLimiter) external; /** * @notice Query if an address is a flow limiter. * @param addr The address to query for. * @return bool Boolean value representing whether or not the address is a flow limiter. */ function isFlowLimiter(address addr) external view returns (bool); /** * @notice This function sets the flow limit for this TokenManager. * @dev Can only be called by the flow limiters. * @param flowLimit_ The maximum difference between the tokens flowing in and/or out at any given interval of time (6h). */ function setFlowLimit(uint256 flowLimit_) external; /** * @notice A function to renew approval to the service if we need to. */ function approveService() external; /** * @notice Getter function for the parameters of a lock/unlock TokenManager. * @dev This function will be mainly used by frontends. * @param operator_ The operator of the TokenManager. * @param tokenAddress_ The token to be managed. * @return params_ The resulting params to be passed to custom TokenManager deployments. */ function params(bytes calldata operator_, address tokenAddress_) external pure returns (bytes memory params_); /** * @notice External function to allow the service to mint tokens through the tokenManager * @dev This function should revert if called by anyone but the service. * @param tokenAddress_ The address of the token, since its cheaper to pass it in instead of reading it as the token manager. * @param to The recipient. * @param amount The amount to mint. */ function mintToken(address tokenAddress_, address to, uint256 amount) external; /** * @notice External function to allow the service to burn tokens through the tokenManager * @dev This function should revert if called by anyone but the service. * @param tokenAddress_ The address of the token, since its cheaper to pass it in instead of reading it as the token manager. * @param from The address to burn the token from. * @param amount The amount to burn. */ function burnToken(address tokenAddress_, address from, uint256 amount) external; } // File contracts/interfaces/ITokenManagerImplementation.sol /** * @title ITokenManagerImplementation Interface * @notice Interface for returning the token manager implementation type. */ interface ITokenManagerImplementation { /** * @notice Returns the implementation address for a given token manager type. * @param tokenManagerType The type of token manager. * @return tokenManagerAddress_ The address of the token manager implementation. */ function tokenManagerImplementation(uint256 tokenManagerType) external view returns (address tokenManagerAddress_); } // File contracts/interfaces/ITokenManagerType.sol /** * @title ITokenManagerType Interface * @notice A simple interface that defines all the token manager types. */ interface ITokenManagerType { enum TokenManagerType { NATIVE_INTERCHAIN_TOKEN, // This type is reserved for interchain tokens deployed by ITS, and can't be used by custom token managers. MINT_BURN_FROM, // The token will be minted/burned on transfers. The token needs to give mint permission to the token manager, but burning happens via an approval. LOCK_UNLOCK, // The token will be locked/unlocked at the token manager. LOCK_UNLOCK_FEE, // The token will be locked/unlocked at the token manager, which will account for any fee-on-transfer behaviour. MINT_BURN // The token will be minted/burned on transfers. The token needs to give mint and burn permission to the token manager. } } // File contracts/interfaces/ITransmitInterchainToken.sol /** * @title ITransmitInterchainToken Interface * @notice Interface for transmiting interchain tokens via the interchain token service */ interface ITransmitInterchainToken { /** * @notice Transmit an interchain transfer for the given tokenId. * @dev Only callable by a token registered under a tokenId. * @param tokenId The tokenId of the token (which must be the msg.sender). * @param sourceAddress The address where the token is coming from. * @param destinationChain The name of the chain to send tokens to. * @param destinationAddress The destinationAddress for the interchainTransfer. * @param amount The amount of token to give. * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract). */ function transmitInterchainTransfer( bytes32 tokenId, address sourceAddress, string calldata destinationChain, bytes memory destinationAddress, uint256 amount, bytes calldata metadata ) external payable; } // File contracts/interfaces/IInterchainTokenService.sol /** * @title IInterchainTokenService Interface * @notice Interface for the Interchain Token Service */ interface IInterchainTokenService is ITransmitInterchainToken, ITokenManagerType, ITokenManagerImplementation, IAxelarValuedExpressExecutable, IOperator, IPausable, IMulticall, IAddressTracker, IUpgradable { error InvalidChainName(); error NotRemoteService(); error TokenManagerDoesNotExist(bytes32 tokenId); error ExecuteWithInterchainTokenFailed(address contractAddress); error ExpressExecuteWithInterchainTokenFailed(address contractAddress); error TokenManagerDeploymentFailed(bytes error); error InterchainTokenDeploymentFailed(bytes error); error InvalidMessageType(uint256 messageType); error InvalidMetadataVersion(uint32 version); error InvalidExpressMessageType(uint256 messageType); error TakeTokenFailed(bytes data); error GiveTokenFailed(bytes data); error TokenHandlerFailed(bytes data); error EmptyData(); error PostDeployFailed(bytes data); error ZeroAmount(); error CannotDeploy(TokenManagerType); error CannotDeployRemotelyToSelf(); error InvalidPayload(); error GatewayCallFailed(bytes data); error EmptyTokenName(); error EmptyTokenSymbol(); error EmptyParams(); error EmptyDestinationAddress(); error EmptyTokenAddress(); error NotSupported(); error NotInterchainTokenFactory(address sender); event InterchainTransfer( bytes32 indexed tokenId, address indexed sourceAddress, string destinationChain, bytes destinationAddress, uint256 amount, bytes32 indexed dataHash ); event InterchainTransferReceived( bytes32 indexed commandId, bytes32 indexed tokenId, string sourceChain, bytes sourceAddress, address indexed destinationAddress, uint256 amount, bytes32 dataHash ); event TokenMetadataRegistered(address indexed tokenAddress, uint8 decimals); event LinkTokenStarted( bytes32 indexed tokenId, string destinationChain, bytes sourceTokenAddress, bytes destinationTokenAddress, TokenManagerType indexed tokenManagerType, bytes params ); event InterchainTokenDeploymentStarted( bytes32 indexed tokenId, string tokenName, string tokenSymbol, uint8 tokenDecimals, bytes minter, string destinationChain ); event TokenManagerDeployed(bytes32 indexed tokenId, address tokenManager, TokenManagerType indexed tokenManagerType, bytes params); event InterchainTokenDeployed( bytes32 indexed tokenId, address tokenAddress, address indexed minter, string name, string symbol, uint8 decimals ); event InterchainTokenIdClaimed(bytes32 indexed tokenId, address indexed deployer, bytes32 indexed salt); /** * @notice Returns the address of the token manager deployer contract. * @return tokenManagerDeployerAddress The address of the token manager deployer contract. */ function tokenManagerDeployer() external view returns (address tokenManagerDeployerAddress); /** * @notice Returns the address of the interchain token deployer contract. * @return interchainTokenDeployerAddress The address of the interchain token deployer contract. */ function interchainTokenDeployer() external view returns (address interchainTokenDeployerAddress); /** * @notice Returns the address of TokenManager implementation. * @return tokenManagerAddress_ The address of the token manager contract. */ function tokenManager() external view returns (address tokenManagerAddress_); /** * @notice Returns the address of TokenHandler implementation. * @return tokenHandlerAddress The address of the token handler contract. */ function tokenHandler() external view returns (address tokenHandlerAddress); /** * @notice Returns the address of the interchain token factory. * @return address The address of the interchain token factory. */ function interchainTokenFactory() external view returns (address); /** * @notice Returns the hash of the chain name. * @return bytes32 The hash of the chain name. */ function chainNameHash() external view returns (bytes32); /** * @notice Returns the address of the token manager associated with the given tokenId. * @param tokenId The tokenId of the token manager. * @return tokenManagerAddress_ The address of the token manager. */ function tokenManagerAddress(bytes32 tokenId) external view returns (address tokenManagerAddress_); /** * @notice Returns the instance of ITokenManager from a specific tokenId. * @param tokenId The tokenId of the deployed token manager. * @return tokenManager_ The instance of ITokenManager associated with the specified tokenId. */ function deployedTokenManager(bytes32 tokenId) external view returns (ITokenManager tokenManager_); /** * @notice Returns the address of the token that an existing tokenManager points to. * @param tokenId The tokenId of the registered token. * @return tokenAddress The address of the token. */ function registeredTokenAddress(bytes32 tokenId) external view returns (address tokenAddress); /** * @notice Returns the address of the interchain token associated with the given tokenId. * @param tokenId The tokenId of the interchain token. * @return tokenAddress The address of the interchain token. */ function interchainTokenAddress(bytes32 tokenId) external view returns (address tokenAddress); /** * @notice Returns the custom tokenId associated with the given operator and salt. * @param operator_ The operator address. * @param salt The salt used for token id calculation. * @return tokenId The custom tokenId associated with the operator and salt. */ function interchainTokenId(address operator_, bytes32 salt) external view returns (bytes32 tokenId); /** * @notice Registers metadata for a token on the ITS Hub. This metadata is used for scaling linked tokens. * The token metadata must be registered before linkToken can be called for the corresponding token. * @param tokenAddress The address of the token. * @param gasValue The cross-chain gas value for sending the registration message to ITS Hub. */ function registerTokenMetadata(address tokenAddress, uint256 gasValue) external payable; /** * @notice Only to be used by the InterchainTokenFactory to register custom tokens to this chain. Then link token can be used to register those tokens to other chains. * @param salt A unique salt to derive tokenId from. * @param tokenManagerType The type of the token manager to use for the token registration. * @param linkParams The operator for the token. */ function registerCustomToken( bytes32 salt, address tokenAddress, TokenManagerType tokenManagerType, bytes calldata linkParams ) external payable returns (bytes32 tokenId); /** * @notice If `destinationChain` is an empty string, this function will register the token address on the current chain. * Otherwise, it will link the token address on the destination chain with the token corresponding to the tokenId on the current chain. * A token manager is deployed on EVM chains that's responsible for managing the linked token. * @dev This function replaces the prior `deployTokenManager` function. * @param salt A unique identifier to allow for multiple tokens registered per deployer. * @param destinationChain The chain to link the token to. Pass an empty string for this chain. * @param destinationTokenAddress The token address to link, as bytes. * @param tokenManagerType The type of the token manager to use to send and receive tokens. * @param linkParams Additional parameteres to use to link the token. Fow not it is just the address of the operator. * @param gasValue Pass a non-zero value only for remote linking, which should be the gas to use to pay for the contract call. * @return tokenId The tokenId associated with the token manager. */ function linkToken( bytes32 salt, string calldata destinationChain, bytes memory destinationTokenAddress, TokenManagerType tokenManagerType, bytes memory linkParams, uint256 gasValue ) external payable returns (bytes32 tokenId); /** * @notice Deploys and registers an interchain token on a remote chain. * @param salt The salt used for token deployment. * @param destinationChain The name of the destination chain. Use '' for this chain. * @param name The name of the interchain tokens. * @param symbol The symbol of the interchain tokens. * @param decimals The number of decimals for the interchain tokens. * @param minter The minter data for mint/burn operations. * @param gasValue The gas value for deployment. * @return tokenId The tokenId corresponding to the deployed InterchainToken. */ function deployInterchainToken( bytes32 salt, string calldata destinationChain, string memory name, string memory symbol, uint8 decimals, bytes memory minter, uint256 gasValue ) external payable returns (bytes32 tokenId); /** * @notice Initiates an interchain transfer of a specified token to a destination chain. * @param tokenId The unique identifier of the token to be transferred. * @param destinationChain The destination chain to send the tokens to. * @param destinationAddress The address on the destination chain to send the tokens to. * @param amount The amount of tokens to be transferred. * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract). */ function interchainTransfer( bytes32 tokenId, string calldata destinationChain, bytes calldata destinationAddress, uint256 amount, bytes calldata metadata, uint256 gasValue ) external payable; /** * @notice Sets the flow limits for multiple tokens. * @param tokenIds An array of tokenIds. * @param flowLimits An array of flow limits corresponding to the tokenIds. */ function setFlowLimits(bytes32[] calldata tokenIds, uint256[] calldata flowLimits) external; /** * @notice Allows the owner to pause/unpause the token service. * @param paused whether to pause or unpause. */ function setPauseStatus(bool paused) external; /** * @notice Allows the owner to migrate legacy tokens that cannot be migrated automatically. * @param tokenId the tokenId of the registered token. */ function migrateInterchainToken(bytes32 tokenId) external; } // File contracts/interfaces/IMinter.sol /** * @title IMinter Interface * @notice An interface for a contract module which provides a basic access control mechanism, where * there is an account (a minter) that can be granted exclusive access to specific functions. */ interface IMinter is IRolesBase { /** * @notice Change the minter of the contract. * @dev Can only be called by the current minter. * @param minter_ The address of the new minter. */ function transferMintership(address minter_) external; /** * @notice Proposed a change of the minter of the contract. * @dev Can only be called by the current minter. * @param minter_ The address of the new minter. */ function proposeMintership(address minter_) external; /** * @notice Accept a change of the minter of the contract. * @dev Can only be called by the proposed minter. * @param fromMinter The previous minter. */ function acceptMintership(address fromMinter) external; /** * @notice Query if an address is a minter * @param addr the address to query for * @return bool Boolean value representing whether or not the address is a minter. */ function isMinter(address addr) external view returns (bool); } // File contracts/interfaces/ITokenHandler.sol /** * @title ITokenHandler Interface * @notice This interface is responsible for handling tokens before initiating an interchain token transfer, or after receiving one. */ interface ITokenHandler { error UnsupportedTokenManagerType(uint256 tokenManagerType); error NotToken(address caller, address token); /** * @notice This function gives token to a specified address from the token manager. * @param tokenId The token id of the tokenManager. * @param to The address to give tokens to. * @param amount The amount of tokens to give. * @return uint256 The amount of token actually given, which could be different for certain token type. * @return address the address of the token. */ function giveToken(bytes32 tokenId, address to, uint256 amount) external returns (uint256, address); /** * @notice This function takes token from a specified address to the token manager. * @param tokenId The tokenId for 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. */ // slither-disable-next-line locked-ether function takeToken(bytes32 tokenId, bool tokenOnly, address from, uint256 amount) external payable returns (uint256); /** * @notice This function transfers token from and to a specified address. * @param tokenId The token id of the token manager. * @param from The address to transfer tokens from. * @param to The address to transfer tokens to. * @param amount The amount of token to transfer. * @return uint256 The amount of token actually transferred, which could be different for certain token type. * @return address The address of the token corresponding to the input tokenId. */ function transferTokenFrom(bytes32 tokenId, address from, address to, uint256 amount) external returns (uint256, address); /** * @notice This function prepares a token manager after it is deployed * @param tokenManagerType The token manager type. * @param tokenManager The address of the token manager. */ function postTokenManagerDeploy(uint256 tokenManagerType, ITokenManager tokenManager) external payable; } // File contracts/interfaces/ITokenManagerDeployer.sol /** * @title ITokenManagerDeployer Interface * @notice This interface is used to deploy new instances of the TokenManagerProxy contract. */ interface ITokenManagerDeployer { error TokenManagerDeploymentFailed(); /** * @notice Deploys a new instance of the TokenManagerProxy contract. * @param tokenId The token ID. * @param implementationType Token manager implementation type. * @param params Additional parameters used in the setup of the token manager. * @return tokenManager Address of the deployed tokenManager. */ function deployTokenManager( bytes32 tokenId, uint256 implementationType, bytes calldata params ) external payable returns (address tokenManager); } // File contracts/utils/Create3AddressFixed.sol /** * @title Create3AddressFixed contract * @notice This contract can be used to predict the deterministic deployment address of a contract deployed with the `CREATE3` technique. * It is equivalent to the Create3Address found in axelar-gmp-sdk-solidity repo but uses a fixed bytecode for CreateDeploy, * which allows changing compilation options (like number of runs) without affecting the future deployment addresses. */ contract Create3AddressFixed { // slither-disable-next-line too-many-digits bytes internal constant CREATE_DEPLOY_BYTECODE = hex'608060405234801561001057600080fd5b50610162806100206000396000f3fe60806040526004361061001d5760003560e01c806277436014610022575b600080fd5b61003561003036600461007b565b610037565b005b8051602082016000f061004957600080fd5b50565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561008d57600080fd5b813567ffffffffffffffff808211156100a557600080fd5b818401915084601f8301126100b957600080fd5b8135818111156100cb576100cb61004c565b604051601f8201601f19908116603f011681019083821181831017156100f3576100f361004c565b8160405282815287602084870101111561010c57600080fd5b82602086016020830137600092810160200192909252509594505050505056fea264697066735822122094780ce55d28f1d568f4e0ab1b9dc230b96e952b73d2e06456fbff2289fa27f464736f6c63430008150033'; bytes32 internal constant CREATE_DEPLOY_BYTECODE_HASH = keccak256(CREATE_DEPLOY_BYTECODE); /** * @notice Compute the deployed address that will result from the `CREATE3` method. * @param deploySalt A salt to influence the contract address * @return deployed The deterministic contract address if it was deployed */ function _create3Address(bytes32 deploySalt) internal view returns (address deployed) { address deployer = address( uint160(uint256(keccak256(abi.encodePacked(hex'ff', address(this), deploySalt, CREATE_DEPLOY_BYTECODE_HASH)))) ); deployed = address(uint160(uint256(keccak256(abi.encodePacked(hex'd6_94', deployer, hex'01'))))); } } // File contracts/utils/RolesConstants.sol /** * @title RolesConstants * @notice This contract contains enum values representing different contract roles. */ contract RolesConstants { enum Roles { MINTER, OPERATOR, FLOW_LIMITER } } // File contracts/utils/Operator.sol /** * @title Operator Contract * @notice A contract module which provides a basic access control mechanism, where * there is an account (a operator) that can be granted exclusive access to * specific functions. * @dev This module is used through inheritance. */ contract Operator is IOperator, RolesBase, RolesConstants { /** * @notice Internal function that stores the new operator address in the correct storage slot * @param operator The address of the new operator */ function _addOperator(address operator) internal { _addRole(operator, uint8(Roles.OPERATOR)); } /** * @notice Change the operator of the contract. * @dev Can only be called by the current operator. * @param operator The address of the new operator. */ function transferOperatorship(address operator) external onlyRole(uint8(Roles.OPERATOR)) { _transferRole(msg.sender, operator, uint8(Roles.OPERATOR)); } /** * @notice Propose a change of the operator of the contract. * @dev Can only be called by the current operator. * @param operator The address of the new operator. */ function proposeOperatorship(address operator) external onlyRole(uint8(Roles.OPERATOR)) { _proposeRole(msg.sender, operator, uint8(Roles.OPERATOR)); } /** * @notice Accept a proposed change of operatorship. * @dev Can only be called by the proposed operator. * @param fromOperator The previous operator of the contract. */ function acceptOperatorship(address fromOperator) external { _acceptRole(fromOperator, msg.sender, uint8(Roles.OPERATOR)); } /** * @notice Query if an address is a operator. * @param addr The address to query for. * @return bool Boolean value representing whether or not the address is an operator. */ function isOperator(address addr) external view returns (bool) { return hasRole(addr, uint8(Roles.OPERATOR)); } } // File contracts/InterchainTokenService.sol /** * @title The Interchain Token Service * @notice This contract is responsible for facilitating interchain token transfers. * It (mostly) does not handle tokens, but is responsible for the messaging that needs to occur for interchain transfers to happen. * @dev The only storage used in this contract is for Express calls. * Furthermore, no ether is intended to or should be sent to this contract except as part of deploy/interchainTransfer payable methods for gas payment. */ contract InterchainTokenService is Upgradable, Operator, Pausable, Multicall, Create3AddressFixed, ExpressExecutorTracker, InterchainAddressTracker, IInterchainTokenService { using AddressBytes for bytes; using AddressBytes for address; /** * @dev There are two types of Axelar Gateways for cross-chain messaging: * 1. Cross-chain messaging (GMP): The Axelar Gateway allows sending cross-chain messages. * This is compatible across both Amplifier and consensus chains. IAxelarGateway interface exposes this functionality. * 2. Cross-chain messaging with Gateway Token: The AxelarGateway on legacy consensus EVM connections supports this (via callContractWithToken) * but not Amplifier chains. The gateway is cast to IAxelarGatewayWithToken when gateway tokens need to be handled. * ITS deployments on Amplifier chains will revert when this functionality is used. */ IAxelarGateway public immutable gateway; IAxelarGasService public immutable gasService; address public immutable interchainTokenFactory; bytes32 public immutable chainNameHash; address public immutable interchainTokenDeployer; address public immutable tokenManagerDeployer; /** * @dev Token manager implementation addresses */ address public immutable tokenManager; address public immutable tokenHandler; address public immutable gatewayCaller; bytes32 internal constant PREFIX_INTERCHAIN_TOKEN_ID = keccak256('its-interchain-token-id'); bytes32 internal constant PREFIX_INTERCHAIN_TOKEN_SALT = keccak256('its-interchain-token-salt'); bytes32 private constant CONTRACT_ID = keccak256('interchain-token-service'); bytes32 private constant EXECUTE_SUCCESS = keccak256('its-execute-success'); bytes32 private constant EXPRESS_EXECUTE_SUCCESS = keccak256('its-express-execute-success'); /** * @dev The message types that are sent between InterchainTokenService on different chains. */ uint256 private constant MESSAGE_TYPE_INTERCHAIN_TRANSFER = 0; uint256 private constant MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN = 1; // uint256 private constant MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER = 2; uint256 private constant MESSAGE_TYPE_SEND_TO_HUB = 3; uint256 private constant MESSAGE_TYPE_RECEIVE_FROM_HUB = 4; uint256 private constant MESSAGE_TYPE_LINK_TOKEN = 5; uint256 private constant MESSAGE_TYPE_REGISTER_TOKEN_METADATA = 6; /** * @dev Tokens and token managers deployed via the Token Factory contract use a special deployer address. * This removes the dependency on the address the token factory was deployed too to be able to derive the same tokenId. */ address internal constant TOKEN_FACTORY_DEPLOYER = address(0); /** * @dev Latest version of metadata that's supported. */ uint32 internal constant LATEST_METADATA_VERSION = 1; /** * @dev Chain name where ITS Hub exists. This is used for routing ITS calls via ITS hub. * This is set as a constant, since the ITS Hub will exist on Axelar. */ string internal constant ITS_HUB_CHAIN_NAME = 'axelar'; bytes32 internal constant ITS_HUB_CHAIN_NAME_HASH = keccak256(abi.encodePacked(ITS_HUB_CHAIN_NAME)); /** * @dev Special identifier that the trusted address for a chain should be set to, which indicates if the ITS call * for that chain should be routed via the ITS hub. */ string internal constant ITS_HUB_ROUTING_IDENTIFIER = 'hub'; bytes32 internal constant ITS_HUB_ROUTING_IDENTIFIER_HASH = keccak256(abi.encodePacked(ITS_HUB_ROUTING_IDENTIFIER)); /** * @notice Constructor for the Interchain Token Service. * @dev All of the variables passed here are stored as immutable variables. * @param tokenManagerDeployer_ The address of the TokenManagerDeployer. * @param interchainTokenDeployer_ The address of the InterchainTokenDeployer. * @param gateway_ The address of the AxelarGateway. * @param gasService_ The address of the AxelarGasService. * @param interchainTokenFactory_ The address of the InterchainTokenFactory. * @param chainName_ The name of the chain that this contract is deployed on. * @param tokenManagerImplementation_ The tokenManager implementation. * @param tokenHandler_ The tokenHandler implementation. * @param gatewayCaller_ The gatewayCaller implementation. */ constructor( address tokenManagerDeployer_, address interchainTokenDeployer_, address gateway_, address gasService_, address interchainTokenFactory_, string memory chainName_, address tokenManagerImplementation_, address tokenHandler_, address gatewayCaller_ ) { if ( gasService_ == address(0) || tokenManagerDeployer_ == address(0) || interchainTokenDeployer_ == address(0) || gateway_ == address(0) || interchainTokenFactory_ == address(0) || tokenManagerImplementation_ == address(0) || tokenHandler_ == address(0) || gatewayCaller_ == address(0) ) revert ZeroAddress(); gateway = IAxelarGateway(gateway_); gasService = IAxelarGasService(gasService_); tokenManagerDeployer = tokenManagerDeployer_; interchainTokenDeployer = interchainTokenDeployer_; interchainTokenFactory = interchainTokenFactory_; if (bytes(chainName_).length == 0) revert InvalidChainName(); chainNameHash = keccak256(bytes(chainName_)); tokenManager = tokenManagerImplementation_; tokenHandler = tokenHandler_; gatewayCaller = gatewayCaller_; } /*******\ MODIFIERS \*******/ /** * @notice This modifier is used to ensure that only a remote InterchainTokenService can invoke the execute function. * @param sourceChain The source chain of the contract call. * @param sourceAddress The source address that the call came from. */ modifier onlyRemoteService(string calldata sourceChain, string calldata sourceAddress) { if (!isTrustedAddress(sourceChain, sourceAddress)) revert NotRemoteService(); _; } /** * @notice This modifier is used to ensure that only a the token factory can call a function. */ modifier onlyTokenFactory() { if (msg.sender != interchainTokenFactory) revert NotInterchainTokenFactory(msg.sender); _; } /*****\ GETTERS \*****/ /** * @notice Getter for the contract id. * @return bytes32 The contract id of this contract. */ function contractId() external pure returns (bytes32) { return CONTRACT_ID; } /** * @notice Calculates the address of a TokenManager from a specific tokenId. * @dev The TokenManager does not need to exist already. * @param tokenId The tokenId. * @return tokenManagerAddress_ The deployment address of the TokenManager. */ function tokenManagerAddress(bytes32 tokenId) public view returns (address tokenManagerAddress_) { tokenManagerAddress_ = _create3Address(tokenId); } /** * @notice Returns the instance of ITokenManager from a specific tokenId. * @dev This function checks if a token manager contract exists at the address for the specified tokenId. * If no token manager is deployed for the tokenId, the function will revert with `TokenManagerDoesNotExist`. * @param tokenId The tokenId of the deployed token manager. * @return tokenManager_ The instance of ITokenManager associated with the specified tokenId. */ function deployedTokenManager(bytes32 tokenId) public view returns (ITokenManager tokenManager_) { address tokenManagerAddress_ = tokenManagerAddress(tokenId); if (tokenManagerAddress_.code.length == 0) revert TokenManagerDoesNotExist(tokenId); tokenManager_ = ITokenManager(tokenManagerAddress_); } /** * @notice Returns the address of the token that an existing tokenManager points to. * @dev This function requires that a token manager is already deployed for the specified tokenId. * It will call `deployedTokenManager` to get the token manager and return the address of the associated token. * @param tokenId The tokenId of the registered token. * @return tokenAddress The address of the token. */ function registeredTokenAddress(bytes32 tokenId) public view returns (address tokenAddress) { tokenAddress = ITokenManager(deployedTokenManager(tokenId)).tokenAddress(); } /** * @notice Returns the address of the interchain token associated with the given tokenId. * @dev The token does not need to exist. * @param tokenId The tokenId of the interchain token. * @return tokenAddress The address of the interchain token. */ function interchainTokenAddress(bytes32 tokenId) public view returns (address tokenAddress) { tokenId = _getInterchainTokenSalt(tokenId); tokenAddress = _create3Address(tokenId); } /** * @notice Calculates the tokenId that would correspond to a link for a given deployer with a specified salt. * @param sender The address of the TokenManager deployer. * @param salt The salt that the deployer uses for the deployment. * @return tokenId The tokenId that the custom TokenManager would get (or has gotten). */ function interchainTokenId(address sender, bytes32 salt) public pure returns (bytes32 tokenId) { tokenId = keccak256(abi.encode(PREFIX_INTERCHAIN_TOKEN_ID, sender, salt)); } /** * @notice Getter function for TokenManager implementation. This will mainly be called by TokenManager proxies * to figure out their implementations. * @return tokenManagerAddress The address of the TokenManager implementation. */ function tokenManagerImplementation(uint256 /*tokenManagerType*/) external view returns (address) { return tokenManager; } /************\ USER FUNCTIONS \************/ /** * @notice Registers metadata for a token on the ITS Hub. This metadata is used for scaling linked tokens. * The token metadata must be registered before linkToken can be called for the corresponding token. * @param tokenAddress The address of the token. * @param gasValue The cross-chain gas value for sending the registration message to ITS Hub. */ function registerTokenMetadata(address tokenAddress, uint256 gasValue) external payable { if (tokenAddress == address(0)) revert EmptyTokenAddress(); uint8 decimals = IERC20Named(tokenAddress).decimals(); bytes memory payload = abi.encode(MESSAGE_TYPE_REGISTER_TOKEN_METADATA, tokenAddress.toBytes(), decimals); emit TokenMetadataRegistered(tokenAddress, decimals); _callContract( ITS_HUB_CHAIN_NAME, trustedAddress(ITS_HUB_CHAIN_NAME), payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue ); } /** * @notice Only to be used by the InterchainTokenFactory to register custom tokens to this chain. Then link token can be used to register those tokens to other chains. * @param salt A unique salt to derive tokenId from. * @param tokenManagerType The type of the token manager to use for the token registration. * @param linkParams The operator for the token. */ function registerCustomToken( bytes32 salt, address tokenAddress, TokenManagerType tokenManagerType, bytes calldata linkParams ) external payable whenNotPaused onlyTokenFactory returns (bytes32 tokenId) { // Custom token managers can't be deployed with native interchain token type, which is reserved for interchain tokens if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType); address deployer = TOKEN_FACTORY_DEPLOYER; tokenId = interchainTokenId(deployer, salt); emit InterchainTokenIdClaimed(tokenId, deployer, salt); _deployTokenManager(tokenId, tokenManagerType, tokenAddress, linkParams); } /** * @notice If `destinationChain` is an empty string, this function will register the token address on the current chain. * Otherwise, it will link the token address on the destination chain with the token corresponding to the tokenId on the current chain. * A token manager is deployed on EVM chains that's responsible for managing the linked token. * @dev This function replaces the prior `deployTokenManager` function. * @param salt A unique identifier to allow for multiple tokens registered per deployer. * @param destinationChain The chain to link the token to. Pass an empty string for this chain. * @param destinationTokenAddress The token address to link, as bytes. * @param tokenManagerType The type of the token manager to use to send and receive tokens. * @param linkParams Additional parameteres to use to link the token. Fow not it is just the address of the operator. * @param gasValue Pass a non-zero value only for remote linking, which should be the gas to use to pay for the contract call. * @return tokenId The tokenId associated with the token manager. */ function linkToken( bytes32 salt, string calldata destinationChain, bytes calldata destinationTokenAddress, TokenManagerType tokenManagerType, bytes calldata linkParams, uint256 gasValue ) public payable whenNotPaused returns (bytes32 tokenId) { if (destinationTokenAddress.length == 0) revert EmptyTokenAddress(); // Custom token managers can't be deployed with native interchain token type, which is reserved for interchain tokens if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType); // Cannot deploy to this chain using linkToken anymore if (bytes(destinationChain).length == 0) { revert NotSupported(); } // Cannot deploy to this chain using linkToken anymore if (chainNameHash == keccak256(bytes(destinationChain))) revert CannotDeployRemotelyToSelf(); address deployer = msg.sender; if (deployer == interchainTokenFactory) { deployer = TOKEN_FACTORY_DEPLOYER; } tokenId = interchainTokenId(deployer, salt); emit InterchainTokenIdClaimed(tokenId, deployer, salt); bytes memory sourceTokenAddress = registeredTokenAddress(tokenId).toBytes(); emit LinkTokenStarted(tokenId, destinationChain, sourceTokenAddress, destinationTokenAddress, tokenManagerType, linkParams); bytes memory payload = abi.encode( MESSAGE_TYPE_LINK_TOKEN, tokenId, tokenManagerType, sourceTokenAddress, destinationTokenAddress, linkParams ); _routeMessage(destinationChain, payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue); } /** * @notice Used to deploy an interchain token alongside a TokenManager in another chain. * @dev At least the `gasValue` amount of native token must be passed to the function call. `gasValue` exists because this function can be * part of a multicall involving multiple functions that could make remote contract calls. * If minter is empty bytes, no additional minter is set on the token, only ITS is allowed to mint. * If the token is being deployed on the current chain, minter should correspond to an EVM address (as bytes). * Otherwise, an encoding appropriate to the destination chain should be used. * @param salt The salt to be used during deployment. * @param destinationChain The name of the destination chain to deploy to. * @param name The name of the token to be deployed. * @param symbol The symbol of the token to be deployed. * @param decimals The decimals of the token to be deployed. * @param minter The address that will be able to mint and burn the deployed token. * @param gasValue The amount of native tokens to be used to pay for gas for the remote deployment. * @return tokenId The tokenId corresponding to the deployed InterchainToken. */ function deployInterchainToken( bytes32 salt, string calldata destinationChain, string memory name, string memory symbol, uint8 decimals, bytes memory minter, uint256 gasValue ) external payable whenNotPaused onlyTokenFactory returns (bytes32 tokenId) { address deployer = TOKEN_FACTORY_DEPLOYER; tokenId = interchainTokenId(deployer, salt); emit InterchainTokenIdClaimed(tokenId, deployer, salt); if (bytes(destinationChain).length == 0) { address tokenAddress = _deployInterchainToken(tokenId, minter, name, symbol, decimals); _deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, tokenAddress, minter); } else { if (chainNameHash == keccak256(bytes(destinationChain))) revert CannotDeployRemotelyToSelf(); _deployRemoteInterchainToken(tokenId, name, symbol, decimals, minter, destinationChain, gasValue); } } /** * @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. * @return address The token address. * @return uint256 The value the call is worth. */ function contractCallValue( string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) public view virtual onlyRemoteService(sourceChain, sourceAddress) whenNotPaused returns (address, uint256) { return _contractCallValue(payload); } /** * @notice Executes the cross-chain ITS message. * @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. */ 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 Express executes operations based on the payload and selector. * @dev This function is `payable` because non-payable functions cannot be called in a multicall that calls other `payable` functions. * @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. */ function expressExecute( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes calldata payload ) public payable whenNotPaused { uint256 messageType = abi.decode(payload, (uint256)); if (messageType != MESSAGE_TYPE_INTERCHAIN_TRANSFER) { revert InvalidExpressMessageType(messageType); } if (gateway.isCommandExecuted(commandId)) revert AlreadyExecuted(); address expressExecutor = msg.sender; bytes32 payloadHash = keccak256(payload); emit ExpressExecuted(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor); _setExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash, expressExecutor); _expressExecute(commandId, sourceChain, payload); } /** * @notice Returns the express executor for a given command. * @param commandId The commandId for the contractCall. * @param sourceChain The source chain. * @param sourceAddress The source address. * @param payloadHash The hash of the payload. * @return expressExecutor The address of the express executor. */ function getExpressExecutor( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes32 payloadHash ) external view returns (address expressExecutor) { expressExecutor = _getExpressExecutor(commandId, sourceChain, sourceAddress, payloadHash); } /** * @notice Uses the caller's tokens to fullfill a sendCall ahead of time. Use this only if you have detected an outgoing * interchainTransfer that matches the parameters passed here. * @param commandId The unique message id of the transfer being expressed. * @param sourceChain the name of the chain where the interchainTransfer originated from. * @param payload the payload of the receive token */ function _expressExecute(bytes32 commandId, string calldata sourceChain, bytes calldata payload) internal { (, bytes32 tokenId, bytes memory sourceAddress, bytes memory destinationAddressBytes, uint256 amount, bytes memory data) = abi .decode(payload, (uint256, bytes32, bytes, bytes, uint256, bytes)); address destinationAddress = destinationAddressBytes.toAddress(); IERC20 token; { (bool success, bytes memory returnData) = tokenHandler.delegatecall( abi.encodeWithSelector(ITokenHandler.transferTokenFrom.selector, tokenId, msg.sender, destinationAddress, amount) ); if (!success) revert TokenHandlerFailed(returnData); (amount, token) = abi.decode(returnData, (uint256, IERC20)); } // slither-disable-next-line reentrancy-events emit InterchainTransferReceived( commandId, tokenId, sourceChain, sourceAddress, destinationAddress, amount, data.length == 0 ? bytes32(0) : keccak256(data) ); if (data.length != 0) { bytes32 result = IInterchainTokenExpressExecutable(destinationAddress).expressExecuteWithInterchainToken( commandId, sourceChain, sourceAddress, data, tokenId, address(token), amount ); if (result != EXPRESS_EXECUTE_SUCCESS) revert ExpressExecuteWithInterchainTokenFailed(destinationAddress); } } /** * @notice Initiates an interchain transfer of a specified token to a destination chain. * @dev The function retrieves the TokenManager associated with the tokenId. * @param tokenId The unique identifier of the token to be transferred. * @param destinationChain The destination chain to send the tokens to. * @param destinationAddress The address on the destination chain to send the tokens to. * @param amount The amount of tokens to be transferred. * @param metadata Optional metadata for the transfer. The first 4 bytes is the metadata version. To call the `destinationAddress` as a contract with a payload, provide `bytes.concat(bytes4(0), payload)` as the metadata. The token will be transferred to the destination app contract before it is executed. */ function interchainTransfer( bytes32 tokenId, string calldata destinationChain, bytes calldata destinationAddress, uint256 amount, bytes calldata metadata, uint256 gasValue ) external payable whenNotPaused { amount = _takeToken(tokenId, msg.sender, amount, false); (IGatewayCaller.MetadataVersion metadataVersion, bytes memory data) = _decodeMetadata(metadata); _transmitInterchainTransfer(tokenId, msg.sender, destinationChain, destinationAddress, amount, metadataVersion, data, gasValue); } /******************\ TOKEN ONLY FUNCTIONS \******************/ /** * @notice Transmit an interchain transfer for the given tokenId. * @dev Only callable by a token registered under a tokenId. * @param tokenId The tokenId of the token (which must be the msg.sender). * @param sourceAddress The address where the token is coming from. * @param destinationChain The name of the chain to send tokens to. * @param destinationAddress The destinationAddress for the interchainTransfer. * @param amount The amount of token to give. * @param metadata Optional metadata for the call for additional effects (such as calling a destination contract). */ function transmitInterchainTransfer( bytes32 tokenId, address sourceAddress, string calldata destinationChain, bytes memory destinationAddress, uint256 amount, bytes calldata metadata ) external payable whenNotPaused { amount = _takeToken(tokenId, sourceAddress, amount, true); (IGatewayCaller.MetadataVersion metadataVersion, bytes memory data) = _decodeMetadata(metadata); _transmitInterchainTransfer(tokenId, sourceAddress, destinationChain, destinationAddress, amount, metadataVersion, data, msg.value); } /*************\ OWNER FUNCTIONS \*************/ /** * @notice Used to set a flow limit for a token manager that has the service as its operator. * @param tokenIds An array of the tokenIds of the tokenManagers to set the flow limits of. * @param flowLimits The flowLimits to set. */ function setFlowLimits(bytes32[] calldata tokenIds, uint256[] calldata flowLimits) external onlyRole(uint8(Roles.OPERATOR)) { uint256 length = tokenIds.length; if (length != flowLimits.length) revert LengthMismatch(); for (uint256 i; i < length; ++i) { // slither-disable-next-line calls-loop deployedTokenManager(tokenIds[i]).setFlowLimit(flowLimits[i]); } } /** * @notice Used to set a trusted address for a chain. * @param chain The chain to set the trusted address of. * @param address_ The address to set as trusted. */ function setTrustedAddress(string memory chain, string memory address_) external onlyOwner { _setTrustedAddress(chain, address_); } /** * @notice Used to remove a trusted address for a chain. * @param chain The chain to set the trusted address of. */ function removeTrustedAddress(string memory chain) external onlyOwner { _removeTrustedAddress(chain); } /** * @notice Allows the owner to pause/unpause the token service. * @param paused Boolean value representing whether to pause or unpause. */ function setPauseStatus(bool paused) external onlyOwner { if (paused) { _pause(); } else { _unpause(); } } /** * @notice Allows the owner to migrate minter of native interchain tokens from ITS to the corresponding token manager. * @param tokenId the tokenId of the registered token. */ function migrateInterchainToken(bytes32 tokenId) external onlyOwner { ITokenManager tokenManager_ = deployedTokenManager(tokenId); address tokenAddress = tokenManager_.tokenAddress(); IMinter(tokenAddress).transferMintership(address(tokenManager_)); } /****************\ INTERNAL FUNCTIONS \****************/ function _setup(bytes calldata params) internal override { (address operator, string memory chainName_, string[] memory trustedChainNames, string[] memory trustedAddresses) = abi.decode( params, (address, string, string[], string[]) ); uint256 length = trustedChainNames.length; if (operator == address(0)) revert ZeroAddress(); if (bytes(chainName_).length == 0 || keccak256(bytes(chainName_)) != chainNameHash) revert InvalidChainName(); if (length != trustedAddresses.length) revert LengthMismatch(); _addOperator(operator); _setChainName(chainName_); for (uint256 i; i < length; ++i) { _setTrustedAddress(trustedChainNames[i], trustedAddresses[i]); } } /** * @notice Processes the payload data for a send token call. * @param commandId The unique message id. * @param expressExecutor The address of the express executor. Equals `address(0)` if it wasn't expressed. * @param sourceChain The chain where the transaction originates from. * @param payload The encoded data payload to be processed. */ function _processInterchainTransferPayload( bytes32 commandId, address expressExecutor, string memory sourceChain, bytes memory payload ) internal { bytes32 tokenId; bytes memory sourceAddress; address destinationAddress; uint256 amount; bytes memory data; { bytes memory destinationAddressBytes; (, tokenId, sourceAddress, destinationAddressBytes, amount, data) = abi.decode( payload, (uint256, bytes32, bytes, bytes, uint256, bytes) ); destinationAddress = destinationAddressBytes.toAddress(); } // Return token to the express executor if (expressExecutor != address(0)) { _giveToken(tokenId, expressExecutor, amount); return; } address tokenAddress; (amount, tokenAddress) = _giveToken(tokenId, destinationAddress, amount); // slither-disable-next-line reentrancy-events emit InterchainTransferReceived( commandId, tokenId, sourceChain, sourceAddress, destinationAddress, amount, data.length == 0 ? bytes32(0) : keccak256(data) ); if (data.length != 0) { bytes32 result = IInterchainTokenExecutable(destinationAddress).executeWithInterchainToken( commandId, sourceChain, sourceAddress, data, tokenId, tokenAddress, amount ); if (result != EXECUTE_SUCCESS) revert ExecuteWithInterchainTokenFailed(destinationAddress); } } /** * @notice Processes a deploy token manager payload. */ function _processLinkTokenPayload(bytes memory payload) internal { (, bytes32 tokenId, TokenManagerType tokenManagerType, , bytes memory destinationTokenAddress, bytes memory linkParams) = abi .decode(payload, (uint256, bytes32, TokenManagerType, bytes, bytes, bytes)); if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType); _deployTokenManager(tokenId, tokenManagerType, destinationTokenAddress.toAddress(), linkParams); } /** * @notice Processes a deploy interchain token manager payload. * @param payload The encoded data payload to be processed. */ function _processDeployInterchainTokenPayload(bytes memory payload) internal { (, bytes32 tokenId, string memory name, string memory symbol, uint8 decimals, bytes memory minterBytes) = abi.decode( payload, (uint256, bytes32, string, string, uint8, bytes) ); address tokenAddress; tokenAddress = _deployInterchainToken(tokenId, minterBytes, name, symbol, decimals); _deployTokenManager(tokenId, TokenManagerType.NATIVE_INTERCHAIN_TOKEN, tokenAddress, minterBytes); } /** * @notice Route the ITS message to the destination chain with the given payload * @dev This method also determines whether the ITS call should be routed via the ITS Hub. * If the `trustedAddress(destinationChain) == 'hub'`, then the call is wrapped and routed to the ITS Hub destination. * @param destinationChain The target chain where the contract will be called. * @param payload The data payload for the transaction. * @param gasValue The amount of gas to be paid for the transaction. */ function _routeMessage( string memory destinationChain, bytes memory payload, IGatewayCaller.MetadataVersion metadataVersion, uint256 gasValue ) internal { string memory destinationAddress; (destinationChain, destinationAddress, payload) = _getCallParams(destinationChain, payload); _callContract(destinationChain, destinationAddress, payload, metadataVersion, gasValue); } /** * @notice Calls a contract on a destination chain via the gateway caller. * @param destinationChain The chain where the contract will be called. * @param destinationAddress The address of the contract to call. * @param payload The data payload for the transaction. * @param metadataVersion The version of the metadata. * @param gasValue The amount of gas to be paid for the transaction. */ function _callContract( string memory destinationChain, string memory destinationAddress, bytes memory payload, IGatewayCaller.MetadataVersion metadataVersion, uint256 gasValue ) internal { // Check whether no trusted address was set for the destination chain if (bytes(destinationAddress).length == 0) revert UntrustedChain(); (bool success, bytes memory returnData) = gatewayCaller.delegatecall( abi.encodeWithSelector( IGatewayCaller.callContract.selector, destinationChain, destinationAddress, payload, metadataVersion, gasValue ) ); if (!success) revert GatewayCallFailed(returnData); } /** * @dev Get the params for the cross-chain message, taking routing via ITS Hub into account. */ function _getCallParams( string memory destinationChain, bytes memory payload ) internal view returns (string memory, string memory, bytes memory) { string memory destinationAddress = trustedAddress(destinationChain); // Prevent sending directly to the ITS Hub chain. This is not supported yet, so fail early to prevent the user from having their funds stuck. if (keccak256(abi.encodePacked(destinationChain)) == ITS_HUB_CHAIN_NAME_HASH) revert UntrustedChain(); // Check whether the ITS call should be routed via ITS hub for this destination chain if (keccak256(abi.encodePacked(destinationAddress)) == ITS_HUB_ROUTING_IDENTIFIER_HASH) { // Wrap ITS message in an ITS Hub message payload = abi.encode(MESSAGE_TYPE_SEND_TO_HUB, destinationChain, payload); destinationChain = ITS_HUB_CHAIN_NAME; destinationAddress = trustedAddress(ITS_HUB_CHAIN_NAME); } return (destinationChain, destinationAddress, payload); } function _execute( bytes32 commandId, string calldata sourceChain, string calldata sourceAddress, bytes memory payload, bytes32 payloadHash ) internal { uint256 messageType; string memory originalSourceChain; (messageType, originalSourceChain, payload) = _getExecuteParams(sourceChain, payload); if (messageType == MESSAGE_TYPE_INTERCHAIN_TRANSFER) { address expressExecutor = _getExpressExecutorAndEmitEvent(commandId, sourceChain, sourceAddress, payloadHash); _processInterchainTransferPayload(commandId, expressExecutor, originalSourceChain, payload); } else if (messageType == MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN) { _processDeployInterchainTokenPayload(payload); } else if (messageType == MESSAGE_TYPE_LINK_TOKEN) { _processLinkTokenPayload(payload); } else { revert InvalidMessageType(messageType); } } function _getMessageType(bytes memory payload) internal pure returns (uint256 messageType) { if (payload.length < 32) revert InvalidPayload(); /// @solidity memory-safe-assembly assembly { messageType := mload(add(payload, 32)) } } /** * @dev Return the parameters for the execute call, taking routing via ITS Hub into account. */ function _getExecuteParams( string calldata sourceChain, bytes memory payload ) internal view returns (uint256, string memory, bytes memory) { // Read the first 32 bytes of the payload to determine the message type uint256 messageType = _getMessageType(payload); // True source chain, this is overridden if the ITS call is coming via the ITS hub string memory originalSourceChain = sourceChain; // Unwrap ITS message if coming from ITS hub if (messageType == MESSAGE_TYPE_RECEIVE_FROM_HUB) { if (keccak256(abi.encodePacked(sourceChain)) != ITS_HUB_CHAIN_NAME_HASH) revert UntrustedChain(); (, originalSourceChain, payload) = abi.decode(payload, (uint256, string, bytes)); // Check whether the original source chain is expected to be routed via the ITS Hub if (trustedAddressHash(originalSourceChain) != ITS_HUB_ROUTING_IDENTIFIER_HASH) revert UntrustedChain(); // Get message type of the inner ITS message messageType = _getMessageType(payload); } else { // Prevent receiving a direct message from the ITS Hub. This is not supported yet. if (keccak256(abi.encodePacked(sourceChain)) == ITS_HUB_CHAIN_NAME_HASH) revert UntrustedChain(); } return (messageType, originalSourceChain, payload); } /** * @notice Deploys an interchain token on a destination chain. * @param tokenId The ID of the token. * @param name The name of the token. * @param symbol The symbol of the token. * @param decimals The number of decimals of the token. * @param minter The minter address for the token. * @param destinationChain The destination chain where the token will be deployed. * @param gasValue The amount of gas to be paid for the transaction. */ function _deployRemoteInterchainToken( bytes32 tokenId, string memory name, string memory symbol, uint8 decimals, bytes memory minter, string calldata destinationChain, uint256 gasValue ) internal { if (bytes(name).length == 0) revert EmptyTokenName(); if (bytes(symbol).length == 0) revert EmptyTokenSymbol(); // slither-disable-next-line unused-return deployedTokenManager(tokenId); // slither-disable-next-line reentrancy-events emit InterchainTokenDeploymentStarted(tokenId, name, symbol, decimals, minter, destinationChain); bytes memory payload = abi.encode(MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN, tokenId, name, symbol, decimals, minter); _routeMessage(destinationChain, payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue); } /** * @notice Deploys a token manager. * @param tokenId The ID of the token. * @param tokenManagerType The type of the token manager to be deployed. * @param tokenAddress The address of the token to be managed. * @param operator The operator of the token manager. */ function _deployTokenManager(bytes32 tokenId, TokenManagerType tokenManagerType, address tokenAddress, bytes memory operator) internal { // TokenManagerProxy params bytes memory params = abi.encode(operator, tokenAddress); (bool success, bytes memory returnData) = tokenManagerDeployer.delegatecall( abi.encodeWithSelector(ITokenManagerDeployer.deployTokenManager.selector, tokenId, tokenManagerType, params) ); if (!success) revert TokenManagerDeploymentFailed(returnData); address tokenManager_; assembly { tokenManager_ := mload(add(returnData, 0x20)) } (success, returnData) = tokenHandler.delegatecall( abi.encodeWithSelector(ITokenHandler.postTokenManagerDeploy.selector, tokenManagerType, tokenManager_) ); if (!success) revert PostDeployFailed(returnData); // slither-disable-next-line reentrancy-events emit TokenManagerDeployed(tokenId, tokenManager_, tokenManagerType, params); } /** * @notice Computes the salt for an interchain token deployment. * @param tokenId The ID of the token. * @return salt The computed salt for the token deployment. */ function _getInterchainTokenSalt(bytes32 tokenId) internal pure returns (bytes32 salt) { salt = keccak256(abi.encode(PREFIX_INTERCHAIN_TOKEN_SALT, tokenId)); } /** * @notice Deploys an interchain token. * @param tokenId The ID of the token. * @param minterBytes The minter address for the token. * @param name The name of the token. * @param symbol The symbol of the token. * @param decimals The number of decimals of the token. */ function _deployInterchainToken( bytes32 tokenId, bytes memory minterBytes, string memory name, string memory symbol, uint8 decimals ) internal returns (address tokenAddress) { if (bytes(name).length == 0) revert EmptyTokenName(); if (bytes(symbol).length == 0) revert EmptyTokenSymbol(); bytes32 salt = _getInterchainTokenSalt(tokenId); address minter; if (bytes(minterBytes).length != 0) minter = minterBytes.toAddress(); (bool success, bytes memory returnData) = interchainTokenDeployer.delegatecall( abi.encodeWithSelector(IInterchainTokenDeployer.deployInterchainToken.selector, salt, tokenId, minter, name, symbol, decimals) ); if (!success) { revert InterchainTokenDeploymentFailed(returnData); } assembly { tokenAddress := mload(add(returnData, 0x20)) } // slither-disable-next-line reentrancy-events emit InterchainTokenDeployed(tokenId, tokenAddress, minter, name, symbol, decimals); } /** * @notice Decodes the metadata into a version number and data bytes. * @dev The function expects the metadata to have the version in the first 4 bytes, followed by the actual data. * @param metadata The bytes containing the metadata to decode. * @return version The version number extracted from the metadata. * @return data The data bytes extracted from the metadata. */ function _decodeMetadata(bytes calldata metadata) internal pure returns (IGatewayCaller.MetadataVersion version, bytes memory data) { if (metadata.length < 4) return (IGatewayCaller.MetadataVersion.CONTRACT_CALL, data); uint32 versionUint = uint32(bytes4(metadata[:4])); if (versionUint > LATEST_METADATA_VERSION) revert InvalidMetadataVersion(versionUint); version = IGatewayCaller.MetadataVersion(versionUint); if (metadata.length == 4) return (version, data); data = metadata[4:]; } /** * @notice Transmit a callContractWithInterchainToken for the given tokenId. * @param tokenId The tokenId of the TokenManager (which must be the msg.sender). * @param sourceAddress The address where the token is coming from, which will also be used for gas reimbursement. * @param destinationChain The name of the chain to send tokens to. * @param destinationAddress The destinationAddress for the interchainTransfer. * @param amount The amount of tokens to send. * @param metadataVersion The version of the metadata. * @param data The data to be passed with the token transfer. * @param gasValue The amount of gas to be paid for the transaction. */ function _transmitInterchainTransfer( bytes32 tokenId, address sourceAddress, string calldata destinationChain, bytes memory destinationAddress, uint256 amount, IGatewayCaller.MetadataVersion metadataVersion, bytes memory data, uint256 gasValue ) internal { if (destinationAddress.length == 0) revert EmptyDestinationAddress(); if (amount == 0) revert ZeroAmount(); // slither-disable-next-line reentrancy-events emit InterchainTransfer( tokenId, sourceAddress, destinationChain, destinationAddress, amount, data.length == 0 ? bytes32(0) : keccak256(data) ); bytes memory payload = abi.encode( MESSAGE_TYPE_INTERCHAIN_TRANSFER, tokenId, sourceAddress.toBytes(), destinationAddress, amount, data ); _routeMessage(destinationChain, payload, metadataVersion, gasValue); } /** * @dev Takes token from a sender via the token service. `tokenOnly` indicates if the caller should be restricted to the token only. */ function _takeToken(bytes32 tokenId, address from, uint256 amount, bool tokenOnly) internal returns (uint256) { (bool success, bytes memory data) = tokenHandler.delegatecall( abi.encodeWithSelector(ITokenHandler.takeToken.selector, tokenId, tokenOnly, from, amount) ); if (!success) revert TakeTokenFailed(data); amount = abi.decode(data, (uint256)); return amount; } /** * @dev Gives token to recipient via the token service. */ function _giveToken(bytes32 tokenId, address to, uint256 amount) internal returns (uint256, address tokenAddress) { (bool success, bytes memory data) = tokenHandler.delegatecall( abi.encodeWithSelector(ITokenHandler.giveToken.selector, tokenId, to, amount) ); if (!success) revert GiveTokenFailed(data); (amount, tokenAddress) = abi.decode(data, (uint256, address)); 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 (registeredTokenAddress(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); } } }