Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
19507765 | 274 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
TokenManager
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 1000 runs
Other Settings:
london EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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 { 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 { 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 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; // 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 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 { 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); 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 { 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) } 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 } }
{ "evmVersion": "london", "optimizer": { "enabled": true, "runs": 1000, "details": { "peephole": true, "inliner": true, "jumpdestRemover": true, "orderLiterals": true, "deduplicate": true, "cse": true, "constantOptimizer": true, "yul": true, "yulDetails": { "stackAllocation": true } } }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"interchainTokenService_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"flowLimiter","type":"address"}],"name":"AlreadyFlowLimiter","type":"error"},{"inputs":[{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"uint256","name":"flowAmount","type":"uint256"},{"internalType":"address","name":"tokenManager","type":"address"}],"name":"FlowLimitExceeded","type":"error"},{"inputs":[],"name":"GiveTokenFailed","type":"error"},{"inputs":[{"internalType":"bytes","name":"bytesAddress","type":"bytes"}],"name":"InvalidBytesLength","type":"error"},{"inputs":[{"internalType":"address","name":"fromAccount","type":"address"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint256","name":"accountRoles","type":"uint256"}],"name":"InvalidProposedRoles","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"accountRoles","type":"uint256"}],"name":"MissingAllRoles","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"accountRoles","type":"uint256"}],"name":"MissingAnyOfRoles","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint8","name":"role","type":"uint8"}],"name":"MissingRole","type":"error"},{"inputs":[{"internalType":"address","name":"flowLimiter","type":"address"}],"name":"NotFlowLimiter","type":"error"},{"inputs":[],"name":"NotProxy","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"NotService","type":"error"},{"inputs":[],"name":"NotSupported","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"NotToken","type":"error"},{"inputs":[],"name":"TakeTokenFailed","type":"error"},{"inputs":[],"name":"TokenLinkerZeroAddress","type":"error"},{"inputs":[],"name":"TokenTransferFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"tokenId","type":"bytes32"},{"indexed":false,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"uint256","name":"flowLimit_","type":"uint256"}],"name":"FlowLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"accountRoles","type":"uint256"}],"name":"RolesAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"fromAccount","type":"address"},{"indexed":true,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"accountRoles","type":"uint256"}],"name":"RolesProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"accountRoles","type":"uint256"}],"name":"RolesRemoved","type":"event"},{"inputs":[{"internalType":"address","name":"fromOperator","type":"address"}],"name":"acceptOperatorship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addFlowIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"flowLimiter","type":"address"}],"name":"addFlowLimiter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addFlowOut","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"approveService","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress_","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"contractId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"flowInAmount","outputs":[{"internalType":"uint256","name":"flowInAmount_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flowLimit","outputs":[{"internalType":"uint256","name":"flowLimit_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flowOutAmount","outputs":[{"internalType":"uint256","name":"flowOutAmount_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"params_","type":"bytes"}],"name":"getTokenAddressFromParams","outputs":[{"internalType":"address","name":"tokenAddress_","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint8","name":"role","type":"uint8"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementationType","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"interchainTokenId","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"interchainTokenService","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isFlowLimiter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"isOperator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tokenAddress_","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mintToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"operator_","type":"bytes"},{"internalType":"address","name":"tokenAddress_","type":"address"}],"name":"params","outputs":[{"internalType":"bytes","name":"params_","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"proposeOperatorship","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"flowLimiter","type":"address"}],"name":"removeFlowLimiter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"flowLimit_","type":"uint256"}],"name":"setFlowLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"params_","type":"bytes"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"transferOperatorship","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60c060405234801561001057600080fd5b5060405161175838038061175883398101604081905261002f9161006b565b306080526001600160a01b03811661005a5760405163ba675fc760e01b815260040160405180910390fd5b6001600160a01b031660a05261009b565b60006020828403121561007d57600080fd5b81516001600160a01b038116811461009457600080fd5b9392505050565b60805160a0516116676100f1600039600081816101ad015281816103c9015281816104da0152818161052001528181610669015281816107ad0152818161096d0152610a51015260006108ca01526116676000f3fe608060405234801561001057600080fd5b50600436106101a35760003560e01c80636f3eef62116100ee5780639ded06df11610097578063dce2913611610071578063dce2913614610372578063deb11e7814610385578063e915cfd114610398578063f5983e83146103ab57600080fd5b80639ded06df14610339578063a56dbe631461034c578063da4886df1461035f57600080fd5b80638b38b35d116100c85780638b38b35d146102f757806395a8c58d1461031e5780639d76ea581461033157600080fd5b80636f3eef62146102b65780637dbab19b146102c95780638291286c146102d157600080fd5b80632f3c7888116101505780634fdf7cb51161012a5780634fdf7cb5146102145780636bec32da146102805780636d70f7ae1461029357600080fd5b80632f3c7888146102525780633416794d1461025a5780634a6a42d81461026d57600080fd5b8063129d818811610181578063129d8188146102145780631c53d2651461022a578063274158381461024a57600080fd5b806309c6bed9146101a857806310d8d8e3146101ec578063120a63b514610201575b600080fd5b6101cf7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6101ff6101fa366004611215565b6103be565b005b6101ff61020f366004611243565b61041a565b61021c61046c565b6040519081526020016101e3565b61023d6102383660046112a9565b6104a0565b6040516101e39190611324565b6101ff6104cf565b61021c61063a565b6101ff610268366004611357565b61065e565b6101ff61027b366004611243565b610760565b6101ff61028e366004611357565b6107a2565b6102a66102a1366004611243565b610834565b60405190151581526020016101e3565b6101ff6102c4366004611243565b610847565b61021c610889565b7fde5fa9c0ef6d18cdb8ef4461bfb45c94dc915629a2a1f311fb33838aff95436f61021c565b7f201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f5461021c565b6102a661032c366004611398565b6108a5565b6101cf61046c565b6101ff6103473660046113d7565b6108c7565b6101ff61035a366004611215565b610999565b6101ff61036d366004611243565b610a3a565b6101ff610380366004611215565b610a46565b6102a6610393366004611243565b610a9a565b6101ff6103a6366004611243565b610aa7565b6101cf6103b93660046113d7565b610ae8565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461040e57604051630d6c7be960e01b81523360048201526024015b60405180910390fd5b61041781610afe565b50565b600161043561042833610b73565b600160ff84161b16151590565b61045d5760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b610468826002610b7f565b5050565b60006040517fa038794000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608383836040516020016104b793929190611419565b60405160208183030381529060405290509392505050565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461051a57604051630d6c7be960e01b8152336004820152602401610405565b604080517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031660248201526000196044808301919091528251808303909101815260649091018252602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b30000000000000000000000000000000000000000000000000000000017905282517f9d76ea580000000000000000000000000000000000000000000000000000000081529251610638933092639d76ea58926004808401938290030181865afa158015610605573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610629919061145b565b6001600160a01b031690610b8f565b565b60008061064961546042611478565b9050600061065682610c66565b549392505050565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146106a957604051630d6c7be960e01b8152336004820152602401610405565b6040516001600160a01b03831660248201526044810182905261075b907f9dc29fac00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526001600160a01b03851690610b8f565b505050565b600161076e61042833610b73565b6107965760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b61046833836001610cbb565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146107ed57604051630d6c7be960e01b8152336004820152602401610405565b6040516001600160a01b03831660248201526044810182905261075b907f40c10f1900000000000000000000000000000000000000000000000000000000906064016106ec565b60006108418260016108a5565b92915050565b600161085561042833610b73565b61087d5760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b61046833836001610ccc565b60008061089861546042611478565b9050600061065682610cdd565b60006108c06108b384610b73565b600160ff85161b16151590565b9392505050565b307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031603610929576040517fbf10dd3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109378284018461153d565b9050600081516000146109505761094d82610d18565b90505b6109688160015b600160ff919091161b600417610d5f565b6109937f00000000000000000000000000000000000000000000000000000000000000006001610957565b50505050565b60026109a761042833610b73565b6109cf5760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b61046882306001600160a01b031663129d81886040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a359190611572565b610dc0565b61041781336001610e23565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610a9157604051630d6c7be960e01b8152336004820152602401610405565b61041781610e34565b60006108418260026108a5565b6001610ab561042833610b73565b610add5760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b610468826002610e94565b6000610af68284018461158b565b949350505050565b6000610b287f201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f5490565b905080600003610b36575050565b6000610b4461546042611478565b90506000610b5182610cdd565b90506000610b5e83610c66565b9050610b6c84838388610ea4565b5050505050565b60008061065683610f38565b61046882600160ff84161b610d5f565b600080836001600160a01b031683604051610baa91906115d2565b6000604051808303816000865af19150503d8060008114610be7576040519150601f19603f3d011682016040523d82523d6000602084013e610bec565b606091505b50915091506000828015610c18575081511580610c18575081806020019051810190610c1891906115ee565b9050801580610c2f57506001600160a01b0385163b155b15610b6c576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080517fb221f782e2afd1b10c9284a915bb8aa319f8e6cc8d90aebea117327eafbd28c160208201529081018290526000906060015b60408051601f19818403018152919052805160209091012092915050565b61075b8383600160ff85161b610fa5565b61075b8383600160ff85161b610ffb565b604080517ff4ff4213eda59f47bb3fdf8cb7fcc07540628bdeb7f1aebcfe07049bbd9f634f6020820152908101829052600090606001610c9d565b60008151601414610d5757816040517fd08dbec50000000000000000000000000000000000000000000000000000000081526004016104059190611324565b506014015190565b600081610d6b84610b73565b179050610d788382611093565b826001600160a01b03167f34e73c57659d4b6809b53db4feee9b007b892e978114eda420d2991aba15014383604051610db391815260200190565b60405180910390a2505050565b7f201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f829055604080513381526020810184905282917f024e856c5f6f5e287ff2be13db089b016f28a6ebe6aaffdfb5fa5b902fdd366b910160405180910390a25050565b61075b8383600160ff85161b6110a6565b6000610e5e7f201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f5490565b905080600003610e6c575050565b6000610e7a61546042611478565b90506000610e8782610c66565b90506000610b5e83610cdd565b61046882600160ff84161b611119565b82548254610eb28682611610565b610ebc8484611610565b1115610efe57610ecc8682611610565b610ed68484611610565b604051632bab62d160e01b815260048101929092526024820152306044820152606401610405565b85831115610f2f57604051632bab62d160e01b81526004810187905260248101849052306044820152606401610405565b50019091555050565b60007fde9bdca322e1a848f72215bc15cf2c87fe7749145789a9ee281a2a6290af26ab82604051602001610f8892919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b604051602081830303815290604052805190602001209050919050565b610fb8610fb184610b73565b8216821490565b610fe757604051631fe9beed60e21b81526001600160a01b038416600482015260248101829052604401610405565b610ff18382611119565b61075b8282610d5f565b611007610fb184610b73565b61103657604051631fe9beed60e21b81526001600160a01b038416600482015260248101829052604401610405565b61104183838361116e565b816001600160a01b0316836001600160a01b03167ff7158d1591c2cf17c0e6b9459d86365c47fe0969c79f40ef49e0c437d8f399148360405161108691815260200190565b60405180910390a3505050565b600061109e83610f38565b919091555050565b806110b18484611183565b14611102576040517f6004fe400000000000000000000000000000000000000000000000000000000081526001600160a01b0380851660048301528316602482015260448101829052606401610405565b61110e8383600061116e565b61075b838383610fa5565b6000811961112684610b73565b1690506111338382611093565b826001600160a01b03167fccf920c8facee98a9c2a6c6124f2857b87b17e9f3a819bfcc6945196ee77366b83604051610db391815260200190565b600061117a8484611199565b91909155505050565b6000806111908484611199565b54949350505050565b60007ff96e07b2f4fbb81c31567d2b261589af429e98f0958d53f7e6ad5d63aea0ab7c83836040516020016111f793929190928352606091821b6bffffffffffffffffffffffff199081166020850152911b16603482015260480190565b60405160208183030381529060405280519060200120905092915050565b60006020828403121561122757600080fd5b5035919050565b6001600160a01b038116811461041757600080fd5b60006020828403121561125557600080fd5b81356108c08161122e565b60008083601f84011261127257600080fd5b50813567ffffffffffffffff81111561128a57600080fd5b6020830191508360208285010111156112a257600080fd5b9250929050565b6000806000604084860312156112be57600080fd5b833567ffffffffffffffff8111156112d557600080fd5b6112e186828701611260565b90945092505060208401356112f58161122e565b809150509250925092565b60005b8381101561131b578181015183820152602001611303565b50506000910152565b6020815260008251806020840152611343816040850160208701611300565b601f01601f19169190910160400192915050565b60008060006060848603121561136c57600080fd5b83356113778161122e565b925060208401356113878161122e565b929592945050506040919091013590565b600080604083850312156113ab57600080fd5b82356113b68161122e565b9150602083013560ff811681146113cc57600080fd5b809150509250929050565b600080602083850312156113ea57600080fd5b823567ffffffffffffffff81111561140157600080fd5b61140d85828601611260565b90969095509350505050565b604081528260408201528284606083013760006060848301015260006060601f19601f86011683010190506001600160a01b0383166020830152949350505050565b60006020828403121561146d57600080fd5b81516108c08161122e565b60008261149557634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126114c157600080fd5b813567ffffffffffffffff808211156114dc576114dc61149a565b604051601f8301601f19908116603f011681019082821181831017156115045761150461149a565b8160405283815286602085880101111561151d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006020828403121561154f57600080fd5b813567ffffffffffffffff81111561156657600080fd5b610af6848285016114b0565b60006020828403121561158457600080fd5b5051919050565b6000806040838503121561159e57600080fd5b823567ffffffffffffffff8111156115b557600080fd5b6115c1858286016114b0565b92505060208301356113cc8161122e565b600082516115e4818460208701611300565b9190910192915050565b60006020828403121561160057600080fd5b815180151581146108c057600080fd5b8082018082111561084157634e487b7160e01b600052601160045260246000fdfea2646970667358221220f18fec8fc035fcabccfd6346e8acaad5cc555ca3c50b292850b2b7741e18cf7f64736f6c63430008150033000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101a35760003560e01c80636f3eef62116100ee5780639ded06df11610097578063dce2913611610071578063dce2913614610372578063deb11e7814610385578063e915cfd114610398578063f5983e83146103ab57600080fd5b80639ded06df14610339578063a56dbe631461034c578063da4886df1461035f57600080fd5b80638b38b35d116100c85780638b38b35d146102f757806395a8c58d1461031e5780639d76ea581461033157600080fd5b80636f3eef62146102b65780637dbab19b146102c95780638291286c146102d157600080fd5b80632f3c7888116101505780634fdf7cb51161012a5780634fdf7cb5146102145780636bec32da146102805780636d70f7ae1461029357600080fd5b80632f3c7888146102525780633416794d1461025a5780634a6a42d81461026d57600080fd5b8063129d818811610181578063129d8188146102145780631c53d2651461022a578063274158381461024a57600080fd5b806309c6bed9146101a857806310d8d8e3146101ec578063120a63b514610201575b600080fd5b6101cf7f000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c81565b6040516001600160a01b0390911681526020015b60405180910390f35b6101ff6101fa366004611215565b6103be565b005b6101ff61020f366004611243565b61041a565b61021c61046c565b6040519081526020016101e3565b61023d6102383660046112a9565b6104a0565b6040516101e39190611324565b6101ff6104cf565b61021c61063a565b6101ff610268366004611357565b61065e565b6101ff61027b366004611243565b610760565b6101ff61028e366004611357565b6107a2565b6102a66102a1366004611243565b610834565b60405190151581526020016101e3565b6101ff6102c4366004611243565b610847565b61021c610889565b7fde5fa9c0ef6d18cdb8ef4461bfb45c94dc915629a2a1f311fb33838aff95436f61021c565b7f201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f5461021c565b6102a661032c366004611398565b6108a5565b6101cf61046c565b6101ff6103473660046113d7565b6108c7565b6101ff61035a366004611215565b610999565b6101ff61036d366004611243565b610a3a565b6101ff610380366004611215565b610a46565b6102a6610393366004611243565b610a9a565b6101ff6103a6366004611243565b610aa7565b6101cf6103b93660046113d7565b610ae8565b336001600160a01b037f000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c161461040e57604051630d6c7be960e01b81523360048201526024015b60405180910390fd5b61041781610afe565b50565b600161043561042833610b73565b600160ff84161b16151590565b61045d5760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b610468826002610b7f565b5050565b60006040517fa038794000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60608383836040516020016104b793929190611419565b60405160208183030381529060405290509392505050565b336001600160a01b037f000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c161461051a57604051630d6c7be960e01b8152336004820152602401610405565b604080517f000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c6001600160a01b031660248201526000196044808301919091528251808303909101815260649091018252602080820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167f095ea7b30000000000000000000000000000000000000000000000000000000017905282517f9d76ea580000000000000000000000000000000000000000000000000000000081529251610638933092639d76ea58926004808401938290030181865afa158015610605573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610629919061145b565b6001600160a01b031690610b8f565b565b60008061064961546042611478565b9050600061065682610c66565b549392505050565b336001600160a01b037f000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c16146106a957604051630d6c7be960e01b8152336004820152602401610405565b6040516001600160a01b03831660248201526044810182905261075b907f9dc29fac00000000000000000000000000000000000000000000000000000000906064015b60408051601f198184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff00000000000000000000000000000000000000000000000000000000909316929092179091526001600160a01b03851690610b8f565b505050565b600161076e61042833610b73565b6107965760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b61046833836001610cbb565b336001600160a01b037f000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c16146107ed57604051630d6c7be960e01b8152336004820152602401610405565b6040516001600160a01b03831660248201526044810182905261075b907f40c10f1900000000000000000000000000000000000000000000000000000000906064016106ec565b60006108418260016108a5565b92915050565b600161085561042833610b73565b61087d5760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b61046833836001610ccc565b60008061089861546042611478565b9050600061065682610cdd565b60006108c06108b384610b73565b600160ff85161b16151590565b9392505050565b307f00000000000000000000000081a0545091864617e7037171fdfcbbdcfe3aeb236001600160a01b031603610929576040517fbf10dd3a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006109378284018461153d565b9050600081516000146109505761094d82610d18565b90505b6109688160015b600160ff919091161b600417610d5f565b6109937f000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c6001610957565b50505050565b60026109a761042833610b73565b6109cf5760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b61046882306001600160a01b031663129d81886040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a11573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a359190611572565b610dc0565b61041781336001610e23565b336001600160a01b037f000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c1614610a9157604051630d6c7be960e01b8152336004820152602401610405565b61041781610e34565b60006108418260026108a5565b6001610ab561042833610b73565b610add5760405163bb6c163960e01b815233600482015260ff82166024820152604401610405565b610468826002610e94565b6000610af68284018461158b565b949350505050565b6000610b287f201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f5490565b905080600003610b36575050565b6000610b4461546042611478565b90506000610b5182610cdd565b90506000610b5e83610c66565b9050610b6c84838388610ea4565b5050505050565b60008061065683610f38565b61046882600160ff84161b610d5f565b600080836001600160a01b031683604051610baa91906115d2565b6000604051808303816000865af19150503d8060008114610be7576040519150601f19603f3d011682016040523d82523d6000602084013e610bec565b606091505b50915091506000828015610c18575081511580610c18575081806020019051810190610c1891906115ee565b9050801580610c2f57506001600160a01b0385163b155b15610b6c576040517f045c4b0200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080517fb221f782e2afd1b10c9284a915bb8aa319f8e6cc8d90aebea117327eafbd28c160208201529081018290526000906060015b60408051601f19818403018152919052805160209091012092915050565b61075b8383600160ff85161b610fa5565b61075b8383600160ff85161b610ffb565b604080517ff4ff4213eda59f47bb3fdf8cb7fcc07540628bdeb7f1aebcfe07049bbd9f634f6020820152908101829052600090606001610c9d565b60008151601414610d5757816040517fd08dbec50000000000000000000000000000000000000000000000000000000081526004016104059190611324565b506014015190565b600081610d6b84610b73565b179050610d788382611093565b826001600160a01b03167f34e73c57659d4b6809b53db4feee9b007b892e978114eda420d2991aba15014383604051610db391815260200190565b60405180910390a2505050565b7f201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f829055604080513381526020810184905282917f024e856c5f6f5e287ff2be13db089b016f28a6ebe6aaffdfb5fa5b902fdd366b910160405180910390a25050565b61075b8383600160ff85161b6110a6565b6000610e5e7f201b7a0b7c19aaddc4ce9579b7df8d2db123805861bc7763627f13e04d8af42f5490565b905080600003610e6c575050565b6000610e7a61546042611478565b90506000610e8782610c66565b90506000610b5e83610cdd565b61046882600160ff84161b611119565b82548254610eb28682611610565b610ebc8484611610565b1115610efe57610ecc8682611610565b610ed68484611610565b604051632bab62d160e01b815260048101929092526024820152306044820152606401610405565b85831115610f2f57604051632bab62d160e01b81526004810187905260248101849052306044820152606401610405565b50019091555050565b60007fde9bdca322e1a848f72215bc15cf2c87fe7749145789a9ee281a2a6290af26ab82604051602001610f8892919091825260601b6bffffffffffffffffffffffff1916602082015260340190565b604051602081830303815290604052805190602001209050919050565b610fb8610fb184610b73565b8216821490565b610fe757604051631fe9beed60e21b81526001600160a01b038416600482015260248101829052604401610405565b610ff18382611119565b61075b8282610d5f565b611007610fb184610b73565b61103657604051631fe9beed60e21b81526001600160a01b038416600482015260248101829052604401610405565b61104183838361116e565b816001600160a01b0316836001600160a01b03167ff7158d1591c2cf17c0e6b9459d86365c47fe0969c79f40ef49e0c437d8f399148360405161108691815260200190565b60405180910390a3505050565b600061109e83610f38565b919091555050565b806110b18484611183565b14611102576040517f6004fe400000000000000000000000000000000000000000000000000000000081526001600160a01b0380851660048301528316602482015260448101829052606401610405565b61110e8383600061116e565b61075b838383610fa5565b6000811961112684610b73565b1690506111338382611093565b826001600160a01b03167fccf920c8facee98a9c2a6c6124f2857b87b17e9f3a819bfcc6945196ee77366b83604051610db391815260200190565b600061117a8484611199565b91909155505050565b6000806111908484611199565b54949350505050565b60007ff96e07b2f4fbb81c31567d2b261589af429e98f0958d53f7e6ad5d63aea0ab7c83836040516020016111f793929190928352606091821b6bffffffffffffffffffffffff199081166020850152911b16603482015260480190565b60405160208183030381529060405280519060200120905092915050565b60006020828403121561122757600080fd5b5035919050565b6001600160a01b038116811461041757600080fd5b60006020828403121561125557600080fd5b81356108c08161122e565b60008083601f84011261127257600080fd5b50813567ffffffffffffffff81111561128a57600080fd5b6020830191508360208285010111156112a257600080fd5b9250929050565b6000806000604084860312156112be57600080fd5b833567ffffffffffffffff8111156112d557600080fd5b6112e186828701611260565b90945092505060208401356112f58161122e565b809150509250925092565b60005b8381101561131b578181015183820152602001611303565b50506000910152565b6020815260008251806020840152611343816040850160208701611300565b601f01601f19169190910160400192915050565b60008060006060848603121561136c57600080fd5b83356113778161122e565b925060208401356113878161122e565b929592945050506040919091013590565b600080604083850312156113ab57600080fd5b82356113b68161122e565b9150602083013560ff811681146113cc57600080fd5b809150509250929050565b600080602083850312156113ea57600080fd5b823567ffffffffffffffff81111561140157600080fd5b61140d85828601611260565b90969095509350505050565b604081528260408201528284606083013760006060848301015260006060601f19601f86011683010190506001600160a01b0383166020830152949350505050565b60006020828403121561146d57600080fd5b81516108c08161122e565b60008261149557634e487b7160e01b600052601260045260246000fd5b500490565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126114c157600080fd5b813567ffffffffffffffff808211156114dc576114dc61149a565b604051601f8301601f19908116603f011681019082821181831017156115045761150461149a565b8160405283815286602085880101111561151d57600080fd5b836020870160208301376000602085830101528094505050505092915050565b60006020828403121561154f57600080fd5b813567ffffffffffffffff81111561156657600080fd5b610af6848285016114b0565b60006020828403121561158457600080fd5b5051919050565b6000806040838503121561159e57600080fd5b823567ffffffffffffffff8111156115b557600080fd5b6115c1858286016114b0565b92505060208301356113cc8161122e565b600082516115e4818460208701611300565b9190910192915050565b60006020828403121561160057600080fd5b815180151581146108c057600080fd5b8082018082111561084157634e487b7160e01b600052601160045260246000fdfea2646970667358221220f18fec8fc035fcabccfd6346e8acaad5cc555ca3c50b292850b2b7741e18cf7f64736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c
-----Decoded View---------------
Arg [0] : interchainTokenService_ (address): 0xB5FB4BE02232B1bBA4dC8f81dc24C26980dE9e3C
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000b5fb4be02232b1bba4dc8f81dc24c26980de9e3c
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.