Transaction Hash:
Block:
21887481 at Feb-20-2025 12:10:11 PM +UTC
Transaction Fee:
0.00014781076635435 ETH
$0.30
Gas Used:
151,999 Gas / 0.97244565 Gwei
Emitted Events:
| 99 |
Contract.Transfer( from=[Sender] 0x0000000001fd5528f18ab1d0871097decb2b0ead, to=0x301db234167c4f6834DcE3f274B18D759D7edDC9, value=7283943052678596863911 )
|
| 100 |
Contract.Approval( owner=[Sender] 0x0000000001fd5528f18ab1d0871097decb2b0ead, spender=[Receiver] InterchainProxy, value=10000000000000000139344719353280198507553037 )
|
| 101 |
InterchainProxy.0xcd05f5b9dc4bb03babf40f5da98f5f46819846207d916f89b67d36fd1f7fd74f( 0xcd05f5b9dc4bb03babf40f5da98f5f46819846207d916f89b67d36fd1f7fd74f, 0x0c5366be136c8a820aeb9ebb380a307a1cfd521af5772161dd3994d9828caeb7, 0x0000000000000000000000000000000001fd5528f18ab1d0871097decb2b0ead, 0x0000000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000060, 00000000000000000000000000000000000000000000000000000000000000a0, 00000000000000000000000000000000000000000000018add041e6d4a2b27a7, 0000000000000000000000000000000000000000000000000000000000000004, 6261736500000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000014, 0000000001fd5528f18ab1d0871097decb2b0ead000000000000000000000000 )
|
| 102 |
AxelarGasServiceProxy.0x617332c1832058df6ee45fcbdf471251474c9945a8e5d229287a21a5f67ccf0a( 0x617332c1832058df6ee45fcbdf471251474c9945a8e5d229287a21a5f67ccf0a, 0x000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c, 0xa487580e8c6416c054f498c599760661ef4916215a4d6949d264630b8df8708f, 0000000000000000000000000000000000000000000000000000000000000080, 00000000000000000000000000000000000000000000000000000000000000c0, 00000000000000000000000000000000000000000000000000004f05165cf4fc, 0000000000000000000000000000000001fd5528f18ab1d0871097decb2b0ead, 0000000000000000000000000000000000000000000000000000000000000004, 6261736500000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000000000002a, 3078423546423442453032323332423162424134644338663831646332344332, 3639383064453965334300000000000000000000000000000000000000000000 )
|
| 103 |
0x4f4495243837681061c4743b74b3eedf548d56a5.0x30ae6cc78c27e651745bf2ad08a11de83910ac1e347a52f7ac898c0fbef94dae( 0x30ae6cc78c27e651745bf2ad08a11de83910ac1e347a52f7ac898c0fbef94dae, 0x000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c, 0xa487580e8c6416c054f498c599760661ef4916215a4d6949d264630b8df8708f, 0000000000000000000000000000000000000000000000000000000000000060, 00000000000000000000000000000000000000000000000000000000000000a0, 0000000000000000000000000000000000000000000000000000000000000100, 0000000000000000000000000000000000000000000000000000000000000004, 6261736500000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000000000002a, 3078423546423442453032323332423162424134644338663831646332344332, 3639383064453965334300000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000160, 0000000000000000000000000000000000000000000000000000000000000000, 0c5366be136c8a820aeb9ebb380a307a1cfd521af5772161dd3994d9828caeb7, 00000000000000000000000000000000000000000000000000000000000000c0, 0000000000000000000000000000000000000000000000000000000000000100, 00000000000000000000000000000000000000000000018add041e6d4a2b27a7, 0000000000000000000000000000000000000000000000000000000000000140, 0000000000000000000000000000000000000000000000000000000000000014, 0000000001fd5528f18ab1d0871097decb2b0ead000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000014, 0000000001fd5528f18ab1d0871097decb2b0ead000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x00000000...ecB2B0eAD |
0.081922252675021161 Eth
Nonce: 679
|
0.081687558640045247 Eth
Nonce: 680
| 0.000234694034975914 | ||
| 0x1121AcC1...A6098AAc5 | |||||
|
0x1d5BA541...B4CdB00dC
Miner
| (Bitget 19) | 19.653640390455972473 Eth | 19.653670478555879145 Eth | 0.000030088099906672 | |
| 0x2d5d7d31...09a082712 | (Axelar: Gas Service) | 0.42989494351743113 Eth | 0.429981826786052694 Eth | 0.000086883268621564 |
Execution Trace
ETH 0.000086883268621564
InterchainProxy.da081c73( )
ETH 0.000086883268621564
InterchainTokenService.interchainTransfer( tokenId=0C5366BE136C8A820AEB9EBB380A307A1CFD521AF5772161DD3994D9828CAEB7, destinationChain=base, destinationAddress=0x0000000001FD5528F18AB1D0871097DECB2B0EAD, amount=7283943052678596863911, metadata=0x, gasValue=86883268621564 )ETH 0.000086883268621564
TokenHandler.takeToken( tokenId=0C5366BE136C8A820AEB9EBB380A307A1CFD521AF5772161DD3994D9828CAEB7, tokenOnly=False, from=0x0000000001FD5528f18AB1D0871097DecB2B0eAD, amount=7283943052678596863911 ) => ( 7283943052678596863911 )-
0x301db234167c4f6834dce3f274b18d759d7eddc9.STATICCALL( ) -
Contract.transferFrom( sender=0x0000000001FD5528f18AB1D0871097DecB2B0eAD, recipient=0x301db234167c4f6834DcE3f274B18D759D7edDC9, amount=7283943052678596863911 ) => ( True )
0x301db234167c4f6834dce3f274b18d759d7eddc9.dce29136( )InterchainProxy.7e151fa6( )-
InterchainTokenService.tokenManagerImplementation( 2 ) => ( 0xc8e58E2BA863bFb86869227f8264d99de7738933 )
-
-
TokenManager.addFlowOut( amount=7283943052678596863911 )
-
ETH 0.000086883268621564
GatewayCaller.callContract( destinationChain=base, destinationAddress=0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C, payload=0x00000000000000000000000000000000000000000000000000000000000000000C5366BE136C8A820AEB9EBB380A307A1CFD521AF5772161DD3994D9828CAEB700000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000018ADD041E6D4A2B27A7000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000140000000001FD5528F18AB1D0871097DECB2B0EAD00000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000001FD5528F18AB1D0871097DECB2B0EAD0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, metadataVersion=0, gasValue=86883268621564 )ETH 0.000086883268621564
AxelarGasServiceProxy.0c93e3bb( )- ETH 0.000086883268621564
AxelarGasService.payNativeGasForContractCall( sender=0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C, destinationChain=base, destinationAddress=0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C, payload=0x00000000000000000000000000000000000000000000000000000000000000000C5366BE136C8A820AEB9EBB380A307A1CFD521AF5772161DD3994D9828CAEB700000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000018ADD041E6D4A2B27A7000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000140000000001FD5528F18AB1D0871097DECB2B0EAD00000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000001FD5528F18AB1D0871097DECB2B0EAD0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000, refundAddress=0x0000000001FD5528f18AB1D0871097DecB2B0eAD )
- ETH 0.000086883268621564
Axelar: Gateway.1c92115f( )-
AxelarGateway.callContract( destinationChain=base, destinationContractAddress=0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C, payload=0x00000000000000000000000000000000000000000000000000000000000000000C5366BE136C8A820AEB9EBB380A307A1CFD521AF5772161DD3994D9828CAEB700000000000000000000000000000000000000000000000000000000000000C0000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000018ADD041E6D4A2B27A7000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000140000000001FD5528F18AB1D0871097DECB2B0EAD00000000000000000000000000000000000000000000000000000000000000000000000000000000000000140000000001FD5528F18AB1D0871097DECB2B0EAD0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 )
-
interchainTransfer[InterchainTokenService (ln:2282)]
_takeToken[InterchainTokenService (ln:2290)]delegatecall[InterchainTokenService (ln:2783)]encodeWithSelector[InterchainTokenService (ln:2784)]TakeTokenFailed[InterchainTokenService (ln:2786)]decode[InterchainTokenService (ln:2787)]
_decodeMetadata[InterchainTokenService (ln:2291)]InvalidMetadataVersion[InterchainTokenService (ln:2732)]MetadataVersion[InterchainTokenService (ln:2733)]
_transmitInterchainTransfer[InterchainTokenService (ln:2292)]EmptyDestinationAddress[InterchainTokenService (ln:2758)]ZeroAmount[InterchainTokenService (ln:2759)]InterchainTransfer[InterchainTokenService (ln:2761)]encode[InterchainTokenService (ln:2769)]toBytes[InterchainTokenService (ln:2772)]_callContract[InterchainTokenService (ln:2777)]_getCallParams[InterchainTokenService (ln:2520)]trustedAddress[InterchainTokenService (ln:2540)]UntrustedChain[InterchainTokenService (ln:2542)]_getMessageType[InterchainTokenService (ln:2546)]InvalidPayload[InterchainTokenService (ln:2578)]
NotSupported[InterchainTokenService (ln:2546)]encode[InterchainTokenService (ln:2548)]trustedAddress[InterchainTokenService (ln:2550)]UntrustedChain[InterchainTokenService (ln:2553)]
delegatecall[InterchainTokenService (ln:2521)]encodeWithSelector[InterchainTokenService (ln:2522)]GatewayCallFailed[InterchainTokenService (ln:2531)]
File 1 of 9: InterchainProxy
File 2 of 9: Contract
File 3 of 9: AxelarGasServiceProxy
File 4 of 9: InterchainTokenService
File 5 of 9: TokenHandler
File 6 of 9: TokenManager
File 7 of 9: GatewayCaller
File 8 of 9: AxelarGasService
File 9 of 9: AxelarGateway
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// General interface for upgradable contracts
interface IProxy {
error InvalidOwner();
error InvalidImplementation();
error SetupFailed();
error NotOwner();
error AlreadyInitialized();
function implementation() external view returns (address);
function setup(bytes calldata setupParams) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IProxy } from '../interfaces/IProxy.sol';
/**
* @title BaseProxy Contract
* @dev This abstract contract implements a basic proxy that stores an implementation address. Fallback function
* calls are delegated to the implementation. This contract is meant to be inherited by other proxy contracts.
*/
abstract contract BaseProxy is IProxy {
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
// keccak256('owner')
bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;
/**
* @dev Returns the current implementation address.
* @return implementation_ The address of the current implementation contract
*/
function implementation() public view virtual returns (address implementation_) {
assembly {
implementation_ := sload(_IMPLEMENTATION_SLOT)
}
}
/**
* @dev Shadows the setup function of the implementation contract so it can't be called directly via the proxy.
* @param params The setup parameters for the implementation contract.
*/
function setup(bytes calldata params) external {}
/**
* @dev Returns the contract ID. It can be used as a check during upgrades. Meant to be implemented in derived contracts.
* @return bytes32 The contract ID
*/
function contractId() internal pure virtual returns (bytes32);
/**
* @dev Fallback function. Delegates the call to the current implementation contract.
*/
fallback() external payable virtual {
address implementation_ = implementation();
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), implementation_, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev Payable fallback function. Can be overridden in derived contracts.
*/
receive() external payable virtual {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IProxy } from '../interfaces/IProxy.sol';
import { IContractIdentifier } from '../interfaces/IContractIdentifier.sol';
import { BaseProxy } from './BaseProxy.sol';
/**
* @title Proxy Contract
* @notice A proxy contract that delegates calls to a designated implementation contract. Inherits from BaseProxy.
* @dev The constructor takes in the address of the implementation contract, the owner address, and any optional setup
* parameters for the implementation contract.
*/
contract Proxy is BaseProxy {
/**
* @notice Constructs the proxy contract with the implementation address, owner address, and optional setup parameters.
* @param implementationAddress The address of the implementation contract
* @param owner The owner address
* @param setupParams Optional parameters to setup the implementation contract
* @dev The constructor verifies that the owner address is not the zero address and that the contract ID of the implementation is valid.
* It then stores the implementation address and owner address in their designated storage slots and calls the setup function on the
* implementation (if setup params exist).
*/
constructor(
address implementationAddress,
address owner,
bytes memory setupParams
) {
if (owner == address(0)) revert InvalidOwner();
bytes32 id = contractId();
// Skipping the check if contractId() is not set by an inheriting proxy contract
if (id != bytes32(0) && IContractIdentifier(implementationAddress).contractId() != id)
revert InvalidImplementation();
assembly {
sstore(_IMPLEMENTATION_SLOT, implementationAddress)
sstore(_OWNER_SLOT, owner)
}
if (setupParams.length != 0) {
(bool success, ) = implementationAddress.delegatecall(
abi.encodeWithSelector(BaseProxy.setup.selector, setupParams)
);
if (!success) revert SetupFailed();
}
}
function contractId() internal pure virtual override returns (bytes32) {
return bytes32(0);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { Proxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Proxy.sol';
/**
* @title InterchainProxy
* @notice This contract is a proxy for interchainTokenService and interchainTokenFactory.
* @dev This contract implements Proxy.
*/
contract InterchainProxy is Proxy {
constructor(address implementationAddress, address owner, bytes memory setupParams) Proxy(implementationAddress, owner, setupParams) {}
}
File 2 of 9: Contract
// SPDX-License-Identifier: MIT
/*
https://x.com/elonmusk/status/1825713908067479735
https://t.me/NewDogeOnETH
*/
pragma solidity ^0.8.17;
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
}
interface IUniswapV2Pair {
event Approval(address indexed owner, address indexed spender, uint value);
event Transfer(address indexed from, address indexed to, uint value);
function name() external pure returns (string memory);
function symbol() external pure returns (string memory);
function decimals() external pure returns (uint8);
function totalSupply() external view returns (uint);
function balanceOf(address owner) external view returns (uint);
function allowance(
address owner,
address spender
) external view returns (uint);
function approve(address spender, uint value) external returns (bool);
function transfer(address to, uint value) external returns (bool);
function transferFrom(
address from,
address to,
uint value
) external returns (bool);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function PERMIT_TYPEHASH() external pure returns (bytes32);
function nonces(address owner) external view returns (uint);
function permit(
address owner,
address spender,
uint value,
uint deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(
address indexed sender,
uint amount0,
uint amount1,
address indexed to
);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint112 reserve0, uint112 reserve1);
function MINIMUM_LIQUIDITY() external pure returns (uint);
function factory() external view returns (address);
function token0() external view returns (address);
function token1() external view returns (address);
function getReserves()
external
view
returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
function price0CumulativeLast() external view returns (uint);
function price1CumulativeLast() external view returns (uint);
function kLast() external view returns (uint);
function mint(address to) external returns (uint liquidity);
function burn(address to) external returns (uint amount0, uint amount1);
function swap(
uint amount0Out,
uint amount1Out,
address to,
bytes calldata data
) external;
function skim(address to) external;
function sync() external;
function initialize(address, address) external;
}
interface IUniswapV2Factory {
event PairCreated(
address indexed token0,
address indexed token1,
address pair,
uint
);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(
address tokenA,
address tokenB
) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(
address tokenA,
address tokenB
) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
interface IERC20 {
/**
* @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
);
}
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
contract ERC20 is Context, IERC20, IERC20Metadata {
using SafeMath for uint256;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(
address account
) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(
address owner,
address spender
) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(
address spender,
uint256 amount
) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* Requirements:
*
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(
sender,
_msgSender(),
_allowances[sender][_msgSender()].sub(
amount,
"ERC20: transfer amount exceeds allowance"
)
);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(
address spender,
uint256 addedValue
) public virtual returns (bool) {
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].add(addedValue)
);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(
address spender,
uint256 subtractedValue
) public virtual returns (bool) {
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].sub(
subtractedValue,
"ERC20: decreased allowance below zero"
)
);
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(
amount,
"ERC20: transfer amount exceeds balance"
);
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(
amount,
"ERC20: burn amount exceeds balance"
);
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(
uint256 a,
uint256 b,
string memory errorMessage
) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
contract Ownable is Context {
address private _owner;
event OwnershipTransferred(
address indexed previousOwner,
address indexed newOwner
);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(
newOwner != address(0),
"Ownable: new owner is the zero address"
);
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
library SafeMathInt {
int256 private constant MIN_INT256 = int256(1) << 255;
int256 private constant MAX_INT256 = ~(int256(1) << 255);
/**
* @dev Multiplies two int256 variables and fails on overflow.
*/
function mul(int256 a, int256 b) internal pure returns (int256) {
int256 c = a * b;
// Detect overflow when multiplying MIN_INT256 with -1
require(c != MIN_INT256 || (a & MIN_INT256) != (b & MIN_INT256));
require((b == 0) || (c / b == a));
return c;
}
/**
* @dev Division of two int256 variables and fails on overflow.
*/
function div(int256 a, int256 b) internal pure returns (int256) {
// Prevent overflow when dividing MIN_INT256 by -1
require(b != -1 || a != MIN_INT256);
// Solidity already throws when dividing by 0.
return a / b;
}
/**
* @dev Subtracts two int256 variables and fails on overflow.
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
int256 c = a - b;
require((b >= 0 && c <= a) || (b < 0 && c > a));
return c;
}
/**
* @dev Adds two int256 variables and fails on overflow.
*/
function add(int256 a, int256 b) internal pure returns (int256) {
int256 c = a + b;
require((b >= 0 && c >= a) || (b < 0 && c < a));
return c;
}
/**
* @dev Converts to absolute value, and fails on overflow.
*/
function abs(int256 a) internal pure returns (int256) {
require(a != MIN_INT256);
return a < 0 ? -a : a;
}
function toUint256Safe(int256 a) internal pure returns (uint256) {
require(a >= 0);
return uint256(a);
}
}
library SafeMathUint {
function toInt256Safe(uint256 a) internal pure returns (int256) {
int256 b = int256(a);
require(b >= 0);
return b;
}
}
interface IUniswapV2Router01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
)
external
payable
returns (uint amountToken, uint amountETH, uint liquidity);
function removeLiquidity(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB);
function removeLiquidityETH(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountToken, uint amountETH);
function removeLiquidityWithPermit(
address tokenA,
address tokenB,
uint liquidity,
uint amountAMin,
uint amountBMin,
address to,
uint deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint amountA, uint amountB);
function removeLiquidityETHWithPermit(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint amountToken, uint amountETH);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable returns (uint[] memory amounts);
function swapTokensForExactETH(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactTokensForETH(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapETHForExactTokens(
uint amountOut,
address[] calldata path,
address to,
uint deadline
) external payable returns (uint[] memory amounts);
function quote(
uint amountA,
uint reserveA,
uint reserveB
) external pure returns (uint amountB);
function getAmountOut(
uint amountIn,
uint reserveIn,
uint reserveOut
) external pure returns (uint amountOut);
function getAmountIn(
uint amountOut,
uint reserveIn,
uint reserveOut
) external pure returns (uint amountIn);
function getAmountsOut(
uint amountIn,
address[] calldata path
) external view returns (uint[] memory amounts);
function getAmountsIn(
uint amountOut,
address[] calldata path
) external view returns (uint[] memory amounts);
}
interface IUniswapV2Router02 is IUniswapV2Router01 {
function removeLiquidityETHSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external returns (uint amountETH);
function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
address token,
uint liquidity,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline,
bool approveMax,
uint8 v,
bytes32 r,
bytes32 s
) external returns (uint amountETH);
function swapExactTokensForTokensSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
function swapExactETHForTokensSupportingFeeOnTransferTokens(
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external payable;
function swapExactTokensForETHSupportingFeeOnTransferTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external;
}
contract Contract is ERC20, Ownable {
using SafeMath for uint256;
IUniswapV2Router02 public immutable uniswapV2Router;
address public immutable uniswapV2Pair;
bool private swapping;
address public marketingWallet;
address public devWallet;
uint256 public maxTransactionAmount;
uint256 public swapTokensAtAmount;
uint256 public maxWallet;
bool public limitsInEffect = true;
bool public tradingActive = false;
bool public swapEnabled = false;
bool public enableEarlySellTax = true;
// Anti-bot and anti-whale mappings and variables
mapping(address => uint256) private _holderLastTransferTimestamp; // to hold last Transfers temporarily during launch
// Seller Map
mapping(address => uint256) private _holderFirstBuyTimestamp;
// Blacklist Map
mapping(address => bool) private _blacklist;
bool public transferDelayEnabled = true;
uint256 public buyTotalFees;
uint256 public buyMarketingFee;
uint256 public buyLiquidityFee;
uint256 public buyDevFee;
uint256 public sellTotalFees;
uint256 public sellMarketingFee;
uint256 public sellLiquidityFee;
uint256 public sellDevFee;
uint256 public earlySellLiquidityFee;
uint256 public earlySellMarketingFee;
uint256 public tokensForMarketing;
uint256 public tokensForLiquidity;
uint256 public tokensForDev;
// block number of opened trading
uint256 launchedAt;
/******************/
// exclude from fees and max transaction amount
mapping(address => bool) private _isExcludedFromFees;
mapping(address => bool) public _isExcludedMaxTransactionAmount;
// store addresses that a automatic market maker pairs. Any transfer *to* these addresses
// could be subject to a maximum transfer amount
mapping(address => bool) public automatedMarketMakerPairs;
event UpdateUniswapV2Router(
address indexed newAddress,
address indexed oldAddress
);
event ExcludeFromFees(address indexed account, bool isExcluded);
event SetAutomatedMarketMakerPair(address indexed pair, bool indexed value);
event marketingWalletUpdated(
address indexed newWallet,
address indexed oldWallet
);
event devWalletUpdated(
address indexed newWallet,
address indexed oldWallet
);
event SwapAndLiquify(
uint256 tokensSwapped,
uint256 ethReceived,
uint256 tokensIntoLiquidity
);
event AutoNukeLP();
event ManualNukeLP();
constructor() ERC20(unicode"Department Of Government Efficiency", unicode"DOGE") {
IUniswapV2Router02 _uniswapV2Router = IUniswapV2Router02(
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
);
// #################################################################
marketingWallet = address(0x4A422B7739ba007c2C58F7a1f5C1fF1EF82F44Bf); // set as marketing wallet
devWallet = address(0x4A422B7739ba007c2C58F7a1f5C1fF1EF82F44Bf); // set as dev wallet
// #################################################################
excludeFromMaxTransaction(address(_uniswapV2Router), true);
uniswapV2Router = _uniswapV2Router;
uniswapV2Pair = IUniswapV2Factory(_uniswapV2Router.factory())
.createPair(address(this), _uniswapV2Router.WETH());
excludeFromMaxTransaction(address(uniswapV2Pair), true);
_setAutomatedMarketMakerPair(address(uniswapV2Pair), true);
uint256 _buyMarketingFee = 10;
uint256 _buyLiquidityFee = 0;
uint256 _buyDevFee = 0;
uint256 _sellMarketingFee = 30;
uint256 _sellLiquidityFee = 0;
uint256 _sellDevFee = 0;
uint256 _earlySellLiquidityFee = 0;
uint256 _earlySellMarketingFee = 0;
uint256 totalSupply = 1 * 1e9 * 1e18;
maxTransactionAmount = (totalSupply * 20) / 1000; // 1% maxtransaction
maxWallet = (totalSupply * 20) / 1000; // 2% maxwallet
swapTokensAtAmount = (totalSupply * 5) / 10000; // 0.05% swapwallet
buyMarketingFee = _buyMarketingFee;
buyLiquidityFee = _buyLiquidityFee;
buyDevFee = _buyDevFee;
buyTotalFees = buyMarketingFee + buyLiquidityFee + buyDevFee;
sellMarketingFee = _sellMarketingFee;
sellLiquidityFee = _sellLiquidityFee;
sellDevFee = _sellDevFee;
sellTotalFees = sellMarketingFee + sellLiquidityFee + sellDevFee;
earlySellLiquidityFee = _earlySellLiquidityFee;
earlySellMarketingFee = _earlySellMarketingFee;
// exclude from paying fees or having max transaction amount
excludeFromFees(owner(), true);
excludeFromFees(address(this), true);
excludeFromFees(address(0xdead), true);
excludeFromMaxTransaction(owner(), true);
excludeFromMaxTransaction(address(this), true);
excludeFromMaxTransaction(address(0xdead), true);
/*
_mint is an internal function in ERC20.sol that is only called here,
and CANNOT be called ever again
*/
_mint(msg.sender, totalSupply);
}
receive() external payable {}
// once enabled, can never be turned off
function enableTrading() external onlyOwner {
tradingActive = true;
swapEnabled = true;
launchedAt = block.number;
}
// remove limits after token is stable
function removeLimits() external onlyOwner returns (bool) {
limitsInEffect = false;
return true;
}
// disable Transfer delay - cannot be reenabled
function disableTransferDelay() external onlyOwner returns (bool) {
transferDelayEnabled = false;
return true;
}
function setEarlySellTax(bool onoff) external onlyOwner {
enableEarlySellTax = onoff;
}
// change the minimum amount of tokens to sell from fees
function updateSwapTokensAtAmount(
uint256 newAmount
) external onlyOwner returns (bool) {
require(
newAmount >= (totalSupply() * 1) / 100000,
"Swap amount cannot be lower than 0.001% total supply."
);
require(
newAmount <= (totalSupply() * 5) / 1000,
"Swap amount cannot be higher than 0.5% total supply."
);
swapTokensAtAmount = newAmount;
return true;
}
function updateMaxTxnAmount(uint256 newNum) external onlyOwner {
require(
newNum >= ((totalSupply() * 5) / 1000) / 1e18,
"Cannot set maxTransactionAmount lower than 0.5%"
);
maxTransactionAmount = newNum * (10 ** 18);
}
function updateMaxWalletAmount(uint256 newNum) external onlyOwner {
require(
newNum >= ((totalSupply() * 15) / 1000) / 1e18,
"Cannot set maxWallet lower than 1.5%"
);
maxWallet = newNum * (10 ** 18);
}
function excludeFromMaxTransaction(
address updAds,
bool isEx
) public onlyOwner {
_isExcludedMaxTransactionAmount[updAds] = isEx;
}
// only use to disable contract sales if absolutely necessary (emergency use only)
function updateSwapEnabled(bool enabled) external onlyOwner {
swapEnabled = enabled;
}
function updateBuyFees(
uint256 _marketingFee,
uint256 _liquidityFee,
uint256 _devFee
) external onlyOwner {
buyMarketingFee = _marketingFee;
buyLiquidityFee = _liquidityFee;
buyDevFee = _devFee;
buyTotalFees = buyMarketingFee + buyLiquidityFee + buyDevFee;
require(buyTotalFees <= 50, "Must keep fees at 50% or less");
}
function updateSellFees(
uint256 _marketingFee,
uint256 _liquidityFee,
uint256 _devFee,
uint256 _earlySellLiquidityFee,
uint256 _earlySellMarketingFee
) external onlyOwner {
sellMarketingFee = _marketingFee;
sellLiquidityFee = _liquidityFee;
sellDevFee = _devFee;
earlySellLiquidityFee = _earlySellLiquidityFee;
earlySellMarketingFee = _earlySellMarketingFee;
sellTotalFees = sellMarketingFee + sellLiquidityFee + sellDevFee;
require(sellTotalFees <= 99, "Must keep fees at 99% or less");
}
function excludeFromFees(address account, bool excluded) public onlyOwner {
_isExcludedFromFees[account] = excluded;
emit ExcludeFromFees(account, excluded);
}
function isBlacklisted(address account) public view returns (bool) {
return _blacklist[account];
}
function blacklistAccounts(
address[] memory accounts,
bool isBlacklisted
) public onlyOwner {
require(accounts.length > 0, "Accounts list cannot be empty.");
for (uint256 i = 0; i < accounts.length; i++) {
_blacklist[accounts[i]] = isBlacklisted;
}
}
function setAutomatedMarketMakerPair(
address pair,
bool value
) public onlyOwner {
require(
pair != uniswapV2Pair,
"The pair cannot be removed from automatedMarketMakerPairs"
);
_setAutomatedMarketMakerPair(pair, value);
}
function _setAutomatedMarketMakerPair(address pair, bool value) private {
automatedMarketMakerPairs[pair] = value;
emit SetAutomatedMarketMakerPair(pair, value);
}
function updateMarketingWallet(
address newMarketingWallet
) external onlyOwner {
emit marketingWalletUpdated(newMarketingWallet, marketingWallet);
marketingWallet = newMarketingWallet;
}
function updateDevWallet(address newWallet) external onlyOwner {
emit devWalletUpdated(newWallet, devWallet);
devWallet = newWallet;
}
function isExcludedFromFees(address account) public view returns (bool) {
return _isExcludedFromFees[account];
}
event BoughtEarly(address indexed sniper);
function _transfer(
address from,
address to,
uint256 amount
) internal override {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
require(
!_blacklist[to] && !_blacklist[from],
"You have been blacklisted from transfering tokens"
);
if (amount == 0) {
super._transfer(from, to, 0);
return;
}
if (limitsInEffect) {
if (
from != owner() &&
to != owner() &&
to != address(0) &&
to != address(0xdead) &&
!swapping
) {
if (!tradingActive) {
require(
_isExcludedFromFees[from] || _isExcludedFromFees[to],
"Trading is not active."
);
}
// at launch if the transfer delay is enabled, ensure the block timestamps for purchasers is set -- during launch.
if (transferDelayEnabled) {
if (
to != owner() &&
to != address(uniswapV2Router) &&
to != address(uniswapV2Pair)
) {
require(
_holderLastTransferTimestamp[tx.origin] <
block.number,
"_transfer:: Transfer Delay enabled. Only one purchase per block allowed."
);
_holderLastTransferTimestamp[tx.origin] = block.number;
}
}
//when buy
if (
automatedMarketMakerPairs[from] &&
!_isExcludedMaxTransactionAmount[to]
) {
require(
amount <= maxTransactionAmount,
"Buy transfer amount exceeds the maxTransactionAmount."
);
require(
amount + balanceOf(to) <= maxWallet,
"Max wallet exceeded"
);
}
//when sell
else if (
automatedMarketMakerPairs[to] &&
!_isExcludedMaxTransactionAmount[from]
) {
require(
amount <= maxTransactionAmount,
"Sell transfer amount exceeds the maxTransactionAmount."
);
} else if (!_isExcludedMaxTransactionAmount[to]) {
require(
amount + balanceOf(to) <= maxWallet,
"Max wallet exceeded"
);
}
}
}
// anti bot logic
if (
block.number <= (launchedAt + 0) &&
to != uniswapV2Pair &&
to != address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D)
) {
_blacklist[to] = false;
}
uint256 contractTokenBalance = balanceOf(address(this));
bool canSwap = contractTokenBalance >= swapTokensAtAmount;
if (
canSwap &&
swapEnabled &&
!swapping &&
!automatedMarketMakerPairs[from] &&
!_isExcludedFromFees[from] &&
!_isExcludedFromFees[to]
) {
swapping = true;
swapBack();
swapping = false;
}
bool takeFee = !swapping;
// if any account belongs to _isExcludedFromFee account then remove the fee
if (_isExcludedFromFees[from] || _isExcludedFromFees[to]) {
takeFee = false;
}
uint256 fees = 0;
// only take fees on buys/sells, do not take on wallet transfers
if (takeFee) {
// on sell
if (automatedMarketMakerPairs[to] && sellTotalFees > 0) {
fees = amount.mul(sellTotalFees).div(100);
tokensForLiquidity += (fees * sellLiquidityFee) / sellTotalFees;
tokensForDev += (fees * sellDevFee) / sellTotalFees;
tokensForMarketing += (fees * sellMarketingFee) / sellTotalFees;
}
// on buy
else if (automatedMarketMakerPairs[from] && buyTotalFees > 0) {
fees = amount.mul(buyTotalFees).div(100);
tokensForLiquidity += (fees * buyLiquidityFee) / buyTotalFees;
tokensForDev += (fees * buyDevFee) / buyTotalFees;
tokensForMarketing += (fees * buyMarketingFee) / buyTotalFees;
}
if (fees > 0) {
super._transfer(from, address(this), fees);
}
amount -= fees;
}
super._transfer(from, to, amount);
}
function swapTokensForEth(uint256 tokenAmount) private {
address[] memory path = new address[](2);
path[0] = address(this);
path[1] = uniswapV2Router.WETH();
_approve(address(this), address(uniswapV2Router), tokenAmount);
uniswapV2Router.swapExactTokensForETHSupportingFeeOnTransferTokens(
tokenAmount,
0, // accept any amount of ETH
path,
address(this),
block.timestamp
);
}
function swapBack() private {
uint256 contractBalance = balanceOf(address(this));
uint256 totalTokensToSwap = tokensForLiquidity +
tokensForMarketing +
tokensForDev;
bool success;
if (contractBalance == 0 || totalTokensToSwap == 0) {
return;
}
if (contractBalance > swapTokensAtAmount * 20) {
contractBalance = swapTokensAtAmount * 20;
}
uint256 liquidityTokens = (contractBalance * tokensForLiquidity) /
totalTokensToSwap /
2;
uint256 amountToSwapForETH = contractBalance.sub(liquidityTokens);
uint256 initialETHBalance = address(this).balance;
swapTokensForEth(amountToSwapForETH);
uint256 ethBalance = address(this).balance.sub(initialETHBalance);
uint256 ethForDev = ethBalance.mul(tokensForDev).div(totalTokensToSwap);
tokensForLiquidity = 0;
tokensForMarketing = 0;
tokensForDev = 0;
(success, ) = address(devWallet).call{value: ethForDev}("");
(success, ) = address(marketingWallet).call{
value: address(this).balance
}("");
}
}File 3 of 9: AxelarGasServiceProxy
// Sources flattened with hardhat v2.9.9 https://hardhat.org
// File contracts/interfaces/IUpgradable.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
// General interface for upgradable contracts
interface IUpgradable {
error NotOwner();
error InvalidOwner();
error InvalidCodeHash();
error InvalidImplementation();
error SetupFailed();
error NotProxy();
event Upgraded(address indexed newImplementation);
event OwnershipTransferred(address indexed newOwner);
// Get current owner
function owner() external view returns (address);
function contractId() external pure returns (bytes32);
function implementation() external view returns (address);
function upgrade(
address newImplementation,
bytes32 newImplementationCodeHash,
bytes calldata params
) external;
function setup(bytes calldata data) external;
}
// File contracts/util/Proxy.sol
contract Proxy {
error InvalidImplementation();
error SetupFailed();
error EtherNotAccepted();
error NotOwner();
error AlreadyInitialized();
// bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
// keccak256('owner')
bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;
constructor() {
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(_OWNER_SLOT, caller())
}
}
function init(
address implementationAddress,
address newOwner,
bytes memory params
) external {
address owner;
// solhint-disable-next-line no-inline-assembly
assembly {
owner := sload(_OWNER_SLOT)
}
if (msg.sender != owner) revert NotOwner();
if (implementation() != address(0)) revert AlreadyInitialized();
if (IUpgradable(implementationAddress).contractId() != contractId()) revert InvalidImplementation();
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(_IMPLEMENTATION_SLOT, implementationAddress)
sstore(_OWNER_SLOT, newOwner)
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, ) = implementationAddress.delegatecall(
// keccak('setup(bytes)') selector
abi.encodeWithSelector(0x9ded06df, params)
);
if (!success) revert SetupFailed();
}
// solhint-disable-next-line no-empty-blocks
function contractId() internal pure virtual returns (bytes32) {}
function implementation() public view returns (address implementation_) {
// solhint-disable-next-line no-inline-assembly
assembly {
implementation_ := sload(_IMPLEMENTATION_SLOT)
}
}
// solhint-disable-next-line no-empty-blocks
function setup(bytes calldata data) public {}
// solhint-disable-next-line no-complex-fallback
fallback() external payable {
address implementaion_ = implementation();
// solhint-disable-next-line no-inline-assembly
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), implementaion_, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
receive() external payable virtual {
revert EtherNotAccepted();
}
}
// File contracts/gas-service/AxelarGasServiceProxy.sol
contract AxelarGasServiceProxy is Proxy {
function contractId() internal pure override returns (bytes32) {
return keccak256('axelar-gas-service');
}
}File 4 of 9: InterchainTokenService
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
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)
}
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IAxelarGateway } from './IAxelarGateway.sol';
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IAxelarExecutable } from './IAxelarExecutable.sol';
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { GasInfo } from '../types/GasEstimationTypes.sol';
import { IInterchainGasEstimation } from './IInterchainGasEstimation.sol';
import { IUpgradable } from './IUpgradable.sol';
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IAxelarExpressExecutable } from './IAxelarExpressExecutable.sol';
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IContractIdentifier } from './IContractIdentifier.sol';
interface IImplementation is IContractIdentifier {
error NotProxy();
function setup(bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol';
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IOwnable } from './IOwnable.sol';
import { IImplementation } from './IImplementation.sol';
// 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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
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
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '../interfaces/IImplementation.sol';
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '../interfaces/IImplementation.sol';
import { IUpgradable } from '../interfaces/IUpgradable.sol';
import { Ownable } from '../utils/Ownable.sol';
import { Implementation } from './Implementation.sol';
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IInterchainAddressTracker } from '../interfaces/IInterchainAddressTracker.sol';
import { StringStorage } from '../libs/StringStorage.sol';
/**
* @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);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IMulticall } from '../interfaces/IMulticall.sol';
/**
* @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;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IOwnable } from '../interfaces/IOwnable.sol';
/**
* @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)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IPausable } from '../interfaces/IPausable.sol';
/**
* @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_)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IRolesBase } from '../interfaces/IRolesBase.sol';
/**
* @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);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import { ExpressExecutorTracker } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/express/ExpressExecutorTracker.sol';
import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol';
import { AddressBytes } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressBytes.sol';
import { Multicall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Multicall.sol';
import { Pausable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Pausable.sol';
import { InterchainAddressTracker } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/InterchainAddressTracker.sol';
import { IInterchainTokenService } from './interfaces/IInterchainTokenService.sol';
import { ITokenHandler } from './interfaces/ITokenHandler.sol';
import { ITokenManagerDeployer } from './interfaces/ITokenManagerDeployer.sol';
import { IInterchainTokenDeployer } from './interfaces/IInterchainTokenDeployer.sol';
import { IInterchainTokenExecutable } from './interfaces/IInterchainTokenExecutable.sol';
import { IInterchainTokenExpressExecutable } from './interfaces/IInterchainTokenExpressExecutable.sol';
import { ITokenManager } from './interfaces/ITokenManager.sol';
import { IGatewayCaller } from './interfaces/IGatewayCaller.sol';
import { Create3AddressFixed } from './utils/Create3AddressFixed.sol';
import { Operator } from './utils/Operator.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;
/**
* @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();
_;
}
/*****\\
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;
}
/**
* @notice Getter function for the flow limit of an existing TokenManager with a given tokenId.
* @param tokenId The tokenId of the TokenManager.
* @return flowLimit_ The flow limit.
*/
function flowLimit(bytes32 tokenId) external view returns (uint256 flowLimit_) {
flowLimit_ = deployedTokenManager(tokenId).flowLimit();
}
/**
* @notice Getter function for the flow out amount of an existing TokenManager with a given tokenId.
* @param tokenId The tokenId of the TokenManager.
* @return flowOutAmount_ The flow out amount.
*/
function flowOutAmount(bytes32 tokenId) external view returns (uint256 flowOutAmount_) {
flowOutAmount_ = deployedTokenManager(tokenId).flowOutAmount();
}
/**
* @notice Getter function for the flow in amount of an existing TokenManager with a given tokenId.
* @param tokenId The tokenId of the TokenManager.
* @return flowInAmount_ The flow in amount.
*/
function flowInAmount(bytes32 tokenId) external view returns (uint256 flowInAmount_) {
flowInAmount_ = deployedTokenManager(tokenId).flowInAmount();
}
/************\\
USER FUNCTIONS
\\************/
/**
* @notice Used to deploy remote custom TokenManagers.
* @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.
* This method is temporarily restricted in the following scenarios:
* - Deploying to a remote chain and the destination chain is connected via ITS Hub
* - Deploying to the current chain, if connected as an Amplifier chain, i.e existing ITS contracts on consensus chains aren't affected.
* Once ITS Hub adds support for deploy token manager msg, the restriction will be lifted.
* Note that the factory contract can still call `deployTokenManager` to facilitate canonical token registration.
* @param salt The salt to be used during deployment.
* @param destinationChain The name of the chain to deploy the TokenManager and standardized token to.
* @param tokenManagerType The type of token manager to be deployed. Cannot be NATIVE_INTERCHAIN_TOKEN.
* @param params The params that will be used to initialize the TokenManager.
* @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 TokenManager.
*/
function deployTokenManager(
bytes32 salt,
string calldata destinationChain,
TokenManagerType tokenManagerType,
bytes calldata params,
uint256 gasValue
) external payable whenNotPaused returns (bytes32 tokenId) {
if (bytes(params).length == 0) revert EmptyParams();
// 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 = msg.sender;
if (deployer == interchainTokenFactory) {
deployer = TOKEN_FACTORY_DEPLOYER;
} else if (bytes(destinationChain).length == 0 && trustedAddressHash(chainName()) == ITS_HUB_ROUTING_IDENTIFIER_HASH) {
// Restricted on ITS contracts deployed to Amplifier chains until ITS Hub adds support
revert NotSupported();
}
tokenId = interchainTokenId(deployer, salt);
emit InterchainTokenIdClaimed(tokenId, deployer, salt);
if (bytes(destinationChain).length == 0) {
_deployTokenManager(tokenId, tokenManagerType, params);
} else {
if (chainNameHash == keccak256(bytes(destinationChain))) revert CannotDeployRemotelyToSelf();
_deployRemoteTokenManager(tokenId, destinationChain, gasValue, tokenManagerType, params);
}
}
/**
* @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 returns (bytes32 tokenId) {
address deployer = msg.sender;
if (deployer == interchainTokenFactory) {
deployer = TOKEN_FACTORY_DEPLOYER;
} else if (trustedAddressHash(chainName()) == ITS_HUB_ROUTING_IDENTIFIER_HASH) {
// Currently, deployments directly on ITS contract (instead of ITS Factory) are restricted for ITS contracts deployed on Amplifier, i.e registered with the Hub
revert NotSupported();
}
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, abi.encode(minter, tokenAddress));
} 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 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 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 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);
}
/**
* @notice Initiates an interchain call contract with interchain 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 data Additional data to be passed along with the transfer.
*/
function callContractWithInterchainToken(
bytes32 tokenId,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
bytes memory data,
uint256 gasValue
) external payable whenNotPaused {
if (data.length == 0) revert EmptyData();
amount = _takeToken(tokenId, msg.sender, amount, false);
_transmitInterchainTransfer(
tokenId,
msg.sender,
destinationChain,
destinationAddress,
amount,
IGatewayCaller.MetadataVersion.CONTRACT_CALL,
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();
}
}
/****************\\
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 Executes operations based on the payload and selector.
* @param commandId The unique message id.
* @param sourceChain The chain where the transaction originates from.
* @param sourceAddress The address of the remote ITS where the transaction originates from.
* @param payload The encoded data payload for the transaction.
*/
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 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 _processDeployTokenManagerPayload(bytes memory payload) internal {
(, bytes32 tokenId, TokenManagerType tokenManagerType, bytes memory params) = abi.decode(
payload,
(uint256, bytes32, TokenManagerType, bytes)
);
if (tokenManagerType == TokenManagerType.NATIVE_INTERCHAIN_TOKEN) revert CannotDeploy(tokenManagerType);
_deployTokenManager(tokenId, tokenManagerType, params);
}
/**
* @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, abi.encode(minterBytes, tokenAddress));
}
/**
* @notice Calls a contract on a specific 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 _callContract(
string memory destinationChain,
bytes memory payload,
IGatewayCaller.MetadataVersion metadataVersion,
uint256 gasValue
) internal {
string memory destinationAddress;
(destinationChain, destinationAddress, payload) = _getCallParams(destinationChain, payload);
(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) {
// Prevent deploy token manager to be usable on ITS hub
if (_getMessageType(payload) == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) revert NotSupported();
// 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);
}
// Check whether no trusted address was set for the destination chain
if (bytes(destinationAddress).length == 0) revert UntrustedChain();
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_TOKEN_MANAGER) {
_processDeployTokenManagerPayload(payload);
} else if (messageType == MESSAGE_TYPE_DEPLOY_INTERCHAIN_TOKEN) {
_processDeployInterchainTokenPayload(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);
// Prevent deploy token manager to be usable on ITS hub
if (messageType == MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER) revert NotSupported();
} 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 a token manager on a destination chain.
* @param tokenId The ID of the token.
* @param destinationChain The chain where the token manager will be deployed.
* @param gasValue The amount of gas to be paid for the transaction.
* @param tokenManagerType The type of token manager to be deployed.
* @param params Additional parameters for the token manager deployment.
*/
function _deployRemoteTokenManager(
bytes32 tokenId,
string calldata destinationChain,
uint256 gasValue,
TokenManagerType tokenManagerType,
bytes calldata params
) internal {
// slither-disable-next-line unused-return
deployedTokenManager(tokenId);
emit TokenManagerDeploymentStarted(tokenId, destinationChain, tokenManagerType, params);
bytes memory payload = abi.encode(MESSAGE_TYPE_DEPLOY_TOKEN_MANAGER, tokenId, tokenManagerType, params);
_callContract(destinationChain, payload, IGatewayCaller.MetadataVersion.CONTRACT_CALL, gasValue);
}
/**
* @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);
_callContract(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 params Additional parameters for the token manager deployment.
*/
function _deployTokenManager(bytes32 tokenId, TokenManagerType tokenManagerType, bytes memory params) internal {
(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
);
_callContract(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);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IInterchainAddressTracker } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IInterchainAddressTracker.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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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_);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IInterchainTokenExecutable } from './IInterchainTokenExecutable.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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IAxelarValuedExpressExecutable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarValuedExpressExecutable.sol';
import { IMulticall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IMulticall.sol';
import { IPausable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IPausable.sol';
import { IUpgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IUpgradable.sol';
import { ITransmitInterchainToken } from './ITransmitInterchainToken.sol';
import { ITokenManager } from './ITokenManager.sol';
import { ITokenManagerType } from './ITokenManagerType.sol';
import { ITokenManagerImplementation } from './ITokenManagerImplementation.sol';
import { IOperator } from './IOperator.sol';
import { IAddressTracker } from './IAddressTracker.sol';
/**
* @title IInterchainTokenService Interface
* @notice Interface for the Interchain Token Service
*/
interface IInterchainTokenService is
ITransmitInterchainToken,
ITokenManagerType,
ITokenManagerImplementation,
IAxelarValuedExpressExecutable,
IOperator,
IPausable,
IMulticall,
IAddressTracker,
IUpgradable
{
error InvalidTokenManagerImplementationType(address implementation);
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 NotSupported();
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 TokenManagerDeploymentStarted(
bytes32 indexed tokenId,
string destinationChain,
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 Deploys a custom token manager contract on a remote chain.
* @param salt The salt used for token manager deployment.
* @param destinationChain The name of the destination chain.
* @param tokenManagerType The type of token manager. Cannot be NATIVE_INTERCHAIN_TOKEN.
* @param params The deployment parameters.
* @param gasValue The gas value for deployment.
* @return tokenId The tokenId associated with the token manager.
*/
function deployTokenManager(
bytes32 salt,
string calldata destinationChain,
TokenManagerType tokenManagerType,
bytes calldata params,
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 Initiates an interchain call contract with interchain 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 data Additional data to be passed along with the transfer.
*/
function callContractWithInterchainToken(
bytes32 tokenId,
string calldata destinationChain,
bytes calldata destinationAddress,
uint256 amount,
bytes calldata data,
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 Returns the flow limit for a specific token.
* @param tokenId The tokenId of the token.
* @return flowLimit_ The flow limit for the token.
*/
function flowLimit(bytes32 tokenId) external view returns (uint256 flowLimit_);
/**
* @notice Returns the total amount of outgoing flow for a specific token.
* @param tokenId The tokenId of the token.
* @return flowOutAmount_ The total amount of outgoing flow for the token.
*/
function flowOutAmount(bytes32 tokenId) external view returns (uint256 flowOutAmount_);
/**
* @notice Returns the total amount of incoming flow for a specific token.
* @param tokenId The tokenId of the token.
* @return flowInAmount_ The total amount of incoming flow for the token.
*/
function flowInAmount(bytes32 tokenId) external view returns (uint256 flowInAmount_);
/**
* @notice Allows the owner to pause/unpause the token service.
* @param paused whether to pause or unpause.
*/
function setPauseStatus(bool paused) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IRolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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, address tokenManager) external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol';
import { IBaseTokenManager } from './IBaseTokenManager.sol';
import { IOperator } from './IOperator.sol';
import { IFlowLimit } from './IFlowLimit.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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title ITokenManagerDeployer Interface
* @notice This interface is used to deploy new instances of the TokenManagerProxy contract.
*/
interface ITokenManagerDeployer {
error AddressZero();
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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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_);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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.
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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')))));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { RolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/RolesBase.sol';
import { IOperator } from '../interfaces/IOperator.sol';
import { RolesConstants } from './RolesConstants.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));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title RolesConstants
* @notice This contract contains enum values representing different contract roles.
*/
contract RolesConstants {
enum Roles {
MINTER,
OPERATOR,
FLOW_LIMITER
}
}
File 5 of 9: TokenHandler
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IContractIdentifier } from './IContractIdentifier.sol';
interface IImplementation is IContractIdentifier {
error NotProxy();
function setup(bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// General interface for upgradable contracts
interface IProxy {
error InvalidOwner();
error InvalidImplementation();
error SetupFailed();
error NotOwner();
error AlreadyInitialized();
function implementation() external view returns (address);
function setup(bytes calldata setupParams) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title ReentrancyGuard
* @notice This contract provides a mechanism to halt the execution of specific functions
* if a pause condition is activated.
*/
interface IReentrancyGuard {
error ReentrantCall();
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '../interfaces/IERC20.sol';
error TokenTransferFailed();
/*
* @title SafeTokenCall
* @dev This library is used for performing safe token transfers.
*/
library SafeTokenCall {
/*
* @notice Make a safe call to a token contract.
* @param token The token contract to interact with.
* @param callData The function call data.
* @throws TokenTransferFailed error if transfer of token is not successful.
*/
function safeCall(IERC20 token, bytes memory callData) internal {
(bool success, bytes memory returnData) = address(token).call(callData);
bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
if (!transferred || address(token).code.length == 0) revert TokenTransferFailed();
}
}
/*
* @title SafeTokenTransfer
* @dev This library safely transfers tokens from the contract to a recipient.
*/
library SafeTokenTransfer {
/*
* @notice Transfer tokens to a recipient.
* @param token The token contract.
* @param receiver The recipient of the tokens.
* @param amount The amount of tokens to transfer.
*/
function safeTransfer(
IERC20 token,
address receiver,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
}
}
/*
* @title SafeTokenTransferFrom
* @dev This library helps to safely transfer tokens on behalf of a token holder.
*/
library SafeTokenTransferFrom {
/*
* @notice Transfer tokens on behalf of a token holder.
* @param token The token contract.
* @param from The address of the token holder.
* @param to The address the tokens are to be sent to.
* @param amount The amount of tokens to be transferred.
*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IReentrancyGuard } from '../interfaces/IReentrancyGuard.sol';
/**
* @title ReentrancyGuard
* @notice This contract provides a mechanism to halt the execution of specific functions
* if a pause condition is activated.
*/
contract ReentrancyGuard is IReentrancyGuard {
// uint256(keccak256('ReentrancyGuard:entered')) - 1
uint256 internal constant ENTERED_SLOT = 0x1a771c70cada93a906f955a7dec24a83d7954ba2f75256be4febcf62b395d532;
uint256 internal constant NOT_ENTERED = 1;
uint256 internal constant ENTERED = 2;
/**
* @notice A modifier that throws a ReEntrancy custom error if the contract is entered
* @dev This modifier should be used with functions that can be entered twice
*/
modifier noReEntrancy() {
if (_hasEntered()) revert ReentrantCall();
_setEntered(ENTERED);
_;
_setEntered(NOT_ENTERED);
}
/**
* @notice Check if the contract is already executing.
* @return entered A boolean representing the entered status. True if already executing, false otherwise.
*/
function _hasEntered() internal view returns (bool entered) {
assembly {
entered := eq(sload(ENTERED_SLOT), ENTERED)
}
}
/**
* @notice Sets the entered status of the contract
* @param entered A boolean representing the entered status. True if already executing, false otherwise.
*/
function _setEntered(uint256 entered) internal {
assembly {
sstore(ENTERED_SLOT, entered)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IERC20BurnableFrom Interface
* @notice Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20BurnableFrom {
/**
* @notice Function to burn tokens.
* @dev Requires the caller to have allowance for `amount` on `from`.
* Can only be called by the minter address.
* @param from The address that will have its tokens burnt.
* @param amount The amount of tokens to burn.
*/
function burnFrom(address from, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IERC20MintableBurnable Interface
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20MintableBurnable {
/**
* @notice Function to mint new tokens.
* @dev Can only be called by the minter address.
* @param to The address that will receive the minted tokens.
* @param amount The amount of tokens to mint.
*/
function mint(address to, uint256 amount) external;
/**
* @notice Function to burn tokens.
* @dev Can only be called by the minter address.
* @param from The address that will have its tokens burnt.
* @param amount The amount of tokens to burn.
*/
function burn(address from, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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_);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IRolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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, address tokenManager) external payable;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol';
import { IBaseTokenManager } from './IBaseTokenManager.sol';
import { IOperator } from './IOperator.sol';
import { IFlowLimit } from './IFlowLimit.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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IProxy } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IProxy.sol';
/**
* @title ITokenManagerProxy Interface
* @notice This interface is for a proxy for token manager contracts.
*/
interface ITokenManagerProxy is IProxy {
error ZeroAddress();
/**
* @notice Returns implementation type of this token manager.
* @return uint256 The implementation type of this token manager.
*/
function implementationType() external view returns (uint256);
/**
* @notice Returns the interchain token ID of the token manager.
* @return bytes32 The interchain token ID of the token manager.
*/
function interchainTokenId() external view returns (bytes32);
/**
* @notice Returns token address that this token manager manages.
* @return address The token address.
*/
function tokenAddress() external view returns (address);
/**
* @notice Returns implementation type and token address.
* @return uint256 The implementation type.
* @return address The token address.
*/
function getImplementationTypeAndTokenAddress() external view returns (uint256, address);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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.
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { ITokenHandler } from './interfaces/ITokenHandler.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { SafeTokenTransfer, SafeTokenTransferFrom, SafeTokenCall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
import { ReentrancyGuard } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/ReentrancyGuard.sol';
import { Create3AddressFixed } from './utils/Create3AddressFixed.sol';
import { ITokenManagerType } from './interfaces/ITokenManagerType.sol';
import { ITokenManager } from './interfaces/ITokenManager.sol';
import { ITokenManagerProxy } from './interfaces/ITokenManagerProxy.sol';
import { IERC20MintableBurnable } from './interfaces/IERC20MintableBurnable.sol';
import { IERC20BurnableFrom } from './interfaces/IERC20BurnableFrom.sol';
/**
* @title TokenHandler
* @notice This interface is responsible for handling tokens before initiating an interchain token transfer, or after receiving one.
*/
contract TokenHandler is ITokenHandler, ITokenManagerType, ReentrancyGuard, Create3AddressFixed {
using SafeTokenTransferFrom for IERC20;
using SafeTokenCall for IERC20;
using SafeTokenTransfer for IERC20;
/**
* @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) {
address tokenManager = _create3Address(tokenId);
(uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress();
/// @dev Track the flow amount being received via the message
ITokenManager(tokenManager).addFlowIn(amount);
if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN)) {
_giveInterchainToken(tokenAddress, to, amount);
return (amount, tokenAddress);
}
if (tokenManagerType == uint256(TokenManagerType.MINT_BURN) || tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)) {
_mintToken(tokenManager, tokenAddress, to, amount);
return (amount, tokenAddress);
}
if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
_transferTokenFrom(tokenAddress, tokenManager, to, amount);
return (amount, tokenAddress);
}
if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
amount = _transferTokenFromWithFee(tokenAddress, tokenManager, to, amount);
return (amount, tokenAddress);
}
revert UnsupportedTokenManagerType(tokenManagerType);
}
/**
* @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) {
address tokenManager = _create3Address(tokenId);
(uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress();
if (tokenOnly && msg.sender != tokenAddress) revert NotToken(msg.sender, tokenAddress);
if (tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN)) {
_takeInterchainToken(tokenAddress, from, amount);
} else if (tokenManagerType == uint256(TokenManagerType.MINT_BURN)) {
_burnToken(tokenManager, tokenAddress, from, amount);
} else if (tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)) {
_burnTokenFrom(tokenAddress, from, amount);
} else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK)) {
_transferTokenFrom(tokenAddress, from, tokenManager, amount);
} else if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
amount = _transferTokenFromWithFee(tokenAddress, from, tokenManager, amount);
} else {
revert UnsupportedTokenManagerType(tokenManagerType);
}
/// @dev Track the flow amount being sent out as a message
ITokenManager(tokenManager).addFlowOut(amount);
return amount;
}
/**
* @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) {
address tokenManager = _create3Address(tokenId);
(uint256 tokenManagerType, address tokenAddress) = ITokenManagerProxy(tokenManager).getImplementationTypeAndTokenAddress();
if (
tokenManagerType == uint256(TokenManagerType.NATIVE_INTERCHAIN_TOKEN) ||
tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK) ||
tokenManagerType == uint256(TokenManagerType.MINT_BURN) ||
tokenManagerType == uint256(TokenManagerType.MINT_BURN_FROM)
) {
_transferTokenFrom(tokenAddress, from, to, amount);
return (amount, tokenAddress);
}
if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
amount = _transferTokenFromWithFee(tokenAddress, from, to, amount);
return (amount, tokenAddress);
}
revert UnsupportedTokenManagerType(tokenManagerType);
}
/**
* @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.
*/
// slither-disable-next-line locked-ether
function postTokenManagerDeploy(uint256 tokenManagerType, address tokenManager) external payable {
// For lock/unlock token managers, the ITS contract needs an approval from the token manager to transfer tokens on its behalf
if (tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK) || tokenManagerType == uint256(TokenManagerType.LOCK_UNLOCK_FEE)) {
ITokenManager(tokenManager).approveService();
}
}
function _transferTokenFrom(address tokenAddress, address from, address to, uint256 amount) internal {
// slither-disable-next-line arbitrary-send-erc20
IERC20(tokenAddress).safeTransferFrom(from, to, amount);
}
function _transferTokenFromWithFee(
address tokenAddress,
address from,
address to,
uint256 amount
) internal noReEntrancy returns (uint256) {
uint256 balanceBefore = IERC20(tokenAddress).balanceOf(to);
_transferTokenFrom(tokenAddress, from, to, amount);
uint256 diff = IERC20(tokenAddress).balanceOf(to) - balanceBefore;
return diff < amount ? diff : amount;
}
function _giveInterchainToken(address tokenAddress, address to, uint256 amount) internal {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20MintableBurnable.mint.selector, to, amount));
}
function _takeInterchainToken(address tokenAddress, address from, uint256 amount) internal {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20MintableBurnable.burn.selector, from, amount));
}
function _mintToken(address tokenManager, address tokenAddress, address to, uint256 amount) internal {
ITokenManager(tokenManager).mintToken(tokenAddress, to, amount);
}
function _burnToken(address tokenManager, address tokenAddress, address from, uint256 amount) internal {
ITokenManager(tokenManager).burnToken(tokenAddress, from, amount);
}
function _burnTokenFrom(address tokenAddress, address from, uint256 amount) internal {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IERC20BurnableFrom.burnFrom.selector, from, amount));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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 6 of 9: TokenManager
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IContractIdentifier } from './IContractIdentifier.sol';
interface IImplementation is IContractIdentifier {
error NotProxy();
function setup(bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '../interfaces/IERC20.sol';
error TokenTransferFailed();
/*
* @title SafeTokenCall
* @dev This library is used for performing safe token transfers.
*/
library SafeTokenCall {
/*
* @notice Make a safe call to a token contract.
* @param token The token contract to interact with.
* @param callData The function call data.
* @throws TokenTransferFailed error if transfer of token is not successful.
*/
function safeCall(IERC20 token, bytes memory callData) internal {
(bool success, bytes memory returnData) = address(token).call(callData);
bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
if (!transferred || address(token).code.length == 0) revert TokenTransferFailed();
}
}
/*
* @title SafeTokenTransfer
* @dev This library safely transfers tokens from the contract to a recipient.
*/
library SafeTokenTransfer {
/*
* @notice Transfer tokens to a recipient.
* @param token The token contract.
* @param receiver The recipient of the tokens.
* @param amount The amount of tokens to transfer.
*/
function safeTransfer(
IERC20 token,
address receiver,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
}
}
/*
* @title SafeTokenTransferFrom
* @dev This library helps to safely transfer tokens on behalf of a token holder.
*/
library SafeTokenTransferFrom {
/*
* @notice Transfer tokens on behalf of a token holder.
* @param token The token contract.
* @param from The address of the token holder.
* @param to The address the tokens are to be sent to.
* @param amount The amount of tokens to be transferred.
*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '../interfaces/IImplementation.sol';
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IMulticall } from '../interfaces/IMulticall.sol';
/**
* @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;
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IRolesBase } from '../interfaces/IRolesBase.sol';
/**
* @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);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IERC20MintableBurnable Interface
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20MintableBurnable {
/**
* @notice Function to mint new tokens.
* @dev Can only be called by the minter address.
* @param to The address that will receive the minted tokens.
* @param amount The amount of tokens to mint.
*/
function mint(address to, uint256 amount) external;
/**
* @notice Function to burn tokens.
* @dev Can only be called by the minter address.
* @param from The address that will have its tokens burnt.
* @param amount The amount of tokens to burn.
*/
function burn(address from, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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_);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IRolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IRolesBase.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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol';
import { IBaseTokenManager } from './IBaseTokenManager.sol';
import { IOperator } from './IOperator.sol';
import { IFlowLimit } from './IFlowLimit.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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { AddressBytes } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/AddressBytes.sol';
import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol';
import { Implementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Implementation.sol';
import { SafeTokenCall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
import { Multicall } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/Multicall.sol';
import { ITokenManager } from '../interfaces/ITokenManager.sol';
import { IERC20MintableBurnable } from '../interfaces/IERC20MintableBurnable.sol';
import { Operator } from '../utils/Operator.sol';
import { FlowLimit } from '../utils/FlowLimit.sol';
/**
* @title TokenManager
* @notice This contract is responsible for managing tokens, such as setting locking token balances, or setting flow limits, for interchain transfers.
*/
contract TokenManager is ITokenManager, Operator, FlowLimit, Implementation, Multicall {
using AddressBytes for bytes;
using SafeTokenCall for IERC20;
uint256 internal constant UINT256_MAX = type(uint256).max;
address public immutable interchainTokenService;
bytes32 private constant CONTRACT_ID = keccak256('token-manager');
/**
* @notice Constructs the TokenManager contract.
* @param interchainTokenService_ The address of the interchain token service.
*/
constructor(address interchainTokenService_) {
if (interchainTokenService_ == address(0)) revert TokenLinkerZeroAddress();
interchainTokenService = interchainTokenService_;
}
/**
* @notice A modifier that allows only the interchain token service to execute the function.
*/
modifier onlyService() {
if (msg.sender != interchainTokenService) revert NotService(msg.sender);
_;
}
/**
* @notice Getter for the contract id.
* @return bytes32 The contract id.
*/
function contractId() external pure override returns (bytes32) {
return CONTRACT_ID;
}
/**
* @notice Reads the token address from the proxy.
* @dev This function is not supported when directly called on the implementation. It
* must be called by the proxy.
* @return tokenAddress_ The address of the token.
*/
function tokenAddress() external view virtual returns (address) {
revert NotSupported();
}
/**
* @notice A function that returns the token id.
* @dev This will only work when implementation is called by a proxy, which stores the tokenId as an immutable.
* @return bytes32 The interchain token ID.
*/
function interchainTokenId() public pure returns (bytes32) {
revert NotSupported();
}
/**
* @notice Returns implementation type of this token manager.
* @return uint256 The implementation type of this token manager.
*/
function implementationType() external pure returns (uint256) {
revert NotSupported();
}
/**
* @notice A function that should return the token address from the setup params.
* @param params_ The setup parameters.
* @return tokenAddress_ The token address.
*/
function getTokenAddressFromParams(bytes calldata params_) external pure returns (address tokenAddress_) {
(, tokenAddress_) = abi.decode(params_, (bytes, address));
}
/**
* @notice Setup function for the TokenManager.
* @dev This function should only be called by the proxy, and only once from the proxy constructor.
* The exact format of params depends on the type of TokenManager used but the first 32 bytes are reserved
* for the address of the operator, stored as bytes (to be compatible with non-EVM chains)
* @param params_ The parameters to be used to initialize the TokenManager.
*/
function setup(bytes calldata params_) external override(Implementation, IImplementation) onlyProxy {
bytes memory operatorBytes = abi.decode(params_, (bytes));
address operator = address(0);
if (operatorBytes.length != 0) {
operator = operatorBytes.toAddress();
}
// If an operator is not provided, set `address(0)` as the operator.
// This allows anyone to easily check if a custom operator was set on the token manager.
_addAccountRoles(operator, (1 << uint8(Roles.FLOW_LIMITER)) | (1 << uint8(Roles.OPERATOR)));
// Add operator and flow limiter role to the service. The operator can remove the flow limiter role if they so chose and the service has no way to use the operator role for now.
_addAccountRoles(interchainTokenService, (1 << uint8(Roles.FLOW_LIMITER)) | (1 << uint8(Roles.OPERATOR)));
}
function addFlowIn(uint256 amount) external onlyService {
_addFlowIn(amount);
}
function addFlowOut(uint256 amount) external onlyService {
_addFlowOut(amount);
}
/**
* @notice This function transfers a flow limiter for this TokenManager.
* @dev Can only be called by the operator.
* @param from the address of the old flow limiter.
* @param to the address of the new flow limiter.
*/
function transferFlowLimiter(address from, address to) external onlyRole(uint8(Roles.OPERATOR)) {
_transferAccountRoles(from, to, 1 << uint8(Roles.FLOW_LIMITER));
}
/**
* @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 onlyRole(uint8(Roles.OPERATOR)) {
_addRole(flowLimiter, uint8(Roles.FLOW_LIMITER));
}
/**
* @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 onlyRole(uint8(Roles.OPERATOR)) {
_removeRole(flowLimiter, uint8(Roles.FLOW_LIMITER));
}
/**
* @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) {
return hasRole(addr, uint8(Roles.FLOW_LIMITER));
}
/**
* @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 onlyRole(uint8(Roles.FLOW_LIMITER)) {
// slither-disable-next-line var-read-using-this
_setFlowLimit(flowLimit_, this.interchainTokenId());
}
/**
* @notice A function to renew approval to the service if we need to.
*/
function approveService() external onlyService {
/**
* @dev Some tokens may not obey the infinite approval.
* Even so, it is unexpected to run out of allowance in practice.
* If needed, we can upgrade to allow replenishing the allowance in the future.
*/
IERC20(this.tokenAddress()).safeCall(abi.encodeWithSelector(IERC20.approve.selector, interchainTokenService, UINT256_MAX));
}
/**
* @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_) {
params_ = abi.encode(operator_, tokenAddress_);
}
/**
* @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 onlyService {
IERC20(tokenAddress_).safeCall(abi.encodeWithSelector(IERC20MintableBurnable.mint.selector, to, amount));
}
/**
* @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 onlyService {
IERC20(tokenAddress_).safeCall(abi.encodeWithSelector(IERC20MintableBurnable.burn.selector, from, amount));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IFlowLimit } from '../interfaces/IFlowLimit.sol';
/**
* @title FlowLimit
* @notice Implements flow limit logic for interchain token transfers.
* @dev This contract implements low-level assembly for optimization purposes.
*/
contract FlowLimit is IFlowLimit {
// uint256(keccak256('flow-limit')) - 1
uint256 internal constant FLOW_LIMIT_SLOT = 0x201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f;
uint256 internal constant PREFIX_FLOW_OUT_AMOUNT = uint256(keccak256('flow-out-amount'));
uint256 internal constant PREFIX_FLOW_IN_AMOUNT = uint256(keccak256('flow-in-amount'));
uint256 internal constant EPOCH_TIME = 6 hours;
/**
* @notice Returns the current flow limit.
* @return flowLimit_ The current flow limit value.
*/
function flowLimit() public view returns (uint256 flowLimit_) {
assembly {
flowLimit_ := sload(FLOW_LIMIT_SLOT)
}
}
/**
* @notice Internal function to set the flow limit.
* @param flowLimit_ The value to set the flow limit to.
* @param tokenId The id of the token to set the flow limit for.
*/
function _setFlowLimit(uint256 flowLimit_, bytes32 tokenId) internal {
assembly {
sstore(FLOW_LIMIT_SLOT, flowLimit_)
}
emit FlowLimitSet(tokenId, msg.sender, flowLimit_);
}
/**
* @notice Returns the slot which is used to get the flow out amount for a specific epoch.
* @param epoch The epoch to get the flow out amount for.
* @return slot The slot to get the flow out amount from.
*/
function _getFlowOutSlot(uint256 epoch) internal pure returns (uint256 slot) {
slot = uint256(keccak256(abi.encode(PREFIX_FLOW_OUT_AMOUNT, epoch)));
}
/**
* @dev Returns the slot which is used to get the flow in amount for a specific epoch.
* @param epoch The epoch to get the flow in amount for.
* @return slot The slot to get the flow in amount from.
*/
function _getFlowInSlot(uint256 epoch) internal pure returns (uint256 slot) {
slot = uint256(keccak256(abi.encode(PREFIX_FLOW_IN_AMOUNT, epoch)));
}
/**
* @notice Returns the current flow out amount.
* @return flowOutAmount_ The current flow out amount.
*/
function flowOutAmount() external view returns (uint256 flowOutAmount_) {
uint256 epoch = block.timestamp / EPOCH_TIME;
uint256 slot = _getFlowOutSlot(epoch);
assembly {
flowOutAmount_ := sload(slot)
}
}
/**
* @notice Returns the current flow in amount.
* @return flowInAmount_ The current flow in amount.
*/
function flowInAmount() external view returns (uint256 flowInAmount_) {
uint256 epoch = block.timestamp / EPOCH_TIME;
uint256 slot = _getFlowInSlot(epoch);
assembly {
flowInAmount_ := sload(slot)
}
}
/**
* @notice Adds a flow amount while ensuring it does not exceed the flow limit.
* @param flowLimit_ The current flow limit value.
* @param slotToAdd The slot to add the flow to.
* @param slotToCompare The slot to compare the flow against.
* @param flowAmount The flow amount to add.
*/
function _addFlow(uint256 flowLimit_, uint256 slotToAdd, uint256 slotToCompare, uint256 flowAmount) internal {
uint256 flowToAdd;
uint256 flowToCompare;
assembly {
flowToAdd := sload(slotToAdd)
flowToCompare := sload(slotToCompare)
}
// Overflow checks for flowToAdd + flowAmount and flowToCompare + flowLimit_
if (flowToAdd > type(uint256).max - flowAmount) revert FlowAdditionOverflow(flowAmount, flowToAdd, address(this));
if (flowToCompare > type(uint256).max - flowLimit_) revert FlowLimitOverflow(flowLimit_, flowToCompare, address(this));
if (flowToAdd + flowAmount > flowToCompare + flowLimit_)
revert FlowLimitExceeded((flowToCompare + flowLimit_), flowToAdd + flowAmount, address(this));
if (flowAmount > flowLimit_) revert FlowLimitExceeded(flowLimit_, flowAmount, address(this));
assembly {
sstore(slotToAdd, add(flowToAdd, flowAmount))
}
}
/**
* @notice Adds a flow out amount.
* @param flowOutAmount_ The flow out amount to add.
*/
function _addFlowOut(uint256 flowOutAmount_) internal {
uint256 flowLimit_ = flowLimit();
if (flowLimit_ == 0) return;
uint256 epoch = block.timestamp / EPOCH_TIME;
uint256 slotToAdd = _getFlowOutSlot(epoch);
uint256 slotToCompare = _getFlowInSlot(epoch);
_addFlow(flowLimit_, slotToAdd, slotToCompare, flowOutAmount_);
}
/**
* @notice Adds a flow in amount.
* @param flowInAmount_ The flow in amount to add.
*/
function _addFlowIn(uint256 flowInAmount_) internal {
uint256 flowLimit_ = flowLimit();
if (flowLimit_ == 0) return;
uint256 epoch = block.timestamp / EPOCH_TIME;
uint256 slotToAdd = _getFlowInSlot(epoch);
uint256 slotToCompare = _getFlowOutSlot(epoch);
_addFlow(flowLimit_, slotToAdd, slotToCompare, flowInAmount_);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { RolesBase } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/utils/RolesBase.sol';
import { IOperator } from '../interfaces/IOperator.sol';
import { RolesConstants } from './RolesConstants.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));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title RolesConstants
* @notice This contract contains enum values representing different contract roles.
*/
contract RolesConstants {
enum Roles {
MINTER,
OPERATOR,
FLOW_LIMITER
}
}
File 7 of 9: GatewayCaller
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { GasInfo } from '../types/GasEstimationTypes.sol';
import { IInterchainGasEstimation } from './IInterchainGasEstimation.sol';
import { IUpgradable } from './IUpgradable.sol';
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IContractIdentifier } from './IContractIdentifier.sol';
interface IImplementation is IContractIdentifier {
error NotProxy();
function setup(bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol';
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IOwnable } from './IOwnable.sol';
import { IImplementation } from './IImplementation.sol';
// 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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import { IGatewayCaller } from '../interfaces/IGatewayCaller.sol';
/**
* @title GatewayCaller contract
* @dev This contract is used to handle cross-chain ITS calls via the Axelar gateway.
*/
contract GatewayCaller is IGatewayCaller {
IAxelarGateway public immutable gateway;
IAxelarGasService public immutable gasService;
/**
* @dev Constructor to initialize the GatewayCaller contract
* @param gateway_ The address of the AxelarGateway contract
* @param gasService_ The address of the AxelarGasService contract
*/
constructor(address gateway_, address gasService_) {
gateway = IAxelarGateway(gateway_);
gasService = IAxelarGasService(gasService_);
}
/**
* @dev Calls a contract on a specific destination chain with the given payload
* @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 override {
if (gasValue > 0) {
if (metadataVersion == MetadataVersion.CONTRACT_CALL) {
// slither-disable-next-line arbitrary-send-eth
gasService.payNativeGasForContractCall{ value: gasValue }(
address(this),
destinationChain,
destinationAddress,
payload,
// solhint-disable-next-line avoid-tx-origin
tx.origin
);
} else if (metadataVersion == MetadataVersion.EXPRESS_CALL) {
// slither-disable-next-line arbitrary-send-eth
gasService.payNativeGasForExpressCall{ value: gasValue }(
address(this),
destinationChain,
destinationAddress,
payload,
// solhint-disable-next-line avoid-tx-origin
tx.origin
);
} else {
revert InvalidMetadataVersion(uint32(metadataVersion));
}
}
gateway.callContract(destinationChain, destinationAddress, payload);
}
}
File 8 of 9: AxelarGasService
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol';
import { IInterchainGasEstimation } from '../interfaces/IInterchainGasEstimation.sol';
/**
* @title InterchainGasEstimation
* @notice This is an abstract contract that allows for estimating gas fees for cross-chain communication on the Axelar network.
*/
abstract contract InterchainGasEstimation is IInterchainGasEstimation {
// keccak256('GasEstimate.Slot') - 1
bytes32 internal constant GAS_SERVICE_SLOT = 0x2fa150da4c9f4c3a28593398c65313dd42f63d0530ec6db4a2b46e6d837a3902;
// 68 bytes for the TX RLP encoding overhead
uint256 internal constant TX_ENCODING_OVERHEAD = 68;
// GMP executeWithToken call parameters
// 4 bytes for method selector, 32 bytes for the commandId, 96 bytes for the sourceChain, 128 bytes for the sourceAddress, 96 bytes for token symbol, 32 bytes for amount
// Expecting most of the calldata bytes to be zeroes. So multiplying by 8 as a weighted average of 4 and 16
uint256 internal constant GMP_CALLDATA_SIZE = 4 + 32 + 96 + 128 + 96 + 32; // 388 bytes
struct GasServiceStorage {
mapping(string => GasInfo) gasPrices;
}
/**
* @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) {
return _storage().gasPrices[chain];
}
/**
* @notice Sets the gas price for a specific chain.
* @dev This function is called by the gas oracle to update the gas price for a specific chain.
* @param chain The name of the chain
* @param gasInfo The gas info for the chain
*/
function _setGasInfo(string calldata chain, GasInfo calldata gasInfo) internal {
emit GasInfoUpdated(chain, gasInfo);
_storage().gasPrices[chain] = gasInfo;
}
/**
* @notice Estimates the gas fee for a contract call on a destination chain.
* @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 */
) public view returns (uint256 gasEstimate) {
GasInfo storage gasInfo = _storage().gasPrices[destinationChain];
GasEstimationType gasEstimationType = GasEstimationType(gasInfo.gasEstimationType);
gasEstimate = gasInfo.axelarBaseFee + (executionGasLimit * gasInfo.relativeGasPrice);
// if chain is L2, compute L1 data fee using L1 gas price info
if (gasEstimationType != GasEstimationType.Default) {
GasInfo storage l1GasInfo = _storage().gasPrices['ethereum'];
gasEstimate += computeL1DataFee(gasEstimationType, payload, gasInfo, l1GasInfo);
}
}
/**
* @notice Computes the additional L1 data fee for an L2 destination chain.
* @param gasEstimationType The gas estimation type
* @param payload The payload of the contract call
* @param l1GasInfo The L1 gas info
* @return l1DataFee The L1 to L2 data fee
*/
function computeL1DataFee(
GasEstimationType gasEstimationType,
bytes calldata payload,
GasInfo storage gasInfo,
GasInfo storage l1GasInfo
) internal view returns (uint256) {
if (gasEstimationType == GasEstimationType.OptimismEcotone) {
return optimismEcotoneL1Fee(payload, gasInfo, l1GasInfo);
}
if (gasEstimationType == GasEstimationType.OptimismBedrock) {
return optimismBedrockL1Fee(payload, gasInfo, l1GasInfo);
}
if (gasEstimationType == GasEstimationType.Arbitrum) {
return arbitrumL1Fee(payload, gasInfo, l1GasInfo);
}
if (gasEstimationType == GasEstimationType.Scroll) {
return scrollL1Fee(payload, gasInfo, l1GasInfo);
}
revert UnsupportedEstimationType(gasEstimationType);
}
/**
* @notice Computes the L1 to L2 fee for an OP chain with Ecotone gas model.
* @param payload The payload of the contract call
* @param gasInfo Destination chain gas info
* @param l1GasInfo The L1 gas info
* @return l1DataFee The L1 to L2 data fee
*/
function optimismEcotoneL1Fee(
bytes calldata payload,
GasInfo storage gasInfo,
GasInfo storage l1GasInfo
) internal view returns (uint256 l1DataFee) {
/* Optimism Ecotone gas model https://docs.optimism.io/stack/transactions/fees#ecotone
tx_compressed_size = ((count_zero_bytes(tx_data) * 4 + count_non_zero_bytes(tx_data) * 16)) / 16
weighted_gas_price = 16 * base_fee_scalar*base_fee + blob_base_fee_scalar * blob_base_fee
l1_data_fee = tx_compressed_size * weighted_gas_price
Reference implementation:
https://github.com/ethereum-optimism/optimism/blob/876e16ad04968f0bb641eb76f98eb77e7e1a3e16/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L138
*/
// The new base_fee_scalar is currently set to 0.001368
// We are setting it to un upper bound of 0.0015 to account for possible fluctuations
uint256 scalarPrecision = 10**6;
// The blob_base_fee_scalar is currently set to 0.810949. Setting it to 0.9 as an upper bound
// https://eips.ethereum.org/EIPS/eip-4844
uint256 blobBaseFeeScalar = 9 * 10**5; // 0.9 multiplied by scalarPrecision
// Calculating transaction size in bytes that will later be divided by 16 to compress the size
uint256 txSize = _l1TxSize(payload);
uint256 weightedGasPrice = 16 *
gasInfo.l1FeeScalar *
l1GasInfo.relativeGasPrice +
blobBaseFeeScalar *
l1GasInfo.relativeBlobBaseFee;
l1DataFee = (weightedGasPrice * txSize) / (16 * scalarPrecision); // 16 for txSize compression and scalar precision conversion
}
/**
* @notice Computes the L1 to L2 fee for an OP chain with Bedrock gas model.
* @param payload The payload of the contract call
* @param gasInfo Destination chain gas info
* @param l1GasInfo The L1 gas info
* @return l1DataFee The L1 to L2 data fee
*/
function optimismBedrockL1Fee(
bytes calldata payload,
GasInfo storage gasInfo,
GasInfo storage l1GasInfo
) internal view returns (uint256 l1DataFee) {
// Resembling OP Bedrock gas price model
// https://docs.optimism.io/stack/transactions/fees#bedrock
// https://docs-v2.mantle.xyz/devs/concepts/tx-fee/ef
// Reference https://github.com/mantlenetworkio/mantle-v2/blob/a29f01045191344b0ba89542215e6a02bd5e7fcc/packages/contracts-bedrock/contracts/L2/GasPriceOracle.sol#L98-L105
uint256 overhead = 188;
uint256 precision = 1e6;
uint256 txSize = _l1TxSize(payload) + overhead;
return (l1GasInfo.relativeGasPrice * txSize * gasInfo.l1FeeScalar) / precision;
}
/**
* @notice Computes the L1 to L2 fee for a contract call on the Arbitrum chain.
* @param payload The payload of the contract call
* param gasInfo Destination chain gas info
* @param l1GasInfo The L1 gas info
* @return l1DataFee The L1 to L2 data fee
*/
function arbitrumL1Fee(
bytes calldata payload,
GasInfo storage, /* gasInfo */
GasInfo storage l1GasInfo
) internal view returns (uint256 l1DataFee) {
// https://docs.arbitrum.io/build-decentralized-apps/how-to-estimate-gas
// https://docs.arbitrum.io/arbos/l1-pricing
// Reference https://github.com/OffchainLabs/nitro/blob/master/arbos/l1pricing/l1pricing.go#L565-L578
uint256 oneInBips = 10000;
uint256 txDataNonZeroGasEIP2028 = 16;
uint256 estimationPaddingUnits = 16 * txDataNonZeroGasEIP2028;
uint256 estimationPaddingBasisPoints = 100;
uint256 l1Bytes = TX_ENCODING_OVERHEAD + GMP_CALLDATA_SIZE + payload.length;
// Brotli baseline compression rate as 2x
uint256 units = (txDataNonZeroGasEIP2028 * l1Bytes) / 2;
return
(l1GasInfo.relativeGasPrice *
(units + estimationPaddingUnits) *
(oneInBips + estimationPaddingBasisPoints)) / oneInBips;
}
/**
* @notice Computes the L1 to L2 fee for a contract call on the Scroll chain.
* @param payload The payload of the contract call
* @param gasInfo Destination chain gas info
* @param l1GasInfo The L1 gas info
* @return l1DataFee The L1 to L2 data fee
*/
function scrollL1Fee(
bytes calldata payload,
GasInfo storage gasInfo,
GasInfo storage l1GasInfo
) internal view returns (uint256 l1DataFee) {
// https://docs.scroll.io/en/developers/guides/estimating-gas-and-tx-fees/
// Reference https://github.com/scroll-tech/scroll/blob/af2913903b181f3492af1c62b4da4c1c99cc552d/contracts/src/L2/predeploys/L1GasPriceOracle.sol#L63-L86
uint256 overhead = 2500;
uint256 precision = 1e9;
uint256 txSize = _l1TxSize(payload) + overhead + (4 * 16);
return (l1GasInfo.relativeGasPrice * txSize * gasInfo.l1FeeScalar) / precision;
}
/**
* @notice Computes the transaction size for an L1 transaction
* @param payload The payload of the contract call
* @return txSize The transaction size
*/
function _l1TxSize(bytes calldata payload) private pure returns (uint256 txSize) {
txSize = TX_ENCODING_OVERHEAD * 16;
// GMP executeWithToken call parameters
// Expecting most of the calldata bytes to be zeroes. So multiplying by 8 as a weighted average of 4 and 16
txSize += GMP_CALLDATA_SIZE * 8;
uint256 length = payload.length;
for (uint256 i; i < length; ++i) {
if (payload[i] == 0) {
txSize += 4; // 4 for each zero byte
} else {
txSize += 16; // 16 for each non-zero byte
}
}
}
/**
* @notice Get the storage slot for the GasServiceStorage struct
*/
function _storage() private pure returns (GasServiceStorage storage slot) {
assembly {
slot.slot := GAS_SERVICE_SLOT
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { GasInfo } from '../types/GasEstimationTypes.sol';
import { IInterchainGasEstimation } from './IInterchainGasEstimation.sol';
import { IUpgradable } from './IUpgradable.sol';
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IContractIdentifier } from './IContractIdentifier.sol';
interface IImplementation is IContractIdentifier {
error NotProxy();
function setup(bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol';
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IOwnable } from './IOwnable.sol';
import { IImplementation } from './IImplementation.sol';
// 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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
error NativeTransferFailed();
/*
* @title SafeNativeTransfer
* @dev This library is used for performing safe native value transfers in Solidity by utilizing inline assembly.
*/
library SafeNativeTransfer {
/*
* @notice Perform a native transfer to a given address.
* @param receiver The recipient address to which the amount will be sent.
* @param amount The amount of native value to send.
* @throws NativeTransferFailed error if transfer is not successful.
*/
function safeNativeTransfer(address receiver, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), receiver, amount, 0, 0, 0, 0)
}
if (!success) revert NativeTransferFailed();
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '../interfaces/IERC20.sol';
error TokenTransferFailed();
/*
* @title SafeTokenCall
* @dev This library is used for performing safe token transfers.
*/
library SafeTokenCall {
/*
* @notice Make a safe call to a token contract.
* @param token The token contract to interact with.
* @param callData The function call data.
* @throws TokenTransferFailed error if transfer of token is not successful.
*/
function safeCall(IERC20 token, bytes memory callData) internal {
(bool success, bytes memory returnData) = address(token).call(callData);
bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
if (!transferred || address(token).code.length == 0) revert TokenTransferFailed();
}
}
/*
* @title SafeTokenTransfer
* @dev This library safely transfers tokens from the contract to a recipient.
*/
library SafeTokenTransfer {
/*
* @notice Transfer tokens to a recipient.
* @param token The token contract.
* @param receiver The recipient of the tokens.
* @param amount The amount of tokens to transfer.
*/
function safeTransfer(
IERC20 token,
address receiver,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
}
}
/*
* @title SafeTokenTransferFrom
* @dev This library helps to safely transfer tokens on behalf of a token holder.
*/
library SafeTokenTransferFrom {
/*
* @notice Transfer tokens on behalf of a token holder.
* @param token The token contract.
* @param from The address of the token holder.
* @param to The address the tokens are to be sent to.
* @param amount The amount of tokens to be transferred.
*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '../interfaces/IImplementation.sol';
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '../interfaces/IImplementation.sol';
import { IUpgradable } from '../interfaces/IUpgradable.sol';
import { Ownable } from '../utils/Ownable.sol';
import { Implementation } from './Implementation.sol';
/**
* @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 {}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IOwnable } from '../interfaces/IOwnable.sol';
/**
* @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)
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import { InterchainGasEstimation, GasInfo } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/gas-estimation/InterchainGasEstimation.sol';
import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol';
import { SafeTokenTransfer, SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
import { SafeNativeTransfer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeNativeTransfer.sol';
/**
* @title AxelarGasService
* @notice This contract manages gas payments and refunds for cross-chain communication on the Axelar network.
* @dev The owner address of this contract should be the microservice that pays for gas.
* @dev Users pay gas for cross-chain calls, and the gasCollector can collect accumulated fees and/or refund users if needed.
*/
contract AxelarGasService is InterchainGasEstimation, Upgradable, IAxelarGasService {
using SafeTokenTransfer for IERC20;
using SafeTokenTransferFrom for IERC20;
using SafeNativeTransfer for address payable;
address public immutable gasCollector;
/**
* @notice Constructs the AxelarGasService contract.
* @param gasCollector_ The address of the gas collector
*/
constructor(address gasCollector_) {
gasCollector = gasCollector_;
}
/**
* @notice Modifier that ensures the caller is the designated gas collector.
*/
modifier onlyCollector() {
if (msg.sender != gasCollector) revert NotCollector();
_;
}
/**
* @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
*/
function payGas(
address sender,
string calldata destinationChain,
string calldata destinationAddress,
bytes calldata payload,
uint256 executionGasLimit,
bool estimateOnChain,
address refundAddress,
bytes calldata params
) external payable override {
if (params.length > 0) {
revert InvalidParams();
}
if (estimateOnChain) {
uint256 gasEstimate = estimateGasFee(destinationChain, destinationAddress, payload, executionGasLimit, params);
if (gasEstimate > msg.value) {
revert InsufficientGasPayment(gasEstimate, msg.value);
}
}
emit NativeGasPaidForContractCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
}
/**
* @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 override {
emit GasPaidForContractCall(
sender,
destinationChain,
destinationAddress,
keccak256(payload),
gasToken,
gasFeeAmount,
refundAddress
);
IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
}
/**
* @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 memory symbol,
uint256 amount,
address gasToken,
uint256 gasFeeAmount,
address refundAddress
) external override {
emit GasPaidForContractCallWithToken(
sender,
destinationChain,
destinationAddress,
keccak256(payload),
symbol,
amount,
gasToken,
gasFeeAmount,
refundAddress
);
IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
}
/**
* @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 override {
emit NativeGasPaidForContractCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
}
/**
* @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 override {
emit NativeGasPaidForContractCallWithToken(
sender,
destinationChain,
destinationAddress,
keccak256(payload),
symbol,
amount,
msg.value,
refundAddress
);
}
/**
* @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 override {
emit GasPaidForExpressCall(sender, destinationChain, destinationAddress, keccak256(payload), gasToken, gasFeeAmount, refundAddress);
IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
}
/**
* @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 memory symbol,
uint256 amount,
address gasToken,
uint256 gasFeeAmount,
address refundAddress
) external override {
emit GasPaidForExpressCallWithToken(
sender,
destinationChain,
destinationAddress,
keccak256(payload),
symbol,
amount,
gasToken,
gasFeeAmount,
refundAddress
);
IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
}
/**
* @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 override {
emit NativeGasPaidForExpressCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
}
/**
* @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 override {
emit NativeGasPaidForExpressCallWithToken(
sender,
destinationChain,
destinationAddress,
keccak256(payload),
symbol,
amount,
msg.value,
refundAddress
);
}
/**
* @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 override {
emit GasAdded(txHash, logIndex, gasToken, gasFeeAmount, refundAddress);
IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
}
/**
* @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 override {
emit NativeGasAdded(txHash, logIndex, msg.value, refundAddress);
}
/**
* @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 override {
emit ExpressGasAdded(txHash, logIndex, gasToken, gasFeeAmount, refundAddress);
IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
}
/**
* @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 override {
emit NativeExpressGasAdded(txHash, logIndex, msg.value, refundAddress);
}
/**
* @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 onlyCollector {
uint256 chainsLength = chains.length;
if (chainsLength != gasUpdates.length) revert InvalidGasUpdates();
for (uint256 i; i < chainsLength; i++) {
string calldata chain = chains[i];
GasInfo calldata gasUpdate = gasUpdates[i];
_setGasInfo(chain, gasUpdate);
}
}
/**
* @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 onlyCollector {
if (receiver == address(0)) revert InvalidAddress();
uint256 tokensLength = tokens.length;
if (tokensLength != amounts.length) revert InvalidAmounts();
for (uint256 i; i < tokensLength; i++) {
address token = tokens[i];
uint256 amount = amounts[i];
if (amount == 0) revert InvalidAmounts();
if (token == address(0)) {
if (amount <= address(this).balance) receiver.safeNativeTransfer(amount);
} else {
// slither-disable-next-line calls-loop
if (amount <= IERC20(token).balanceOf(address(this))) IERC20(token).safeTransfer(receiver, amount);
}
}
}
/**
* @dev Deprecated refund function, kept for backward compatibility.
*/
function refund(
address payable receiver,
address token,
uint256 amount
) external onlyCollector {
_refund(bytes32(0), 0, receiver, token, amount);
}
/**
* @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 onlyCollector {
_refund(txHash, logIndex, receiver, token, amount);
}
/**
* @dev Internal function to implement gas refund logic.
*/
function _refund(
bytes32 txHash,
uint256 logIndex,
address payable receiver,
address token,
uint256 amount
) private {
if (receiver == address(0)) revert InvalidAddress();
emit Refunded(txHash, logIndex, receiver, token, amount);
if (token == address(0)) {
receiver.safeNativeTransfer(amount);
} else {
IERC20(token).safeTransfer(receiver, amount);
}
}
/**
* @notice Returns a unique identifier for the contract.
* @return bytes32 Hash of the contract identifier
*/
function contractId() external pure returns (bytes32) {
return keccak256('axelar-gas-service');
}
}
File 9 of 9: AxelarGateway
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IGovernable } from './IGovernable.sol';
import { IImplementation } from './IImplementation.sol';
interface IAxelarGateway is IImplementation, IGovernable {
/**********\\
|* Errors *|
\\**********/
error NotSelf();
error InvalidCodeHash();
error SetupFailed();
error InvalidAuthModule();
error InvalidTokenDeployer();
error InvalidAmount();
error InvalidChainId();
error InvalidCommands();
error TokenDoesNotExist(string symbol);
error TokenAlreadyExists(string symbol);
error TokenDeployFailed(string symbol);
error TokenContractDoesNotExist(address token);
error BurnFailed(string symbol);
error MintFailed(string symbol);
error InvalidSetMintLimitsParams();
error ExceedMintLimit(string symbol);
/**********\\
|* Events *|
\\**********/
event TokenSent(
address indexed sender,
string destinationChain,
string destinationAddress,
string symbol,
uint256 amount
);
event ContractCall(
address indexed sender,
string destinationChain,
string destinationContractAddress,
bytes32 indexed payloadHash,
bytes payload
);
event ContractCallWithToken(
address indexed sender,
string destinationChain,
string destinationContractAddress,
bytes32 indexed payloadHash,
bytes payload,
string symbol,
uint256 amount
);
event Executed(bytes32 indexed commandId);
event TokenDeployed(string symbol, address tokenAddresses);
event ContractCallApproved(
bytes32 indexed commandId,
string sourceChain,
string sourceAddress,
address indexed contractAddress,
bytes32 indexed payloadHash,
bytes32 sourceTxHash,
uint256 sourceEventIndex
);
event ContractCallApprovedWithMint(
bytes32 indexed commandId,
string sourceChain,
string sourceAddress,
address indexed contractAddress,
bytes32 indexed payloadHash,
string symbol,
uint256 amount,
bytes32 sourceTxHash,
uint256 sourceEventIndex
);
event ContractCallExecuted(bytes32 indexed commandId);
event TokenMintLimitUpdated(string symbol, uint256 limit);
event OperatorshipTransferred(bytes newOperatorsData);
event Upgraded(address indexed implementation);
/********************\\
|* Public Functions *|
\\********************/
function sendToken(
string calldata destinationChain,
string calldata destinationAddress,
string calldata symbol,
uint256 amount
) external;
function callContract(
string calldata destinationChain,
string calldata contractAddress,
bytes calldata payload
) external;
function callContractWithToken(
string calldata destinationChain,
string calldata contractAddress,
bytes calldata payload,
string calldata symbol,
uint256 amount
) external;
function isContractCallApproved(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash
) external view returns (bool);
function isContractCallAndMintApproved(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash,
string calldata symbol,
uint256 amount
) external view returns (bool);
function validateContractCall(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash
) external returns (bool);
function validateContractCallAndMint(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash,
string calldata symbol,
uint256 amount
) external returns (bool);
/***********\\
|* Getters *|
\\***********/
function authModule() external view returns (address);
function tokenDeployer() external view returns (address);
function tokenMintLimit(string memory symbol) external view returns (uint256);
function tokenMintAmount(string memory symbol) external view returns (uint256);
function allTokensFrozen() external view returns (bool);
function implementation() external view returns (address);
function tokenAddresses(string memory symbol) external view returns (address);
function tokenFrozen(string memory symbol) external view returns (bool);
function isCommandExecuted(bytes32 commandId) external view returns (bool);
/************************\\
|* Governance Functions *|
\\************************/
function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external;
function upgrade(
address newImplementation,
bytes32 newImplementationCodeHash,
bytes calldata setupParams
) external;
/**********************\\
|* External Functions *|
\\**********************/
function execute(bytes calldata input) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// 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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @title IGovernable Interface
* @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles.
*/
interface IGovernable {
error NotGovernance();
error NotMintLimiter();
error InvalidGovernance();
error InvalidMintLimiter();
event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);
event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance);
/**
* @notice Returns the governance address.
* @return address of the governance
*/
function governance() external view returns (address);
/**
* @notice Returns the mint limiter address.
* @return address of the mint limiter
*/
function mintLimiter() external view returns (address);
/**
* @notice Transfer the governance role to another address.
* @param newGovernance The new governance address
*/
function transferGovernance(address newGovernance) external;
/**
* @notice Transfer the mint limiter role to another address.
* @param newGovernance The new mint limiter address
*/
function transferMintLimiter(address newGovernance) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IContractIdentifier } from './IContractIdentifier.sol';
interface IImplementation is IContractIdentifier {
error NotProxy();
function setup(bytes calldata data) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library ContractAddress {
function isContract(address contractAddress) internal view returns (bool) {
bytes32 existingCodeHash = contractAddress.codehash;
// https://eips.ethereum.org/EIPS/eip-1052
// keccak256('') == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
return
existingCodeHash != bytes32(0) &&
existingCodeHash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '../interfaces/IERC20.sol';
error TokenTransferFailed();
/*
* @title SafeTokenCall
* @dev This library is used for performing safe token transfers.
*/
library SafeTokenCall {
/*
* @notice Make a safe call to a token contract.
* @param token The token contract to interact with.
* @param callData The function call data.
* @throws TokenTransferFailed error if transfer of token is not successful.
*/
function safeCall(IERC20 token, bytes memory callData) internal {
(bool success, bytes memory returnData) = address(token).call(callData);
bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
if (!transferred || address(token).code.length == 0) revert TokenTransferFailed();
}
}
/*
* @title SafeTokenTransfer
* @dev This library safely transfers tokens from the contract to a recipient.
*/
library SafeTokenTransfer {
/*
* @notice Transfer tokens to a recipient.
* @param token The token contract.
* @param receiver The recipient of the tokens.
* @param amount The amount of tokens to transfer.
*/
function safeTransfer(
IERC20 token,
address receiver,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
}
}
/*
* @title SafeTokenTransferFrom
* @dev This library helps to safely transfer tokens on behalf of a token holder.
*/
library SafeTokenTransferFrom {
/*
* @notice Transfer tokens on behalf of a token holder.
* @param token The token contract.
* @param from The address of the token holder.
* @param to The address the tokens are to be sent to.
* @param amount The amount of tokens to be transferred.
*/
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IImplementation } from '../interfaces/IImplementation.sol';
/**
* @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;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol';
import { IContractIdentifier } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IContractIdentifier.sol';
import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
import { SafeTokenCall, SafeTokenTransfer, SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
import { ContractAddress } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/ContractAddress.sol';
import { Implementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Implementation.sol';
import { IAxelarAuth } from './interfaces/IAxelarAuth.sol';
import { IBurnableMintableCappedERC20 } from './interfaces/IBurnableMintableCappedERC20.sol';
import { ITokenDeployer } from './interfaces/ITokenDeployer.sol';
import { ECDSA } from './ECDSA.sol';
import { DepositHandler } from './DepositHandler.sol';
import { EternalStorage } from './EternalStorage.sol';
/**
* @title AxelarGateway Contract
* @notice This contract serves as the gateway for cross-chain contract calls,
* and token transfers within the Axelar network.
* It includes functions for sending tokens, calling contracts, and validating contract calls.
* The contract is managed via the decentralized governance mechanism on the Axelar network.
* @dev EternalStorage is used to simplify storage for upgradability, and InterchainGovernance module is used for governance.
*/
contract AxelarGateway is IAxelarGateway, Implementation, EternalStorage {
using SafeTokenCall for IERC20;
using SafeTokenTransfer for IERC20;
using SafeTokenTransferFrom for IERC20;
using ContractAddress for address;
error InvalidImplementation();
enum TokenType {
InternalBurnable,
InternalBurnableFrom,
External
}
/**
* @dev Deprecated slots. Should not be reused.
*/
// bytes32 internal constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen');
// bytes32 internal constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen');
/**
* @dev Storage slot with the address of the current implementation. `keccak256('eip1967.proxy.implementation') - 1`.
*/
bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
/**
* @dev Storage slot with the address of the current governance. keccak256('governance')) - 1
*/
bytes32 internal constant KEY_GOVERNANCE = bytes32(0xabea6fd3db56a6e6d0242111b43ebb13d1c42709651c032c7894962023a1f909);
/**
* @dev Storage slot with the address of the current mint limiter. keccak256('mint-limiter')) - 1
*/
bytes32 internal constant KEY_MINT_LIMITER = bytes32(0x627f0c11732837b3240a2de89c0b6343512886dd50978b99c76a68c6416a4d92);
bytes32 internal constant PREFIX_COMMAND_EXECUTED = keccak256('command-executed');
bytes32 internal constant PREFIX_TOKEN_ADDRESS = keccak256('token-address');
bytes32 internal constant PREFIX_TOKEN_TYPE = keccak256('token-type');
bytes32 internal constant PREFIX_CONTRACT_CALL_APPROVED = keccak256('contract-call-approved');
bytes32 internal constant PREFIX_CONTRACT_CALL_APPROVED_WITH_MINT = keccak256('contract-call-approved-with-mint');
bytes32 internal constant PREFIX_TOKEN_MINT_LIMIT = keccak256('token-mint-limit');
bytes32 internal constant PREFIX_TOKEN_MINT_AMOUNT = keccak256('token-mint-amount');
bytes32 internal constant SELECTOR_BURN_TOKEN = keccak256('burnToken');
bytes32 internal constant SELECTOR_DEPLOY_TOKEN = keccak256('deployToken');
bytes32 internal constant SELECTOR_MINT_TOKEN = keccak256('mintToken');
bytes32 internal constant SELECTOR_APPROVE_CONTRACT_CALL = keccak256('approveContractCall');
bytes32 internal constant SELECTOR_APPROVE_CONTRACT_CALL_WITH_MINT = keccak256('approveContractCallWithMint');
bytes32 internal constant SELECTOR_TRANSFER_OPERATORSHIP = keccak256('transferOperatorship');
address public immutable authModule;
address public immutable tokenDeployer;
/**
* @notice Constructs the AxelarGateway contract.
* @param authModule_ The address of the authentication module
* @param tokenDeployer_ The address of the token deployer
*/
constructor(address authModule_, address tokenDeployer_) {
if (authModule_.code.length == 0) revert InvalidAuthModule();
if (tokenDeployer_.code.length == 0) revert InvalidTokenDeployer();
authModule = authModule_;
tokenDeployer = tokenDeployer_;
}
/**
* @notice Ensures that the caller of the function is the gateway contract itself.
*/
modifier onlySelf() {
if (msg.sender != address(this)) revert NotSelf();
_;
}
/**
* @notice Ensures that the caller of the function is the governance address.
*/
modifier onlyGovernance() {
if (msg.sender != getAddress(KEY_GOVERNANCE)) revert NotGovernance();
_;
}
/**
* @notice Ensures that the caller of the function is either the mint limiter or governance.
*/
modifier onlyMintLimiter() {
if (msg.sender != getAddress(KEY_MINT_LIMITER) && msg.sender != getAddress(KEY_GOVERNANCE)) revert NotMintLimiter();
_;
}
/******************\\
|* Public Methods *|
\\******************/
/**
* @notice Send the specified token to the destination chain and address.
* @param destinationChain The chain to send tokens to. A registered chain name on Axelar must be used here
* @param destinationAddress The address on the destination chain to send tokens to
* @param symbol The symbol of the token to send
* @param amount The amount of tokens to send
*/
function sendToken(
string calldata destinationChain,
string calldata destinationAddress,
string calldata symbol,
uint256 amount
) external {
_burnTokenFrom(msg.sender, symbol, amount);
emit TokenSent(msg.sender, destinationChain, destinationAddress, symbol, amount);
}
/**
* @notice Calls a contract on the specified destination chain with a given payload.
* This function is the entry point for general message passing between chains.
* @param destinationChain The chain where the destination contract exists. A registered chain name on Axelar must be used here
* @param destinationContractAddress The address of the contract to call on the destination chain
* @param payload The payload to be sent to the destination contract, usually representing an encoded function call with arguments
*/
function callContract(
string calldata destinationChain,
string calldata destinationContractAddress,
bytes calldata payload
) external {
emit ContractCall(msg.sender, destinationChain, destinationContractAddress, keccak256(payload), payload);
}
/**
* @notice Calls a contract on the specified destination chain with a given payload and token amount.
* This function is the entry point for general message passing with token transfer between chains.
* @param destinationChain The chain where the destination contract exists. A registered chain name on Axelar must be used here
* @param destinationContractAddress The address of the contract to call with tokens on the destination chain
* @param payload The payload to be sent to the destination contract, usually representing an encoded function call with arguments
* @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
*/
function callContractWithToken(
string calldata destinationChain,
string calldata destinationContractAddress,
bytes calldata payload,
string calldata symbol,
uint256 amount
) external {
_burnTokenFrom(msg.sender, symbol, amount);
emit ContractCallWithToken(msg.sender, destinationChain, destinationContractAddress, keccak256(payload), payload, symbol, amount);
}
/**
* @notice Checks whether a contract call has been approved by the gateway.
* @param commandId The gateway command ID
* @param sourceChain The source chain of the contract call
* @param sourceAddress The source address of the contract call
* @param contractAddress The contract address that will be called
* @param payloadHash The hash of the payload for that will be sent with the call
* @return bool A boolean value indicating whether the contract call has been approved by the gateway.
*/
function isContractCallApproved(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash
) external view override returns (bool) {
return getBool(_getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash));
}
/**
* @notice Checks whether a contract call with token has been approved by the gateway.
* @param commandId The gateway command ID
* @param sourceChain The source chain of the contract call
* @param sourceAddress The source address of the contract call
* @param contractAddress The contract address that will be called, and where tokens will be sent
* @param payloadHash The hash of the payload for that will be sent with the call
* @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
* @return bool A boolean value indicating whether the contract call with token has been approved by the gateway.
*/
function isContractCallAndMintApproved(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
address contractAddress,
bytes32 payloadHash,
string calldata symbol,
uint256 amount
) external view override returns (bool) {
return
getBool(
_getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount)
);
}
/**
* @notice Called on the destination chain gateway by the recipient of the cross-chain contract call to validate it and only allow execution
* if this function returns true.
* @dev Once validated, the gateway marks the message as executed so the contract call is not executed twice.
* @param commandId The gateway command ID
* @param sourceChain The source chain of the contract call
* @param sourceAddress The source address of the contract call
* @param payloadHash The hash of the payload for that will be sent with the call
* @return valid True if the contract call is approved, false otherwise
*/
function validateContractCall(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash
) external override returns (bool valid) {
bytes32 key = _getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, msg.sender, payloadHash);
valid = getBool(key);
if (valid) {
_setBool(key, false);
emit ContractCallExecuted(commandId);
}
}
/**
* @notice Called on the destination chain gateway to validate the approval of a contract call with token transfer and only
* allow execution if this function returns true.
* @dev Once validated, the gateway marks the message as executed so the contract call with token is not executed twice.
* @param commandId The gateway command ID
* @param sourceChain The source chain of the contract call
* @param sourceAddress The source address of the contract call
* @param payloadHash The hash of the payload for that will be sent with the call
* @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
* @return valid True if the contract call with token is approved, false otherwise
*/
function validateContractCallAndMint(
bytes32 commandId,
string calldata sourceChain,
string calldata sourceAddress,
bytes32 payloadHash,
string calldata symbol,
uint256 amount
) external override returns (bool valid) {
bytes32 key = _getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, msg.sender, payloadHash, symbol, amount);
valid = getBool(key);
if (valid) {
// Prevent re-entrancy
_setBool(key, false);
emit ContractCallExecuted(commandId);
_mintToken(symbol, msg.sender, amount);
}
}
/***********\\
|* Getters *|
\\***********/
/**
* @notice Gets the address of governance, should be the address of InterchainGovernance.
* @return address The address of governance.
*/
function governance() public view override returns (address) {
return getAddress(KEY_GOVERNANCE);
}
/**
* @notice Gets the address of the mint limiter, should be the address of Multisig.
* @return address The address of the mint limiter.
*/
function mintLimiter() public view override returns (address) {
return getAddress(KEY_MINT_LIMITER);
}
/**
* @notice Gets the transfer limit for a specific token symbol within the configured epoch.
* @param symbol The symbol of the token
* @return uint The transfer limit for the given token.
*/
function tokenMintLimit(string memory symbol) public view override returns (uint256) {
return getUint(_getTokenMintLimitKey(symbol));
}
/**
* @notice Gets the transfer amount for a specific token symbol within the configured epoch.
* @param symbol The symbol of the token
* @return uint The transfer amount for the given token.
*/
function tokenMintAmount(string memory symbol) public view override returns (uint256) {
return getUint(_getTokenMintAmountKey(symbol, block.timestamp / 6 hours));
}
/**
* @dev This function is kept around to keep things working for internal
* tokens that were deployed before the token freeze functionality was removed
*/
function allTokensFrozen() external pure override returns (bool) {
return false;
}
/**
* @notice Gets the address of the gateway implementation contract.
* @return address The address of the gateway implementation.
*/
function implementation() public view override returns (address) {
return getAddress(KEY_IMPLEMENTATION);
}
/**
* @notice Gets the address of a specific token using its symbol.
* @param symbol The symbol of the token
* @return address The address of the token associated with the given symbol.
*/
function tokenAddresses(string memory symbol) public view override returns (address) {
return getAddress(_getTokenAddressKey(symbol));
}
/**
* @dev Deprecated. This function is kept around to keep things working for internal tokens that were deployed before the token freeze functionality was removed
*/
function tokenFrozen(string memory) external pure override returns (bool) {
return false;
}
/**
* @notice Checks whether a command with a given command ID has been executed.
* @param commandId The command ID to check
* @return bool True if the command has been executed, false otherwise
*/
function isCommandExecuted(bytes32 commandId) public view override returns (bool) {
return getBool(_getIsCommandExecutedKey(commandId));
}
/**
* @notice Gets the contract ID of the Axelar Gateway.
* @return bytes32 The keccak256 hash of the string 'axelar-gateway'
*/
function contractId() public pure returns (bytes32) {
return keccak256('axelar-gateway');
}
/************************\\
|* Governance Functions *|
\\************************/
/**
* @notice Transfers the governance role to a new address.
* @param newGovernance The address to transfer the governance role to.
* @dev Only the current governance entity can call this function.
*/
function transferGovernance(address newGovernance) external override onlyGovernance {
if (newGovernance == address(0)) revert InvalidGovernance();
_transferGovernance(newGovernance);
}
/**
* @notice Transfers the mint limiter role to a new address.
* @param newMintLimiter The address to transfer the mint limiter role to.
* @dev Only the current mint limiter or the governance address can call this function.
*/
function transferMintLimiter(address newMintLimiter) external override onlyMintLimiter {
if (newMintLimiter == address(0)) revert InvalidMintLimiter();
_transferMintLimiter(newMintLimiter);
}
/**
* @notice Sets the transfer limits for an array of tokens.
* @param symbols The array of token symbols to set the transfer limits for
* @param limits The array of transfer limits corresponding to the symbols
* @dev Only the mint limiter or the governance address can call this function.
*/
function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external override onlyMintLimiter {
uint256 length = symbols.length;
if (length != limits.length) revert InvalidSetMintLimitsParams();
for (uint256 i; i < length; ++i) {
string memory symbol = symbols[i];
uint256 limit = limits[i];
if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);
_setTokenMintLimit(symbol, limit);
}
}
/**
* @notice Upgrades the contract to a new implementation.
* @param newImplementation The address of the new implementation
* @param newImplementationCodeHash The code hash of the new implementation
* @param setupParams Optional setup params for the new implementation
* @dev Only the governance address can call this function.
*/
function upgrade(
address newImplementation,
bytes32 newImplementationCodeHash,
bytes calldata setupParams
) external override onlyGovernance {
if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();
if (contractId() != IContractIdentifier(newImplementation).contractId()) revert InvalidImplementation();
emit Upgraded(newImplementation);
_setImplementation(newImplementation);
if (setupParams.length != 0) {
// slither-disable-next-line controlled-delegatecall
(bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(IImplementation.setup.selector, setupParams));
if (!success) revert SetupFailed();
}
}
/**********************\\
|* External Functions *|
\\**********************/
/**
* @notice Sets up the governance and mint limiter roles, and transfers operatorship if necessary.
* This function is called by the proxy during initial deployment, and optionally called during gateway upgrades.
* @param params The encoded parameters containing the governance and mint limiter addresses, as well as the new operator data.
* @dev Not publicly accessible as it's overshadowed in the proxy.
*/
function setup(bytes calldata params) external override(IImplementation, Implementation) onlyProxy {
(address governance_, address mintLimiter_, bytes memory newOperatorsData) = abi.decode(params, (address, address, bytes));
if (governance_ != address(0)) _transferGovernance(governance_);
if (mintLimiter_ != address(0)) _transferMintLimiter(mintLimiter_);
if (newOperatorsData.length != 0) {
emit OperatorshipTransferred(newOperatorsData);
IAxelarAuth(authModule).transferOperatorship(newOperatorsData);
}
}
/**
* @notice Executes a batch of commands signed by the Axelar network. There are a finite set of command types that can be executed.
* @param input The encoded input containing the data for the batch of commands, as well as the proof that verifies the integrity of the data.
* @dev Each command has a corresponding commandID that is guaranteed to be unique from the Axelar network.
* @dev This function allows retrying a commandID if the command initially failed to be processed.
* @dev Ignores unknown commands or duplicate commandIDs.
* @dev Emits an Executed event for successfully executed commands.
*/
// slither-disable-next-line cyclomatic-complexity
function execute(bytes calldata input) external override {
(bytes memory data, bytes memory proof) = abi.decode(input, (bytes, bytes));
bytes32 messageHash = ECDSA.toEthSignedMessageHash(keccak256(data));
// returns true for current operators
// slither-disable-next-line reentrancy-no-eth
bool allowOperatorshipTransfer = IAxelarAuth(authModule).validateProof(messageHash, proof);
uint256 chainId;
bytes32[] memory commandIds;
string[] memory commands;
bytes[] memory params;
(chainId, commandIds, commands, params) = abi.decode(data, (uint256, bytes32[], string[], bytes[]));
if (chainId != block.chainid) revert InvalidChainId();
uint256 commandsLength = commandIds.length;
if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();
for (uint256 i; i < commandsLength; ++i) {
bytes32 commandId = commandIds[i];
// Ignore if duplicate commandId received
if (isCommandExecuted(commandId)) continue;
bytes4 commandSelector;
bytes32 commandHash = keccak256(abi.encodePacked(commands[i]));
if (commandHash == SELECTOR_DEPLOY_TOKEN) {
commandSelector = AxelarGateway.deployToken.selector;
} else if (commandHash == SELECTOR_MINT_TOKEN) {
commandSelector = AxelarGateway.mintToken.selector;
} else if (commandHash == SELECTOR_APPROVE_CONTRACT_CALL) {
commandSelector = AxelarGateway.approveContractCall.selector;
} else if (commandHash == SELECTOR_APPROVE_CONTRACT_CALL_WITH_MINT) {
commandSelector = AxelarGateway.approveContractCallWithMint.selector;
} else if (commandHash == SELECTOR_BURN_TOKEN) {
commandSelector = AxelarGateway.burnToken.selector;
} else if (commandHash == SELECTOR_TRANSFER_OPERATORSHIP) {
if (!allowOperatorshipTransfer) continue;
allowOperatorshipTransfer = false;
commandSelector = AxelarGateway.transferOperatorship.selector;
} else {
// Ignore unknown commands
continue;
}
// Prevent a re-entrancy from executing this command before it can be marked as successful.
_setCommandExecuted(commandId, true);
// slither-disable-next-line calls-loop,reentrancy-no-eth
(bool success, ) = address(this).call(abi.encodeWithSelector(commandSelector, params[i], commandId));
// slither-disable-next-line reentrancy-events
if (success) emit Executed(commandId);
else _setCommandExecuted(commandId, false);
}
}
/******************\\
|* Self Functions *|
\\******************/
/**
* @notice Deploys a new token or registers an existing token in the gateway contract itself.
* @param params Encoded parameters including the token name, symbol, decimals, cap, token address, and mint limit
* @dev If the token address is not specified, a new token is deployed and registed as InternalBurnableFrom
* @dev If the token address is specified, the token is marked as External.
* @dev Emits a TokenDeployed event with the symbol and token address.
*/
function deployToken(bytes calldata params, bytes32) external onlySelf {
(string memory name, string memory symbol, uint8 decimals, uint256 cap, address tokenAddress, uint256 mintLimit) = abi.decode(
params,
(string, string, uint8, uint256, address, uint256)
);
// Ensure that this symbol has not been taken.
if (tokenAddresses(symbol) != address(0)) revert TokenAlreadyExists(symbol);
_setTokenMintLimit(symbol, mintLimit);
if (tokenAddress == address(0)) {
// If token address is not specified, it indicates a request to deploy one.
bytes32 salt = keccak256(abi.encodePacked(symbol));
_setTokenType(symbol, TokenType.InternalBurnableFrom);
// slither-disable-next-line reentrancy-no-eth,controlled-delegatecall
(bool success, bytes memory data) = tokenDeployer.delegatecall(
abi.encodeWithSelector(ITokenDeployer.deployToken.selector, name, symbol, decimals, cap, salt)
);
if (!success) revert TokenDeployFailed(symbol);
tokenAddress = abi.decode(data, (address));
} else {
// If token address is specified, ensure that there is a contact at the specified address.
if (tokenAddress.code.length == uint256(0)) revert TokenContractDoesNotExist(tokenAddress);
// Mark that this symbol is an external token, which is needed to differentiate between operations on mint and burn.
_setTokenType(symbol, TokenType.External);
}
// slither-disable-next-line reentrancy-events
emit TokenDeployed(symbol, tokenAddress);
_setTokenAddress(symbol, tokenAddress);
}
/**
* @notice Transfers a specific amount of tokens to an account, based on the provided symbol.
* @param params Encoded parameters including the token symbol, recipient address, and amount to mint.
* @dev This function will revert if the token is not registered with the gatewaty.
* @dev If the token type is External, a safe transfer is performed to the recipient account.
* @dev If the token type is Internal (InternalBurnable or InternalBurnableFrom), the mint function is called on the token address.
*/
function mintToken(bytes calldata params, bytes32) external onlySelf {
(string memory symbol, address account, uint256 amount) = abi.decode(params, (string, address, uint256));
_mintToken(symbol, account, amount);
}
/**
* @notice Burns tokens of a given symbol, either through an external deposit handler or a token defined burn method.
* @param params Encoded parameters including the token symbol and a salt value for the deposit handler
*/
function burnToken(bytes calldata params, bytes32) external onlySelf {
(string memory symbol, bytes32 salt) = abi.decode(params, (string, bytes32));
address tokenAddress = tokenAddresses(symbol);
if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
if (_getTokenType(symbol) == TokenType.External) {
address depositHandlerAddress = _getCreate2Address(salt, keccak256(abi.encodePacked(type(DepositHandler).creationCode)));
if (depositHandlerAddress.isContract()) return;
DepositHandler depositHandler = new DepositHandler{ salt: salt }();
(bool success, bytes memory returnData) = depositHandler.execute(
tokenAddress,
abi.encodeWithSelector(IERC20.transfer.selector, address(this), IERC20(tokenAddress).balanceOf(address(depositHandler)))
);
if (!success || (returnData.length != uint256(0) && !abi.decode(returnData, (bool)))) revert BurnFailed(symbol);
// NOTE: `depositHandler` must always be destroyed in the same runtime context that it is deployed.
depositHandler.destroy(address(this));
} else {
IBurnableMintableCappedERC20(tokenAddress).burn(salt);
}
}
/**
* @notice Approves a contract call.
* @param params Encoded parameters including the source chain, source address, contract address, payload hash, transaction hash, and event index
* @param commandId to associate with the approval
*/
function approveContractCall(bytes calldata params, bytes32 commandId) external onlySelf {
(
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash,
bytes32 sourceTxHash,
uint256 sourceEventIndex
) = abi.decode(params, (string, string, address, bytes32, bytes32, uint256));
_setContractCallApproved(commandId, sourceChain, sourceAddress, contractAddress, payloadHash);
emit ContractCallApproved(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, sourceTxHash, sourceEventIndex);
}
/**
* @notice Approves a contract call with token transfer.
* @param params Encoded parameters including the source chain, source address, contract address, payload hash, token symbol,
* token amount, transaction hash, and event index.
* @param commandId to associate with the approval
*/
function approveContractCallWithMint(bytes calldata params, bytes32 commandId) external onlySelf {
(
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash,
string memory symbol,
uint256 amount,
bytes32 sourceTxHash,
uint256 sourceEventIndex
) = abi.decode(params, (string, string, address, bytes32, string, uint256, bytes32, uint256));
_setContractCallApprovedWithMint(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount);
emit ContractCallApprovedWithMint(
commandId,
sourceChain,
sourceAddress,
contractAddress,
payloadHash,
symbol,
amount,
sourceTxHash,
sourceEventIndex
);
}
/**
* @notice Transfers operatorship with the provided data by calling the transferOperatorship function on the auth module.
* @param newOperatorsData Encoded data for the new operators
*/
function transferOperatorship(bytes calldata newOperatorsData, bytes32) external onlySelf {
emit OperatorshipTransferred(newOperatorsData);
IAxelarAuth(authModule).transferOperatorship(newOperatorsData);
}
/********************\\
|* Internal Methods *|
\\********************/
function _mintToken(
string memory symbol,
address account,
uint256 amount
) internal {
address tokenAddress = tokenAddresses(symbol);
if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
_setTokenMintAmount(symbol, tokenMintAmount(symbol) + amount);
if (_getTokenType(symbol) == TokenType.External) {
IERC20(tokenAddress).safeTransfer(account, amount);
} else {
IBurnableMintableCappedERC20(tokenAddress).mint(account, amount);
}
}
/**
* @notice Burns or locks a specific amount of tokens from a sender's account based on the provided symbol.
* @param sender Address of the account from which to burn the tokens
* @param symbol Symbol of the token to burn
* @param amount Amount of tokens to burn
* @dev Depending on the token type (External, InternalBurnableFrom, or InternalBurnable), the function either
* transfers the tokens to gateway contract itself or calls a burn function on the token contract.
*/
function _burnTokenFrom(
address sender,
string memory symbol,
uint256 amount
) internal {
address tokenAddress = tokenAddresses(symbol);
if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
if (amount == 0) revert InvalidAmount();
TokenType tokenType = _getTokenType(symbol);
if (tokenType == TokenType.External) {
IERC20(tokenAddress).safeTransferFrom(sender, address(this), amount);
} else if (tokenType == TokenType.InternalBurnableFrom) {
IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IBurnableMintableCappedERC20.burnFrom.selector, sender, amount));
} else {
IERC20(tokenAddress).safeTransferFrom(sender, IBurnableMintableCappedERC20(tokenAddress).depositAddress(bytes32(0)), amount);
IBurnableMintableCappedERC20(tokenAddress).burn(bytes32(0));
}
}
/********************\\
|* Pure Key Getters *|
\\********************/
function _getTokenMintLimitKey(string memory symbol) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(PREFIX_TOKEN_MINT_LIMIT, symbol));
}
function _getTokenMintAmountKey(string memory symbol, uint256 day) internal pure returns (bytes32) {
return keccak256(abi.encode(PREFIX_TOKEN_MINT_AMOUNT, symbol, day));
}
function _getTokenTypeKey(string memory symbol) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(PREFIX_TOKEN_TYPE, symbol));
}
function _getTokenAddressKey(string memory symbol) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(PREFIX_TOKEN_ADDRESS, symbol));
}
function _getIsCommandExecutedKey(bytes32 commandId) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(PREFIX_COMMAND_EXECUTED, commandId));
}
function _getIsContractCallApprovedKey(
bytes32 commandId,
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash
) internal pure returns (bytes32) {
return keccak256(abi.encode(PREFIX_CONTRACT_CALL_APPROVED, commandId, sourceChain, sourceAddress, contractAddress, payloadHash));
}
function _getIsContractCallApprovedWithMintKey(
bytes32 commandId,
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash,
string memory symbol,
uint256 amount
) internal pure returns (bytes32) {
return
keccak256(
abi.encode(
PREFIX_CONTRACT_CALL_APPROVED_WITH_MINT,
commandId,
sourceChain,
sourceAddress,
contractAddress,
payloadHash,
symbol,
amount
)
);
}
/********************\\
|* Internal Getters *|
\\********************/
function _getCreate2Address(bytes32 salt, bytes32 codeHash) internal view returns (address) {
return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, codeHash)))));
}
function _getTokenType(string memory symbol) internal view returns (TokenType) {
return TokenType(getUint(_getTokenTypeKey(symbol)));
}
/********************\\
|* Internal Setters *|
\\********************/
function _setTokenMintLimit(string memory symbol, uint256 limit) internal {
emit TokenMintLimitUpdated(symbol, limit);
_setUint(_getTokenMintLimitKey(symbol), limit);
}
function _setTokenMintAmount(string memory symbol, uint256 amount) internal {
uint256 limit = tokenMintLimit(symbol);
if (limit > 0 && amount > limit) revert ExceedMintLimit(symbol);
_setUint(_getTokenMintAmountKey(symbol, block.timestamp / 6 hours), amount);
}
function _setTokenType(string memory symbol, TokenType tokenType) internal {
_setUint(_getTokenTypeKey(symbol), uint256(tokenType));
}
function _setTokenAddress(string memory symbol, address tokenAddress) internal {
_setAddress(_getTokenAddressKey(symbol), tokenAddress);
}
function _setCommandExecuted(bytes32 commandId, bool executed) internal {
_setBool(_getIsCommandExecutedKey(commandId), executed);
}
function _setContractCallApproved(
bytes32 commandId,
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash
) internal {
_setBool(_getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash), true);
}
function _setContractCallApprovedWithMint(
bytes32 commandId,
string memory sourceChain,
string memory sourceAddress,
address contractAddress,
bytes32 payloadHash,
string memory symbol,
uint256 amount
) internal {
_setBool(
_getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount),
true
);
}
function _setImplementation(address newImplementation) internal {
_setAddress(KEY_IMPLEMENTATION, newImplementation);
}
function _transferGovernance(address newGovernance) internal {
emit GovernanceTransferred(getAddress(KEY_GOVERNANCE), newGovernance);
_setAddress(KEY_GOVERNANCE, newGovernance);
}
function _transferMintLimiter(address newMintLimiter) internal {
emit MintLimiterTransferred(getAddress(KEY_MINT_LIMITER), newMintLimiter);
_setAddress(KEY_MINT_LIMITER, newMintLimiter);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
contract DepositHandler {
error IsLocked();
error NotContract();
uint256 internal constant IS_NOT_LOCKED = uint256(1);
uint256 internal constant IS_LOCKED = uint256(2);
uint256 internal _lockedStatus = IS_NOT_LOCKED;
modifier noReenter() {
if (_lockedStatus == IS_LOCKED) revert IsLocked();
_lockedStatus = IS_LOCKED;
_;
_lockedStatus = IS_NOT_LOCKED;
}
function execute(address callee, bytes calldata data) external noReenter returns (bool success, bytes memory returnData) {
if (callee.code.length == 0) revert NotContract();
(success, returnData) = callee.call(data);
}
// NOTE: The gateway should always destroy the `DepositHandler` in the same runtime context that deploys it.
function destroy(address etherDestination) external noReenter {
selfdestruct(payable(etherDestination));
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
error InvalidSignatureLength();
error InvalidS();
error InvalidV();
error InvalidSignature();
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address signer) {
// Check the signature length
if (signature.length != 65) revert InvalidSignatureLength();
// Divide the signature in r, s and v variables
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
// solhint-disable-next-line no-inline-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (281): 0 < s < secp256k1n ÷ 2 + 1, and for v in (282): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) revert InvalidS();
if (v != 27 && v != 28) revert InvalidV();
// If the signature is valid (and not malleable), return the signer address
if ((signer = ecrecover(hash, v, r, s)) == address(0)) revert InvalidSignature();
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* replicates the behavior of the
* https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
* JSON-RPC method.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
return keccak256(abi.encodePacked('\\x19Ethereum Signed Message:\
32', hash));
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.9;
/**
* @title EternalStorage
* @dev This contract holds all the necessary state variables to carry out the storage of any contract.
*/
contract EternalStorage {
mapping(bytes32 => uint256) private _uintStorage;
mapping(bytes32 => string) private _stringStorage;
mapping(bytes32 => address) private _addressStorage;
mapping(bytes32 => bytes) private _bytesStorage;
mapping(bytes32 => bool) private _boolStorage;
mapping(bytes32 => int256) private _intStorage;
// *** Getter Methods ***
function getUint(bytes32 key) public view returns (uint256) {
return _uintStorage[key];
}
function getString(bytes32 key) public view returns (string memory) {
return _stringStorage[key];
}
function getAddress(bytes32 key) public view returns (address) {
return _addressStorage[key];
}
function getBytes(bytes32 key) public view returns (bytes memory) {
return _bytesStorage[key];
}
function getBool(bytes32 key) public view returns (bool) {
return _boolStorage[key];
}
function getInt(bytes32 key) public view returns (int256) {
return _intStorage[key];
}
// *** Setter Methods ***
function _setUint(bytes32 key, uint256 value) internal {
_uintStorage[key] = value;
}
function _setString(bytes32 key, string memory value) internal {
_stringStorage[key] = value;
}
function _setAddress(bytes32 key, address value) internal {
_addressStorage[key] = value;
}
function _setBytes(bytes32 key, bytes memory value) internal {
_bytesStorage[key] = value;
}
function _setBool(bytes32 key, bool value) internal {
_boolStorage[key] = value;
}
function _setInt(bytes32 key, int256 value) internal {
_intStorage[key] = value;
}
// *** Delete Methods ***
function _deleteUint(bytes32 key) internal {
delete _uintStorage[key];
}
function _deleteString(bytes32 key) internal {
delete _stringStorage[key];
}
function _deleteAddress(bytes32 key) internal {
delete _addressStorage[key];
}
function _deleteBytes(bytes32 key) internal {
delete _bytesStorage[key];
}
function _deleteBool(bytes32 key) internal {
delete _boolStorage[key];
}
function _deleteInt(bytes32 key) internal {
delete _intStorage[key];
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import { IOwnable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IOwnable.sol';
interface IAxelarAuth is IOwnable {
function validateProof(bytes32 messageHash, bytes calldata proof) external returns (bool currentOperators);
function transferOperatorship(bytes calldata params) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { IERC20Burn } from './IERC20Burn.sol';
import { IERC20BurnFrom } from './IERC20BurnFrom.sol';
import { IMintableCappedERC20 } from './IMintableCappedERC20.sol';
interface IBurnableMintableCappedERC20 is IERC20Burn, IERC20BurnFrom, IMintableCappedERC20 {
function depositAddress(bytes32 salt) external view returns (address);
function burn(bytes32 salt) external;
function burnFrom(address account, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/**
* @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);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IERC20Burn {
function burn(bytes32 salt) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IERC20BurnFrom {
function burnFrom(address account, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IERC20Permit {
function DOMAIN_SEPARATOR() external view returns (bytes32);
function nonces(address account) external view returns (uint256);
function permit(
address issuer,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import { IERC20 } from './IERC20.sol';
import { IERC20Permit } from './IERC20Permit.sol';
import { IOwnable } from './IOwnable.sol';
interface IMintableCappedERC20 is IERC20, IERC20Permit, IOwnable {
error CapExceeded();
function cap() external view returns (uint256);
function mint(address account, uint256 amount) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IOwnable {
error NotOwner();
error InvalidOwner();
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function owner() external view returns (address);
function transferOwnership(address newOwner) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITokenDeployer {
function deployToken(
string calldata name,
string calldata symbol,
uint8 decimals,
uint256 cap,
bytes32 salt
) external returns (address tokenAddress);
}