ETH Price: $3,379.53 (-0.23%)
Gas: 3 Gwei

Contract

0xEc8DDCb498b44C35EFaD7e5e43E0Caf6D16A66E8
 
Transaction Hash
Method
Block
From
To
Value
Withdraw201152882024-06-18 1:10:1112 days ago1718673011IN
Interport: USDT Vault
0 ETH0.000340855.43413689
Withdraw200687282024-06-11 12:57:2319 days ago1718110643IN
Interport: USDT Vault
0 ETH0.00124315.57168159
Deposit200686712024-06-11 12:45:5919 days ago1718109959IN
Interport: USDT Vault
0 ETH0.0010883112.45279784
Approve200564652024-06-09 19:50:1120 days ago1717962611IN
Interport: USDT Vault
0 ETH0.000328477.11332862
Deposit200564592024-06-09 19:48:5920 days ago1717962539IN
Interport: USDT Vault
0 ETH0.000678377.76114739
Approve200563012024-06-09 19:17:2320 days ago1717960643IN
Interport: USDT Vault
0 ETH0.000429979.31146303
Deposit200563002024-06-09 19:17:1120 days ago1717960631IN
Interport: USDT Vault
0 ETH0.000758649.18376754
Withdraw200385332024-06-07 7:43:3523 days ago1717746215IN
Interport: USDT Vault
0 ETH0.0008788711.00839423
Deposit200383432024-06-07 7:05:3523 days ago1717743935IN
Interport: USDT Vault
0 ETH0.0011080812.67727056
Approve200381192024-06-07 6:20:2323 days ago1717741223IN
Interport: USDT Vault
0 ETH0.00045959.9484121
Deposit200381162024-06-07 6:19:4723 days ago1717741187IN
Interport: USDT Vault
0 ETH0.000806239.75845712
Withdraw200251702024-06-05 10:56:3525 days ago1717584995IN
Interport: USDT Vault
0 ETH0.0014719518.43701712
Approve200182712024-06-04 11:50:5926 days ago1717501859IN
Interport: USDT Vault
0 ETH0.000350577.59001569
Deposit200182692024-06-04 11:50:3526 days ago1717501835IN
Interport: USDT Vault
0 ETH0.000615647.45166284
Withdraw199707372024-05-28 20:25:1132 days ago1716927911IN
Interport: USDT Vault
0 ETH0.001064613.33669085
Approve199585032024-05-27 3:24:4734 days ago1716780287IN
Interport: USDT Vault
0 ETH0.000317396.87349429
Deposit199584992024-05-27 3:23:5934 days ago1716780239IN
Interport: USDT Vault
0 ETH0.000598896.85181881
Approve199233212024-05-22 5:23:5939 days ago1716355439IN
Interport: USDT Vault
0 ETH0.000272635.9040947
Deposit199233162024-05-22 5:22:5939 days ago1716355379IN
Interport: USDT Vault
0 ETH0.000482445.84028296
Approve199232092024-05-22 5:01:1139 days ago1716354071IN
Interport: USDT Vault
0 ETH0.000346127.4956485
Deposit199232012024-05-22 4:59:2339 days ago1716353963IN
Interport: USDT Vault
0 ETH0.000565466.84525529
Withdraw199167322024-05-21 7:15:5940 days ago1716275759IN
Interport: USDT Vault
0 ETH0.0006394110.19395914
Withdraw199159072024-05-21 4:30:4740 days ago1716265847IN
Interport: USDT Vault
0 ETH0.000501447.99285809
Withdraw199150422024-05-21 1:36:4740 days ago1716255407IN
Interport: USDT Vault
0 ETH0.0013108815.49050691
Withdraw199134162024-05-20 20:08:5940 days ago1716235739IN
Interport: USDT Vault
0 ETH0.0043437569.23760453
View all transactions

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vault

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 26 : Vault.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { ITokenBurn } from './interfaces/ITokenBurn.sol';
import { ITokenDecimals } from './interfaces/ITokenDecimals.sol';
import { AssetSpenderRole } from './roles/AssetSpenderRole.sol';
import { BalanceManagement } from './BalanceManagement.sol';
import { SystemVersionId } from './SystemVersionId.sol';
import { VaultBase } from './VaultBase.sol';
import { TokenBurnError } from './Errors.sol';
import './helpers/AddressHelper.sol' as AddressHelper;
import './helpers/TransferHelper.sol' as TransferHelper;

/**
 * @title Vault
 * @notice The vault contract
 */
contract Vault is SystemVersionId, VaultBase, AssetSpenderRole, BalanceManagement {
    /**
     * @dev The variable token contract address, can be a zero address
     */
    address public variableToken;

    /**
     * @dev The state of variable token and balance actions
     */
    bool public variableRepaymentEnabled;

    /**
     * @notice Emitted when the state of variable token and balance actions is updated
     * @param variableRepaymentEnabled The state of variable token and balance actions
     */
    event SetVariableRepaymentEnabled(bool indexed variableRepaymentEnabled);

    /**
     * @notice Emitted when the variable token contract address is updated
     * @param variableToken The address of the variable token contract
     */
    event SetVariableToken(address indexed variableToken);

    /**
     * @notice Emitted when the variable tokens are redeemed for the vault asset
     * @param caller The address of the vault asset receiver account
     * @param amount The amount of redeemed variable tokens
     */
    event RedeemVariableToken(address indexed caller, uint256 amount);

    /**
     * @notice Emitted when the variable token decimals do not match the vault asset
     */
    error TokenDecimalsError();

    /**
     * @notice Emitted when a variable token or balance action is not allowed
     */
    error VariableRepaymentNotEnabledError();

    /**
     * @notice Emitted when setting the variable token is attempted while the token is already set
     */
    error VariableTokenAlreadySetError();

    /**
     * @notice Emitted when a variable token action is attempted while the token address is not set
     */
    error VariableTokenNotSetError();

    /**
     * @notice Deploys the VariableToken contract
     * @param _asset The vault asset address
     * @param _name The ERC20 token name
     * @param _symbol The ERC20 token symbol
     * @param _assetSpenders The addresses of initial asset spenders
     * @param _depositAllowed The initial state of deposit availability
     * @param _variableRepaymentEnabled The initial state of variable token and balance actions
     * @param _owner The address of the initial owner of the contract
     * @param _managers The addresses of initial managers of the contract
     * @param _addOwnerToManagers The flag to optionally add the owner to the list of managers
     */
    constructor(
        address _asset,
        string memory _name,
        string memory _symbol,
        address[] memory _assetSpenders,
        bool _depositAllowed,
        bool _variableRepaymentEnabled,
        address _owner,
        address[] memory _managers,
        bool _addOwnerToManagers
    ) VaultBase(_asset, _name, _symbol, _depositAllowed) {
        for (uint256 index; index < _assetSpenders.length; index++) {
            _setAssetSpender(_assetSpenders[index], true);
        }

        _setVariableRepaymentEnabled(_variableRepaymentEnabled);

        _initRoles(_owner, _managers, _addOwnerToManagers);
    }

    /**
     * @notice Updates the Asset Spender role status for the account
     * @param _account The account address
     * @param _value The Asset Spender role status flag
     */
    function setAssetSpender(address _account, bool _value) external onlyManager {
        _setAssetSpender(_account, _value);
    }

    /**
     * @notice Sets the variable token contract address
     * @dev Setting the address value is possible only once
     * @param _variableToken The address of the variable token contract
     */
    function setVariableToken(address _variableToken) external onlyManager {
        if (variableToken != address(0)) {
            revert VariableTokenAlreadySetError();
        }

        AddressHelper.requireContract(_variableToken);

        if (ITokenDecimals(_variableToken).decimals() != decimals) {
            revert TokenDecimalsError();
        }

        variableToken = _variableToken;

        emit SetVariableToken(_variableToken);
    }

    /**
     * @notice Updates the state of variable token and balance actions
     * @param _variableRepaymentEnabled The state of variable token and balance actions
     */
    function setVariableRepaymentEnabled(bool _variableRepaymentEnabled) external onlyManager {
        _setVariableRepaymentEnabled(_variableRepaymentEnabled);
    }

    /**
     * @notice Requests the vault asset tokens
     * @param _amount The amount of the vault asset tokens
     * @param _to The address of the vault asset tokens receiver
     * @param _forVariableBalance True if the request is made for a variable balance repayment, otherwise false
     * @return assetAddress The address of the vault asset token
     */
    function requestAsset(
        uint256 _amount,
        address _to,
        bool _forVariableBalance
    ) external whenNotPaused onlyAssetSpender returns (address assetAddress) {
        if (_forVariableBalance && !variableRepaymentEnabled) {
            revert VariableRepaymentNotEnabledError();
        }

        TransferHelper.safeTransfer(asset, _to, _amount);

        return asset;
    }

    /**
     * @notice Redeems variable tokens for the vault asset
     * @param _amount The number of variable tokens to redeem
     */
    function redeemVariableToken(uint256 _amount) external whenNotPaused nonReentrant checkCaller {
        checkVariableTokenState();

        bool burnSuccess = ITokenBurn(variableToken).burn(msg.sender, _amount);

        if (!burnSuccess) {
            revert TokenBurnError();
        }

        emit RedeemVariableToken(msg.sender, _amount);

        TransferHelper.safeTransfer(asset, msg.sender, _amount);
    }

    /**
     * @notice Checks the status of the variable token and balance actions and the variable token address
     * @dev Throws an error if variable token actions are not allowed
     * @return The address of the variable token
     */
    function checkVariableTokenState() public view returns (address) {
        if (!variableRepaymentEnabled) {
            revert VariableRepaymentNotEnabledError();
        }

        if (variableToken == address(0)) {
            revert VariableTokenNotSetError();
        }

        return variableToken;
    }

    /**
     * @notice Getter of the reserved token flag
     * @dev Returns true if the provided token address is the address of the vault asset
     * @param _tokenAddress The address of the token
     * @return The reserved token flag
     */
    function isReservedToken(address _tokenAddress) public view override returns (bool) {
        return _tokenAddress == asset;
    }

    function _setVariableRepaymentEnabled(bool _variableRepaymentEnabled) private {
        variableRepaymentEnabled = _variableRepaymentEnabled;

        emit SetVariableRepaymentEnabled(_variableRepaymentEnabled);
    }
}

File 2 of 26 : Ownable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev 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.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract 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() {
        _transferOwnership(_msgSender());
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        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 {
        _transferOwnership(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");
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

File 3 of 26 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 4 of 26 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 5 of 26 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 6 of 26 : BalanceManagement.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { ITokenBalance } from './interfaces/ITokenBalance.sol';
import { ManagerRole } from './roles/ManagerRole.sol';
import './helpers/TransferHelper.sol' as TransferHelper;
import './Constants.sol' as Constants;

/**
 * @title BalanceManagement
 * @notice Base contract for the withdrawal of tokens, except for reserved ones
 */
abstract contract BalanceManagement is ManagerRole {
    /**
     * @notice Emitted when the specified token is reserved
     */
    error ReservedTokenError();

    /**
     * @notice Performs the withdrawal of tokens, except for reserved ones
     * @dev Use the "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" address for the native token
     * @param _tokenAddress The address of the token
     * @param _tokenAmount The amount of the token
     */
    function cleanup(address _tokenAddress, uint256 _tokenAmount) external onlyManager {
        if (isReservedToken(_tokenAddress)) {
            revert ReservedTokenError();
        }

        if (_tokenAddress == Constants.NATIVE_TOKEN_ADDRESS) {
            TransferHelper.safeTransferNative(msg.sender, _tokenAmount);
        } else {
            TransferHelper.safeTransfer(_tokenAddress, msg.sender, _tokenAmount);
        }
    }

    /**
     * @notice Getter of the token balance of the current contract
     * @dev Use the "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE" address for the native token
     * @param _tokenAddress The address of the token
     * @return The token balance of the current contract
     */
    function tokenBalance(address _tokenAddress) public view returns (uint256) {
        if (_tokenAddress == Constants.NATIVE_TOKEN_ADDRESS) {
            return address(this).balance;
        } else {
            return ITokenBalance(_tokenAddress).balanceOf(address(this));
        }
    }

    /**
     * @notice Getter of the reserved token flag
     * @dev Override to add reserved token addresses
     * @param _tokenAddress The address of the token
     * @return The reserved token flag
     */
    function isReservedToken(address _tokenAddress) public view virtual returns (bool) {
        // The function returns false by default.
        // The explicit return statement is omitted to avoid the unused parameter warning.
        // See https://github.com/ethereum/solidity/issues/5295
    }
}

File 7 of 26 : CallerGuard.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { ManagerRole } from './roles/ManagerRole.sol';
import './helpers/AddressHelper.sol' as AddressHelper;
import './Constants.sol' as Constants;
import './DataStructures.sol' as DataStructures;

/**
 * @title CallerGuard
 * @notice Base contract to control access from other contracts
 */
abstract contract CallerGuard is ManagerRole {
    /**
     * @dev Caller guard mode enumeration
     */
    enum CallerGuardMode {
        ContractForbidden,
        ContractList,
        ContractAllowed
    }

    /**
     * @dev Caller guard mode value
     */
    CallerGuardMode public callerGuardMode = CallerGuardMode.ContractForbidden;

    /**
     * @dev Registered contract list for "ContractList" mode
     */
    address[] public listedCallerGuardContractList;

    /**
     * @dev Registered contract list indices for "ContractList" mode
     */
    mapping(address /*account*/ => DataStructures.OptionalValue /*index*/)
        public listedCallerGuardContractIndexMap;

    /**
     * @notice Emitted when the caller guard mode is set
     * @param callerGuardMode The caller guard mode
     */
    event SetCallerGuardMode(CallerGuardMode indexed callerGuardMode);

    /**
     * @notice Emitted when a registered contract for "ContractList" mode is added or removed
     * @param contractAddress The contract address
     * @param isListed The registered contract list inclusion flag
     */
    event SetListedCallerGuardContract(address indexed contractAddress, bool indexed isListed);

    /**
     * @notice Emitted when the caller is not allowed to perform the intended action
     */
    error CallerGuardError(address caller);

    /**
     * @dev Modifier to check if the caller is allowed to perform the intended action
     */
    modifier checkCaller() {
        if (msg.sender != tx.origin) {
            bool condition = (callerGuardMode == CallerGuardMode.ContractAllowed ||
                (callerGuardMode == CallerGuardMode.ContractList &&
                    isListedCallerGuardContract(msg.sender)));

            if (!condition) {
                revert CallerGuardError(msg.sender);
            }
        }

        _;
    }

    /**
     * @notice Sets the caller guard mode
     * @param _callerGuardMode The caller guard mode
     */
    function setCallerGuardMode(CallerGuardMode _callerGuardMode) external onlyManager {
        callerGuardMode = _callerGuardMode;

        emit SetCallerGuardMode(_callerGuardMode);
    }

    /**
     * @notice Updates the list of registered contracts for the "ContractList" mode
     * @param _items The addresses and flags for the contracts
     */
    function setListedCallerGuardContracts(
        DataStructures.AccountToFlag[] calldata _items
    ) external onlyManager {
        for (uint256 index; index < _items.length; index++) {
            DataStructures.AccountToFlag calldata item = _items[index];

            if (item.flag) {
                AddressHelper.requireContract(item.account);
            }

            DataStructures.uniqueAddressListUpdate(
                listedCallerGuardContractList,
                listedCallerGuardContractIndexMap,
                item.account,
                item.flag,
                Constants.LIST_SIZE_LIMIT_DEFAULT
            );

            emit SetListedCallerGuardContract(item.account, item.flag);
        }
    }

    /**
     * @notice Getter of the registered contract count
     * @return The registered contract count
     */
    function listedCallerGuardContractCount() external view returns (uint256) {
        return listedCallerGuardContractList.length;
    }

    /**
     * @notice Getter of the complete list of registered contracts
     * @return The complete list of registered contracts
     */
    function fullListedCallerGuardContractList() external view returns (address[] memory) {
        return listedCallerGuardContractList;
    }

    /**
     * @notice Getter of a listed contract flag
     * @param _account The contract address
     * @return The listed contract flag
     */
    function isListedCallerGuardContract(address _account) public view returns (bool) {
        return listedCallerGuardContractIndexMap[_account].isSet;
    }
}

File 8 of 26 : Constants.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @dev The default token decimals value
 */
uint256 constant DECIMALS_DEFAULT = 18;

/**
 * @dev The maximum uint256 value for swap amount limit settings
 */
uint256 constant INFINITY = type(uint256).max;

/**
 * @dev The default limit of account list size
 */
uint256 constant LIST_SIZE_LIMIT_DEFAULT = 100;

/**
 * @dev The limit of swap router list size
 */
uint256 constant LIST_SIZE_LIMIT_ROUTERS = 200;

/**
 * @dev The factor for percentage settings. Example: 100 is 0.1%
 */
uint256 constant MILLIPERCENT_FACTOR = 100_000;

/**
 * @dev The de facto standard address to denote the native token
 */
address constant NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

File 9 of 26 : DataStructures.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @notice Optional value structure
 * @dev Is used in mappings to allow zero values
 * @param isSet Value presence flag
 * @param value Numeric value
 */
struct OptionalValue {
    bool isSet;
    uint256 value;
}

/**
 * @notice Key-to-value structure
 * @dev Is used as an array parameter item to perform multiple key-value settings
 * @param key Numeric key
 * @param value Numeric value
 */
struct KeyToValue {
    uint256 key;
    uint256 value;
}

/**
 * @notice Key-to-value structure for address values
 * @dev Is used as an array parameter item to perform multiple key-value settings with address values
 * @param key Numeric key
 * @param value Address value
 */
struct KeyToAddressValue {
    uint256 key;
    address value;
}

/**
 * @notice Address-to-flag structure
 * @dev Is used as an array parameter item to perform multiple settings
 * @param account Account address
 * @param flag Flag value
 */
struct AccountToFlag {
    address account;
    bool flag;
}

/**
 * @notice Emitted when a list exceeds the size limit
 */
error ListSizeLimitError();

/**
 * @notice Sets or updates a value in a combined map (a mapping with a key list and key index mapping)
 * @param _map The mapping reference
 * @param _keyList The key list reference
 * @param _keyIndexMap The key list index mapping reference
 * @param _key The numeric key
 * @param _value The address value
 * @param _sizeLimit The map and list size limit
 * @return isNewKey True if the key was just added, otherwise false
 */
function combinedMapSet(
    mapping(uint256 => address) storage _map,
    uint256[] storage _keyList,
    mapping(uint256 => OptionalValue) storage _keyIndexMap,
    uint256 _key,
    address _value,
    uint256 _sizeLimit
) returns (bool isNewKey) {
    isNewKey = !_keyIndexMap[_key].isSet;

    if (isNewKey) {
        uniqueListAdd(_keyList, _keyIndexMap, _key, _sizeLimit);
    }

    _map[_key] = _value;
}

/**
 * @notice Removes a value from a combined map (a mapping with a key list and key index mapping)
 * @param _map The mapping reference
 * @param _keyList The key list reference
 * @param _keyIndexMap The key list index mapping reference
 * @param _key The numeric key
 * @return isChanged True if the combined map was changed, otherwise false
 */
function combinedMapRemove(
    mapping(uint256 => address) storage _map,
    uint256[] storage _keyList,
    mapping(uint256 => OptionalValue) storage _keyIndexMap,
    uint256 _key
) returns (bool isChanged) {
    isChanged = _keyIndexMap[_key].isSet;

    if (isChanged) {
        delete _map[_key];
        uniqueListRemove(_keyList, _keyIndexMap, _key);
    }
}

/**
 * @notice Adds a value to a unique value list (a list with value index mapping)
 * @param _list The list reference
 * @param _indexMap The value index mapping reference
 * @param _value The numeric value
 * @param _sizeLimit The list size limit
 * @return isChanged True if the list was changed, otherwise false
 */
function uniqueListAdd(
    uint256[] storage _list,
    mapping(uint256 => OptionalValue) storage _indexMap,
    uint256 _value,
    uint256 _sizeLimit
) returns (bool isChanged) {
    isChanged = !_indexMap[_value].isSet;

    if (isChanged) {
        if (_list.length >= _sizeLimit) {
            revert ListSizeLimitError();
        }

        _indexMap[_value] = OptionalValue(true, _list.length);
        _list.push(_value);
    }
}

/**
 * @notice Removes a value from a unique value list (a list with value index mapping)
 * @param _list The list reference
 * @param _indexMap The value index mapping reference
 * @param _value The numeric value
 * @return isChanged True if the list was changed, otherwise false
 */
function uniqueListRemove(
    uint256[] storage _list,
    mapping(uint256 => OptionalValue) storage _indexMap,
    uint256 _value
) returns (bool isChanged) {
    OptionalValue storage indexItem = _indexMap[_value];

    isChanged = indexItem.isSet;

    if (isChanged) {
        uint256 itemIndex = indexItem.value;
        uint256 lastIndex = _list.length - 1;

        if (itemIndex != lastIndex) {
            uint256 lastValue = _list[lastIndex];
            _list[itemIndex] = lastValue;
            _indexMap[lastValue].value = itemIndex;
        }

        _list.pop();
        delete _indexMap[_value];
    }
}

/**
 * @notice Adds a value to a unique address value list (a list with value index mapping)
 * @param _list The list reference
 * @param _indexMap The value index mapping reference
 * @param _value The address value
 * @param _sizeLimit The list size limit
 * @return isChanged True if the list was changed, otherwise false
 */
function uniqueAddressListAdd(
    address[] storage _list,
    mapping(address => OptionalValue) storage _indexMap,
    address _value,
    uint256 _sizeLimit
) returns (bool isChanged) {
    isChanged = !_indexMap[_value].isSet;

    if (isChanged) {
        if (_list.length >= _sizeLimit) {
            revert ListSizeLimitError();
        }

        _indexMap[_value] = OptionalValue(true, _list.length);
        _list.push(_value);
    }
}

/**
 * @notice Removes a value from a unique address value list (a list with value index mapping)
 * @param _list The list reference
 * @param _indexMap The value index mapping reference
 * @param _value The address value
 * @return isChanged True if the list was changed, otherwise false
 */
function uniqueAddressListRemove(
    address[] storage _list,
    mapping(address => OptionalValue) storage _indexMap,
    address _value
) returns (bool isChanged) {
    OptionalValue storage indexItem = _indexMap[_value];

    isChanged = indexItem.isSet;

    if (isChanged) {
        uint256 itemIndex = indexItem.value;
        uint256 lastIndex = _list.length - 1;

        if (itemIndex != lastIndex) {
            address lastValue = _list[lastIndex];
            _list[itemIndex] = lastValue;
            _indexMap[lastValue].value = itemIndex;
        }

        _list.pop();
        delete _indexMap[_value];
    }
}

/**
 * @notice Adds or removes a value to/from a unique address value list (a list with value index mapping)
 * @dev The list size limit is checked on items adding only
 * @param _list The list reference
 * @param _indexMap The value index mapping reference
 * @param _value The address value
 * @param _flag The value inclusion flag
 * @param _sizeLimit The list size limit
 * @return isChanged True if the list was changed, otherwise false
 */
function uniqueAddressListUpdate(
    address[] storage _list,
    mapping(address => OptionalValue) storage _indexMap,
    address _value,
    bool _flag,
    uint256 _sizeLimit
) returns (bool isChanged) {
    return
        _flag
            ? uniqueAddressListAdd(_list, _indexMap, _value, _sizeLimit)
            : uniqueAddressListRemove(_list, _indexMap, _value);
}

File 10 of 26 : Errors.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @notice Emitted when an attempt to burn a token fails
 */
error TokenBurnError();

/**
 * @notice Emitted when an attempt to mint a token fails
 */
error TokenMintError();

/**
 * @notice Emitted when a zero address is specified where it is not allowed
 */
error ZeroAddressError();

File 11 of 26 : AddressHelper.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @notice Emitted when the account is not a contract
 * @param account The account address
 */
error NonContractAddressError(address account);

/**
 * @notice Function to check if the account is a contract
 * @return The account contract status flag
 */
function isContract(address _account) view returns (bool) {
    return _account.code.length > 0;
}

/**
 * @notice Function to require an account to be a contract
 */
function requireContract(address _account) view {
    if (!isContract(_account)) {
        revert NonContractAddressError(_account);
    }
}

/**
 * @notice Function to require an account to be a contract or a zero address
 */
function requireContractOrZeroAddress(address _account) view {
    if (_account != address(0)) {
        requireContract(_account);
    }
}

File 12 of 26 : TransferHelper.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @notice Emitted when an approval action fails
 */
error SafeApproveError();

/**
 * @notice Emitted when a transfer action fails
 */
error SafeTransferError();

/**
 * @notice Emitted when a transferFrom action fails
 */
error SafeTransferFromError();

/**
 * @notice Emitted when a transfer of the native token fails
 */
error SafeTransferNativeError();

/**
 * @notice Safely approve the token to the account
 * @param _token The token address
 * @param _to The token approval recipient address
 * @param _value The token approval amount
 */
function safeApprove(address _token, address _to, uint256 _value) {
    // 0x095ea7b3 is the selector for "approve(address,uint256)"
    (bool success, bytes memory data) = _token.call(
        abi.encodeWithSelector(0x095ea7b3, _to, _value)
    );

    bool condition = success && (data.length == 0 || abi.decode(data, (bool)));

    if (!condition) {
        revert SafeApproveError();
    }
}

/**
 * @notice Safely transfer the token to the account
 * @param _token The token address
 * @param _to The token transfer recipient address
 * @param _value The token transfer amount
 */
function safeTransfer(address _token, address _to, uint256 _value) {
    // 0xa9059cbb is the selector for "transfer(address,uint256)"
    (bool success, bytes memory data) = _token.call(
        abi.encodeWithSelector(0xa9059cbb, _to, _value)
    );

    bool condition = success && (data.length == 0 || abi.decode(data, (bool)));

    if (!condition) {
        revert SafeTransferError();
    }
}

/**
 * @notice Safely transfer the token between the accounts
 * @param _token The token address
 * @param _from The token transfer source address
 * @param _to The token transfer recipient address
 * @param _value The token transfer amount
 */
function safeTransferFrom(address _token, address _from, address _to, uint256 _value) {
    // 0x23b872dd is the selector for "transferFrom(address,address,uint256)"
    (bool success, bytes memory data) = _token.call(
        abi.encodeWithSelector(0x23b872dd, _from, _to, _value)
    );

    bool condition = success && (data.length == 0 || abi.decode(data, (bool)));

    if (!condition) {
        revert SafeTransferFromError();
    }
}

/**
 * @notice Safely transfer the native token to the account
 * @param _to The native token transfer recipient address
 * @param _value The native token transfer amount
 */
function safeTransferNative(address _to, uint256 _value) {
    (bool success, ) = _to.call{ value: _value }(new bytes(0));

    if (!success) {
        revert SafeTransferNativeError();
    }
}

File 13 of 26 : ITokenBalance.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @title ITokenBalance
 * @notice Token balance interface
 */
interface ITokenBalance {
    /**
     * @notice Getter of the token balance by the account
     * @param _account The account address
     * @return Token balance
     */
    function balanceOf(address _account) external view returns (uint256);
}

File 14 of 26 : ITokenBurn.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @title ITokenBurn
 * @notice Token burning interface
 */
interface ITokenBurn {
    /**
     * @notice Burns tokens from the account, reducing the total supply
     * @param _from The token holder account address
     * @param _amount The number of tokens to burn
     * @return Token burning success status
     */
    function burn(address _from, uint256 _amount) external returns (bool);
}

File 15 of 26 : ITokenDecimals.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @title ITokenDecimals
 * @notice Token decimals interface
 */
interface ITokenDecimals {
    /**
     * @notice Getter of the token decimals
     * @return Token decimals
     */
    function decimals() external pure returns (uint8);
}

File 16 of 26 : MultichainTokenBase.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { ERC20 } from 'solmate/src/tokens/ERC20.sol';
import { BurnerRole } from './roles/BurnerRole.sol';
import { MinterRole } from './roles/MinterRole.sol';
import { MultichainRouterRole } from './roles/MultichainRouterRole.sol';
import { Pausable } from './Pausable.sol';
import { ZeroAddressError } from './Errors.sol';
import './Constants.sol' as Constants;

/**
 * @title MultichainTokenBase
 * @notice Base contract that implements the Multichain token logic
 */
abstract contract MultichainTokenBase is Pausable, ERC20, MultichainRouterRole {
    /**
     * @dev Anyswap ERC20 standard
     */
    address public immutable underlying;

    bool private immutable useExplicitAccess;

    /**
     * @notice Emitted when token burning is not allowed to the caller
     */
    error BurnAccessError();

    /**
     * @notice Emitted when the token allowance is not sufficient for burning
     */
    error BurnAllowanceError();

    /**
     * @notice Emitted when token minting is not allowed to the caller
     */
    error MintAccessError();

    /**
     * @notice Initializes the MultichainTokenBase properties of descendant contracts
     * @param _name The ERC20 token name
     * @param _symbol The ERC20 token symbol
     * @param _decimals The ERC20 token decimals
     * @param _useExplicitAccess The mint and burn actions access flag
     */
    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals,
        bool _useExplicitAccess
    ) ERC20(_name, _symbol, _decimals) {
        underlying = address(0);
        useExplicitAccess = _useExplicitAccess;
    }

    /**
     * @notice Updates the Multichain Router role status for the account
     * @param _account The account address
     * @param _value The Multichain Router role status flag
     */
    function setMultichainRouter(address _account, bool _value) external onlyManager {
        _setMultichainRouter(_account, _value);
    }

    /**
     * @notice Mints tokens and assigns them to the account, increasing the total supply
     * @dev The mint function returns a boolean value, as required by the Anyswap ERC20 standard
     * @param _to The token receiver account address
     * @param _amount The number of tokens to mint
     * @return Token minting success status
     */
    function mint(address _to, uint256 _amount) external whenNotPaused returns (bool) {
        bool condition = isMultichainRouter(msg.sender) ||
            (useExplicitAccess && _isExplicitMinter());

        if (!condition) {
            revert MintAccessError();
        }

        _mint(_to, _amount);

        return true;
    }

    /**
     * @notice Burns tokens from the account, reducing the total supply
     * @dev The burn function returns a boolean value, as required by the Anyswap ERC20 standard
     * @param _from The token holder account address
     * @param _amount The number of tokens to burn
     * @return Token burning success status
     */
    function burn(address _from, uint256 _amount) external whenNotPaused returns (bool) {
        bool condition = isMultichainRouter(msg.sender) ||
            (useExplicitAccess && _isExplicitBurner());

        if (!condition) {
            revert BurnAccessError();
        }

        if (_from == address(0)) {
            revert ZeroAddressError();
        }

        uint256 allowed = allowance[_from][msg.sender];

        if (allowed < _amount) {
            revert BurnAllowanceError();
        }

        if (allowed != Constants.INFINITY) {
            // Cannot overflow because the allowed value
            // is greater or equal to the amount
            unchecked {
                allowance[_from][msg.sender] = allowed - _amount;
            }
        }

        _burn(_from, _amount);

        return true;
    }

    /**
     * @dev Override to add explicit minter access
     */
    function _isExplicitMinter() internal view virtual returns (bool) {
        return false;
    }

    /**
     * @dev Override to add explicit burner access
     */
    function _isExplicitBurner() internal view virtual returns (bool) {
        return false;
    }
}

File 17 of 26 : Pausable.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { Pausable as PausableBase } from '@openzeppelin/contracts/security/Pausable.sol';
import { ManagerRole } from './roles/ManagerRole.sol';

/**
 * @title Pausable
 * @notice Base contract that implements the emergency pause mechanism
 */
abstract contract Pausable is PausableBase, ManagerRole {
    /**
     * @notice Enter pause state
     */
    function pause() external onlyManager whenNotPaused {
        _pause();
    }

    /**
     * @notice Exit pause state
     */
    function unpause() external onlyManager whenPaused {
        _unpause();
    }
}

File 18 of 26 : AssetSpenderRole.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { RoleBearers } from './RoleBearers.sol';

/**
 * @title AssetSpenderRole
 * @notice Base contract that implements the Asset Spender role
 */
abstract contract AssetSpenderRole is RoleBearers {
    bytes32 private constant ROLE_KEY = keccak256('AssetSpender');

    /**
     * @notice Emitted when the Asset Spender role status for the account is updated
     * @param account The account address
     * @param value The Asset Spender role status flag
     */
    event SetAssetSpender(address indexed account, bool indexed value);

    /**
     * @notice Emitted when the caller is not an Asset Spender role bearer
     */
    error OnlyAssetSpenderError();

    /**
     * @dev Modifier to check if the caller is an Asset Spender role bearer
     */
    modifier onlyAssetSpender() {
        if (!isAssetSpender(msg.sender)) {
            revert OnlyAssetSpenderError();
        }

        _;
    }

    /**
     * @notice Getter of the Asset Spender role bearer count
     * @return The Asset Spender role bearer count
     */
    function assetSpenderCount() external view returns (uint256) {
        return _roleBearerCount(ROLE_KEY);
    }

    /**
     * @notice Getter of the complete list of the Asset Spender role bearers
     * @return The complete list of the Asset Spender role bearers
     */
    function fullAssetSpenderList() external view returns (address[] memory) {
        return _fullRoleBearerList(ROLE_KEY);
    }

    /**
     * @notice Getter of the Asset Spender role bearer status
     * @param _account The account address
     */
    function isAssetSpender(address _account) public view returns (bool) {
        return _isRoleBearer(ROLE_KEY, _account);
    }

    function _setAssetSpender(address _account, bool _value) internal {
        _setRoleBearer(ROLE_KEY, _account, _value);

        emit SetAssetSpender(_account, _value);
    }
}

File 19 of 26 : BurnerRole.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { RoleBearers } from './RoleBearers.sol';

/**
 * @title BurnerRole
 * @notice Base contract that implements the Burner role
 */
abstract contract BurnerRole is RoleBearers {
    bytes32 private constant ROLE_KEY = keccak256('Burner');

    /**
     * @notice Emitted when the Burner role status for the account is updated
     * @param account The account address
     * @param value The Burner role status flag
     */
    event SetBurner(address indexed account, bool indexed value);

    /**
     * @notice Getter of the Burner role bearer count
     * @return The Burner role bearer count
     */
    function burnerCount() external view returns (uint256) {
        return _roleBearerCount(ROLE_KEY);
    }

    /**
     * @notice Getter of the complete list of the Burner role bearers
     * @return The complete list of the Burner role bearers
     */
    function fullBurnerList() external view returns (address[] memory) {
        return _fullRoleBearerList(ROLE_KEY);
    }

    /**
     * @notice Getter of the Burner role bearer status
     * @param _account The account address
     */
    function isBurner(address _account) public view returns (bool) {
        return _isRoleBearer(ROLE_KEY, _account);
    }

    function _setBurner(address _account, bool _value) internal {
        _setRoleBearer(ROLE_KEY, _account, _value);

        emit SetBurner(_account, _value);
    }
}

File 20 of 26 : ManagerRole.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { Ownable } from '@openzeppelin/contracts/access/Ownable.sol';
import { RoleBearers } from './RoleBearers.sol';

/**
 * @title ManagerRole
 * @notice Base contract that implements the Manager role.
 * The manager role is a high-permission role for core team members only.
 * Managers can set vaults and routers addresses, fees, cross-chain protocols,
 * and other parameters for Interchain (cross-chain) swaps and single-network swaps.
 * Please note, the manager role is unique for every contract,
 * hence different addresses may be assigned as managers for different contracts.
 */
abstract contract ManagerRole is Ownable, RoleBearers {
    bytes32 private constant ROLE_KEY = keccak256('Manager');

    /**
     * @notice Emitted when the Manager role status for the account is updated
     * @param account The account address
     * @param value The Manager role status flag
     */
    event SetManager(address indexed account, bool indexed value);

    /**
     * @notice Emitted when the Manager role status for the account is renounced
     * @param account The account address
     */
    event RenounceManagerRole(address indexed account);

    /**
     * @notice Emitted when the caller is not a Manager role bearer
     */
    error OnlyManagerError();

    /**
     * @dev Modifier to check if the caller is a Manager role bearer
     */
    modifier onlyManager() {
        if (!isManager(msg.sender)) {
            revert OnlyManagerError();
        }

        _;
    }

    /**
     * @notice Updates the Manager role status for the account
     * @param _account The account address
     * @param _value The Manager role status flag
     */
    function setManager(address _account, bool _value) public onlyOwner {
        _setRoleBearer(ROLE_KEY, _account, _value);

        emit SetManager(_account, _value);
    }

    /**
     * @notice Renounces the Manager role
     */
    function renounceManagerRole() external onlyManager {
        _setRoleBearer(ROLE_KEY, msg.sender, false);

        emit RenounceManagerRole(msg.sender);
    }

    /**
     * @notice Getter of the Manager role bearer count
     * @return The Manager role bearer count
     */
    function managerCount() external view returns (uint256) {
        return _roleBearerCount(ROLE_KEY);
    }

    /**
     * @notice Getter of the complete list of the Manager role bearers
     * @return The complete list of the Manager role bearers
     */
    function fullManagerList() external view returns (address[] memory) {
        return _fullRoleBearerList(ROLE_KEY);
    }

    /**
     * @notice Getter of the Manager role bearer status
     * @param _account The account address
     */
    function isManager(address _account) public view returns (bool) {
        return _isRoleBearer(ROLE_KEY, _account);
    }

    function _initRoles(
        address _owner,
        address[] memory _managers,
        bool _addOwnerToManagers
    ) internal {
        address ownerAddress = _owner == address(0) ? msg.sender : _owner;

        for (uint256 index; index < _managers.length; index++) {
            setManager(_managers[index], true);
        }

        if (_addOwnerToManagers && !isManager(ownerAddress)) {
            setManager(ownerAddress, true);
        }

        if (ownerAddress != msg.sender) {
            transferOwnership(ownerAddress);
        }
    }
}

File 21 of 26 : MinterRole.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { RoleBearers } from './RoleBearers.sol';

/**
 * @title MinterRole
 * @notice Base contract that implements the Minter role
 */
abstract contract MinterRole is RoleBearers {
    bytes32 private constant ROLE_KEY = keccak256('Minter');

    /**
     * @notice Emitted when the Minter role status for the account is updated
     * @param account The account address
     * @param value The Minter role status flag
     */
    event SetMinter(address indexed account, bool indexed value);

    /**
     * @notice Getter of the Minter role bearer count
     * @return The Minter role bearer count
     */
    function minterCount() external view returns (uint256) {
        return _roleBearerCount(ROLE_KEY);
    }

    /**
     * @notice Getter of the complete list of the Minter role bearers
     * @return The complete list of the Minter role bearers
     */
    function fullMinterList() external view returns (address[] memory) {
        return _fullRoleBearerList(ROLE_KEY);
    }

    /**
     * @notice Getter of the Minter role bearer status
     * @param _account The account address
     */
    function isMinter(address _account) public view returns (bool) {
        return _isRoleBearer(ROLE_KEY, _account);
    }

    function _setMinter(address _account, bool _value) internal {
        _setRoleBearer(ROLE_KEY, _account, _value);

        emit SetMinter(_account, _value);
    }
}

File 22 of 26 : MultichainRouterRole.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { RoleBearers } from './RoleBearers.sol';

/**
 * @title MultichainRouterRole
 * @notice Base contract that implements the Multichain Router role
 */
abstract contract MultichainRouterRole is RoleBearers {
    bytes32 private constant ROLE_KEY = keccak256('MultichainRouter');

    /**
     * @notice Emitted when the Multichain Router role status for the account is updated
     * @param account The account address
     * @param value The Multichain Router role status flag
     */
    event SetMultichainRouter(address indexed account, bool indexed value);

    /**
     * @notice Getter of the Multichain Router role bearer count
     * @return The Multichain Router role bearer count
     */
    function multichainRouterCount() external view returns (uint256) {
        return _roleBearerCount(ROLE_KEY);
    }

    /**
     * @notice Getter of the complete list of the Multichain Router role bearers
     * @return The complete list of the Multichain Router role bearers
     */
    function fullMultichainRouterList() external view returns (address[] memory) {
        return _fullRoleBearerList(ROLE_KEY);
    }

    /**
     * @notice Getter of the Multichain Router role bearer status
     * @param _account The account address
     */
    function isMultichainRouter(address _account) public view returns (bool) {
        return _isRoleBearer(ROLE_KEY, _account);
    }

    function _setMultichainRouter(address _account, bool _value) internal {
        _setRoleBearer(ROLE_KEY, _account, _value);

        emit SetMultichainRouter(_account, _value);
    }
}

File 23 of 26 : RoleBearers.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import '../Constants.sol' as Constants;
import '../DataStructures.sol' as DataStructures;

/**
 * @title RoleBearers
 * @notice Base contract that implements role-based access control
 * @dev A custom implementation providing full role bearer lists
 */
abstract contract RoleBearers {
    mapping(bytes32 /*roleKey*/ => address[] /*roleBearers*/) private roleBearerTable;
    mapping(bytes32 /*roleKey*/ => mapping(address /*account*/ => DataStructures.OptionalValue /*status*/))
        private roleBearerIndexTable;

    function _setRoleBearer(bytes32 _roleKey, address _account, bool _value) internal {
        DataStructures.uniqueAddressListUpdate(
            roleBearerTable[_roleKey],
            roleBearerIndexTable[_roleKey],
            _account,
            _value,
            Constants.LIST_SIZE_LIMIT_DEFAULT
        );
    }

    function _isRoleBearer(bytes32 _roleKey, address _account) internal view returns (bool) {
        return roleBearerIndexTable[_roleKey][_account].isSet;
    }

    function _roleBearerCount(bytes32 _roleKey) internal view returns (uint256) {
        return roleBearerTable[_roleKey].length;
    }

    function _fullRoleBearerList(bytes32 _roleKey) internal view returns (address[] memory) {
        return roleBearerTable[_roleKey];
    }
}

File 24 of 26 : SystemVersionId.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

/**
 * @title SystemVersionId
 * @notice Base contract providing the system version identifier
 */
abstract contract SystemVersionId {
    /**
     * @dev The system version identifier
     */
    uint256 public constant SYSTEM_VERSION_ID = uint256(keccak256('2023-03'));
}

File 25 of 26 : VaultBase.sol
// SPDX-License-Identifier: AGPL-3.0-only

pragma solidity 0.8.19;

import { ERC20 } from 'solmate/src/tokens/ERC20.sol';
import { ReentrancyGuard } from '@openzeppelin/contracts/security/ReentrancyGuard.sol';
import { ITokenDecimals } from './interfaces/ITokenDecimals.sol';
import { CallerGuard } from './CallerGuard.sol';
import { MultichainTokenBase } from './MultichainTokenBase.sol';
import './helpers/TransferHelper.sol' as TransferHelper;
import './Constants.sol' as Constants;

/**
 * @title VaultBase
 * @notice Base contract that implements the vault logic
 */
abstract contract VaultBase is MultichainTokenBase, ReentrancyGuard, CallerGuard {
    /**
     * @dev The vault asset address
     */
    address public immutable asset;

    /**
     * @dev The total vault token supply limit
     */
    uint256 public totalSupplyLimit;

    /**
     * @notice Emitted when the total supply limit is set
     * @param limit The total supply limit value
     */
    event SetTotalSupplyLimit(uint256 limit);

    /**
     * @notice Emitted when a deposit action is performed
     * @param caller The address of the depositor account
     * @param assetAmount The amount of the deposited asset
     */
    event Deposit(address indexed caller, uint256 assetAmount);

    /**
     * @notice Emitted when a withdrawal action is performed
     * @param caller The address of the withdrawal account
     * @param assetAmount The amount of the withdrawn asset
     */
    event Withdraw(address indexed caller, uint256 assetAmount);

    /**
     * @notice Emitted when the total supply limit is exceeded
     */
    error TotalSupplyLimitError();

    /**
     * @notice Emitted when a deposit is attempted with a zero amount
     */
    error ZeroAmountError();

    /**
     * @notice Initializes the VaultBase properties of descendant contracts
     * @param _asset The vault asset address
     * @param _name The ERC20 token name
     * @param _symbol The ERC20 token symbol
     * @param _depositAllowed The initial state of deposit availability
     */
    constructor(
        address _asset,
        string memory _name,
        string memory _symbol,
        bool _depositAllowed
    ) MultichainTokenBase(_name, _symbol, ITokenDecimals(_asset).decimals(), false) {
        asset = _asset;

        _setTotalSupplyLimit(_depositAllowed ? Constants.INFINITY : 0);
    }

    /**
     * @notice Sets the total supply
     * @dev Decimals = vault token decimals = asset decimals
     * @param _limit The total supply limit value
     */
    function setTotalSupplyLimit(uint256 _limit) external onlyManager {
        _setTotalSupplyLimit(_limit);
    }

    /**
     * @notice Performs a deposit action. User deposits usdc/usdt for iusdc/iusdt used in Stablecoin Farm.
     * @param _assetAmount The amount of the deposited asset
     */
    function deposit(uint256 _assetAmount) external virtual whenNotPaused nonReentrant checkCaller {
        if (_assetAmount == 0) {
            revert ZeroAmountError();
        }

        if (totalSupply + _assetAmount > totalSupplyLimit) {
            revert TotalSupplyLimitError();
        }

        // Need to transfer before minting or ERC777s could reenter
        TransferHelper.safeTransferFrom(asset, msg.sender, address(this), _assetAmount);

        _mint(msg.sender, _assetAmount);

        emit Deposit(msg.sender, _assetAmount);
    }

    /**
     * @notice Performs a withdrawal action
     * @param _assetAmount The amount of the withdrawn asset
     */
    function withdraw(
        uint256 _assetAmount
    ) external virtual whenNotPaused nonReentrant checkCaller {
        _burn(msg.sender, _assetAmount);

        emit Withdraw(msg.sender, _assetAmount);

        TransferHelper.safeTransfer(asset, msg.sender, _assetAmount);
    }

    function _setTotalSupplyLimit(uint256 _limit) private {
        totalSupplyLimit = _limit;

        emit SetTotalSupplyLimit(_limit);
    }
}

File 26 of 26 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*//////////////////////////////////////////////////////////////
                            METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*//////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*//////////////////////////////////////////////////////////////
                            EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*//////////////////////////////////////////////////////////////
                               ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            address recoveredAddress = ecrecover(
                keccak256(
                    abi.encodePacked(
                        "\x19\x01",
                        DOMAIN_SEPARATOR(),
                        keccak256(
                            abi.encode(
                                keccak256(
                                    "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
                                ),
                                owner,
                                spender,
                                value,
                                nonces[owner]++,
                                deadline
                            )
                        )
                    )
                ),
                v,
                r,
                s
            );

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*//////////////////////////////////////////////////////////////
                        INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address[]","name":"_assetSpenders","type":"address[]"},{"internalType":"bool","name":"_depositAllowed","type":"bool"},{"internalType":"bool","name":"_variableRepaymentEnabled","type":"bool"},{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address[]","name":"_managers","type":"address[]"},{"internalType":"bool","name":"_addOwnerToManagers","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BurnAccessError","type":"error"},{"inputs":[],"name":"BurnAllowanceError","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerGuardError","type":"error"},{"inputs":[],"name":"ListSizeLimitError","type":"error"},{"inputs":[],"name":"MintAccessError","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"NonContractAddressError","type":"error"},{"inputs":[],"name":"OnlyAssetSpenderError","type":"error"},{"inputs":[],"name":"OnlyManagerError","type":"error"},{"inputs":[],"name":"ReservedTokenError","type":"error"},{"inputs":[],"name":"SafeTransferError","type":"error"},{"inputs":[],"name":"SafeTransferFromError","type":"error"},{"inputs":[],"name":"SafeTransferNativeError","type":"error"},{"inputs":[],"name":"TokenBurnError","type":"error"},{"inputs":[],"name":"TokenDecimalsError","type":"error"},{"inputs":[],"name":"TotalSupplyLimitError","type":"error"},{"inputs":[],"name":"VariableRepaymentNotEnabledError","type":"error"},{"inputs":[],"name":"VariableTokenAlreadySetError","type":"error"},{"inputs":[],"name":"VariableTokenNotSetError","type":"error"},{"inputs":[],"name":"ZeroAddressError","type":"error"},{"inputs":[],"name":"ZeroAmountError","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RedeemVariableToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"RenounceManagerRole","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"SetAssetSpender","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum CallerGuard.CallerGuardMode","name":"callerGuardMode","type":"uint8"}],"name":"SetCallerGuardMode","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"contractAddress","type":"address"},{"indexed":true,"internalType":"bool","name":"isListed","type":"bool"}],"name":"SetListedCallerGuardContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"SetManager","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"SetMultichainRouter","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"limit","type":"uint256"}],"name":"SetTotalSupplyLimit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bool","name":"variableRepaymentEnabled","type":"bool"}],"name":"SetVariableRepaymentEnabled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"variableToken","type":"address"}],"name":"SetVariableToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SYSTEM_VERSION_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"assetSpenderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"burn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"callerGuardMode","outputs":[{"internalType":"enum CallerGuard.CallerGuardMode","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"checkVariableTokenState","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"cleanup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetAmount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fullAssetSpenderList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fullListedCallerGuardContractList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fullManagerList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fullMultichainRouterList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isAssetSpender","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isListedCallerGuardContract","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"isMultichainRouter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"isReservedToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"listedCallerGuardContractCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"listedCallerGuardContractIndexMap","outputs":[{"internalType":"bool","name":"isSet","type":"bool"},{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"listedCallerGuardContractList","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"managerCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mint","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"multichainRouterCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"redeemVariableToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceManagerRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"bool","name":"_forVariableBalance","type":"bool"}],"name":"requestAsset","outputs":[{"internalType":"address","name":"assetAddress","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"setAssetSpender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum CallerGuard.CallerGuardMode","name":"_callerGuardMode","type":"uint8"}],"name":"setCallerGuardMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"account","type":"address"},{"internalType":"bool","name":"flag","type":"bool"}],"internalType":"struct AccountToFlag[]","name":"_items","type":"tuple[]"}],"name":"setListedCallerGuardContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"bool","name":"_value","type":"bool"}],"name":"setMultichainRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_limit","type":"uint256"}],"name":"setTotalSupplyLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_variableRepaymentEnabled","type":"bool"}],"name":"setVariableRepaymentEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_variableToken","type":"address"}],"name":"setVariableToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenAddress","type":"address"}],"name":"tokenBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupplyLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"variableRepaymentEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"variableToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assetAmount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]

610140604052600a805460ff191690553480156200001c57600080fd5b5060405162003aaf38038062003aaf8339810160408190526200003f91620009c7565b888888878282856001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000084573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000aa919062000adf565b6000805460ff19168155838383620000c233620001b4565b6003620000d0848262000b9a565b506004620000df838262000b9a565b5060ff81166080524660a052620000f56200020d565b60c0525050600060e0525015156101005250506001600955506001600160a01b0384166101205262000137816200012e576000620002a9565b600019620002a9565b5050505060005b86518110156200018c576200017787828151811062000161576200016162000c66565b60200260200101516001620002e460201b60201c565b80620001838162000c92565b9150506200013e565b5062000198846200034d565b620001a583838362000396565b50505050505050505062000d58565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f600360405162000241919062000cae565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b600d8190556040518181527f95e8c9f3b9477918d3e5407ba96fac8e2084722c9562942bac414734bdf8f8049060200160405180910390a150565b620003117fab6730ecea49587e6c50637868078921bc389a6c228c95e1c7259ae5a61c2174838362000450565b604051811515906001600160a01b038416907fc6b049f4dc9561b397b0cef913ea5f18165b682b193be62c0bbbf9ca8763aeba90600090a35050565b600e805460ff60a01b1916600160a01b831515908102919091179091556040517fdf888ec24e9081be857eb58887c4c9e546edf94ee7dbc643c07f69dd32c0d13590600090a250565b60006001600160a01b03841615620003af5783620003b1565b335b905060005b83518110156200040457620003ef848281518110620003d957620003d962000c66565b602002602001015160016200047960201b60201c565b80620003fb8162000c92565b915050620003b6565b508180156200041b57506200041981620004ec565b155b156200042e576200042e81600162000479565b6001600160a01b03811633146200044a576200044a816200052e565b50505050565b600083815260016020908152604080832060029092529091206200044a919084846064620005b1565b62000483620005e4565b620004b07f6d439300980e333f0256d64be2c9f67e86f4493ce25f82498d6db7f4be3d9e6f838362000450565b604051811515906001600160a01b038416907fbe9474bb3e78da7e315cdffa5cfa30b767fcc95bbf44a6197da60228eea1028690600090a35050565b6001600160a01b03811660009081527f260b29b219d450563ddb0e5ca806bdadb1e125f7e8c506de0443797dd7122728602052604081205460ff165b92915050565b62000538620005e4565b6001600160a01b038116620005a35760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b620005ae81620001b4565b50565b600082620005cc57620005c686868662000648565b620005da565b620005da868686856200077d565b9695505050505050565b6000546001600160a01b03610100909104163314620006465760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016200059a565b565b6001600160a01b0381166000908152602083905260409020805460ff1690811562000775576001808201548654909160009162000686919062000d2c565b90508082146200071a576000878281548110620006a757620006a762000c66565b9060005260206000200160009054906101000a90046001600160a01b0316905080888481548110620006dd57620006dd62000c66565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815290879052604090206001018290555b868054806200072d576200072d62000d42565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b038716825287905260408120805460ff191681556001015550505b509392505050565b6001600160a01b03821660009081526020849052604090205460ff161580156200082a5784548211620007c35760405163b1655e3360e01b815260040160405180910390fd5b6040805180820182526001808252875460208084019182526001600160a01b03881660008181528a83529586209451855460ff1916901515178555915193830193909355885491820189558884529190922090910180546001600160a01b03191690911790555b949350505050565b80516001600160a01b03811681146200084a57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156200089057620008906200084f565b604052919050565b600082601f830112620008aa57600080fd5b81516001600160401b03811115620008c657620008c66200084f565b6020620008dc601f8301601f1916820162000865565b8281528582848701011115620008f157600080fd5b60005b8381101562000911578581018301518282018401528201620008f4565b506000928101909101919091529392505050565b600082601f8301126200093757600080fd5b815160206001600160401b038211156200095557620009556200084f565b8160051b6200096682820162000865565b92835284810182019282810190878511156200098157600080fd5b83870192505b84831015620009ab576200099b8362000832565b8252918301919083019062000987565b979650505050505050565b805180151581146200084a57600080fd5b60008060008060008060008060006101208a8c031215620009e757600080fd5b620009f28a62000832565b60208b01519099506001600160401b038082111562000a1057600080fd5b62000a1e8d838e0162000898565b995060408c015191508082111562000a3557600080fd5b62000a438d838e0162000898565b985060608c015191508082111562000a5a57600080fd5b62000a688d838e0162000925565b975062000a7860808d01620009b6565b965062000a8860a08d01620009b6565b955062000a9860c08d0162000832565b945060e08c015191508082111562000aaf57600080fd5b5062000abe8c828d0162000925565b92505062000ad06101008b01620009b6565b90509295985092959850929598565b60006020828403121562000af257600080fd5b815160ff8116811462000b0457600080fd5b9392505050565b600181811c9082168062000b2057607f821691505b60208210810362000b4157634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000b9557600081815260208120601f850160051c8101602086101562000b705750805b601f850160051c820191505b8181101562000b915782815560010162000b7c565b5050505b505050565b81516001600160401b0381111562000bb65762000bb66200084f565b62000bce8162000bc7845462000b0b565b8462000b47565b602080601f83116001811462000c06576000841562000bed5750858301515b600019600386901b1c1916600185901b17855562000b91565b600085815260208120601f198616915b8281101562000c375788860151825594840194600190910190840162000c16565b508582101562000c565787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b60006001820162000ca75762000ca762000c7c565b5060010190565b600080835462000cbe8162000b0b565b6001828116801562000cd9576001811462000cef5762000d20565b60ff198416875282151583028701945062000d20565b8760005260208060002060005b8581101562000d175781548a82015290840190820162000cfc565b50505082870194505b50929695505050505050565b8181038181111562000528576200052862000c7c565b634e487b7160e01b600052603160045260246000fd5b60805160a05160c05160e0516101005161012051612cc862000de7600039600081816105cf0152818161062f01528181610cf601528181610d5601528181610f97015281816110e40152818161110d015261163d01526000818161101d015261137d015260006106dd01526000610e0a01526000610dda01526000818161054a015261122e0152612cc86000f3fe608060405234801561001057600080fd5b506004361061038e5760003560e01c80636f307dc3116101de578063b6b55f251161010f578063e3725b15116100ad578063f3ae24151161007c578063f3ae2415146108db578063f977350c146108ee578063fe14e8c314610901578063fe8fc8d41461091457600080fd5b8063e3725b151461089a578063eedc966a146108a2578063f00ecca3146108b5578063f2fde38b146108c857600080fd5b8063c2c518e1116100e9578063c2c518e114610841578063d505accf14610849578063d883d8b11461085c578063dd62ed3e1461086f57600080fd5b8063b6b55f2514610812578063bac21a2214610825578063c116a3cb1461082e57600080fd5b80638da5cb5b1161017c5780639dc29fac116101565780639dc29fac146107d1578063a5e90eee146107e4578063a8c9a27a146107f7578063a9059cbb146107ff57600080fd5b80638da5cb5b1461077557806395d89b411461078b5780639c90dfa11461079357600080fd5b80637b25b4d4116101b85780637b25b4d4146107275780637c3d6de11461073a5780637ecebe001461074d5780638456cb591461076d57600080fd5b80636f307dc3146106d857806370a08231146106ff578063715018a61461071f57600080fd5b8063313ce567116102c3578063440d7248116102615780635c56ca35116102305780635c56ca35146106985780635c975abb146106ab578063607ab5e5146106b65780636b56a691146106d057600080fd5b8063440d72481461061f5780634b15b2a91461065f5780634ba3bf7e146106725780635c05468b1461068557600080fd5b806338d52e0f1161029d57806338d52e0f146105ca5780633ef43212146105f15780633f4ba83a1461060457806340c10f191461060c57600080fd5b8063313ce56714610545578063341328c51461057e5780633644e515146105c257600080fd5b806318160ddd116103305780632a3ffb8a1161030a5780632a3ffb8a146104c15780632c966a34146104ff5780632e1a7d4d1461051f57806330eb12781461053257600080fd5b806318160ddd146104905780631b5c1d0e1461049957806323b872dd146104ae57600080fd5b8063095ea7b31161036c578063095ea7b3146103fb5780630f9374101461041e578063103b73971461042657806317daf0b41461046457600080fd5b806304e535e21461039357806306fdde03146103b1578063093f0e27146103c6575b600080fd5b61039b610928565b6040516103a891906126c8565b60405180910390f35b6103b961098a565b6040516103a89190612739565b6103ed7f8e1b4d9fa83837c77e7143aff1b7a1a921a3382984267c0eeb8d18dfd3898fca81565b6040519081526020016103a8565b61040e610409366004612783565b610a18565b60405190151581526020016103a8565b61039b610a85565b600080516020612c3383398151915260005260016020527f3c2285c553468ca8f30447b24bb463c127f1b840e23a0cafa23caa79d906669a546103ed565b61040e6104723660046127ad565b6001600160a01b03166000908152600c602052604090205460ff1690565b6103ed60055481565b6104ac6104a73660046127cf565b610aa3565b005b61040e6104bc3660046127e8565b610ad5565b600080516020612c7383398151915260005260016020527f5a35d0a0fb7e3bcb482aad5b9886840a8073f28d39a0181c254a9e207a441094546103ed565b610507610bb5565b6040516001600160a01b0390911681526020016103a8565b6104ac61052d3660046127cf565b610c1b565b6104ac610540366004612783565b610d26565b61056c7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016103a8565b6105ab61058c3660046127ad565b600c602052600090815260409020805460019091015460ff9091169082565b6040805192151583526020830191909152016103a8565b6103ed610dd6565b6105077f000000000000000000000000000000000000000000000000000000000000000081565b6104ac6105ff3660046127cf565b610e2c565b6104ac610fc8565b61040e61061a366004612783565b611000565b61040e61062d3660046127ad565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0390811691161490565b61050761066d366004612832565b61107a565b6104ac610680366004612872565b611133565b61040e6106933660046127ad565b611163565b61040e6106a63660046127ad565b61117d565b60005460ff1661040e565b600a546106c39060ff1681565b6040516103a891906128bf565b600b546103ed565b6105077f000000000000000000000000000000000000000000000000000000000000000081565b6103ed61070d3660046127ad565b60066020526000908152604090205481565b6104ac611197565b6105076107353660046127cf565b6111a9565b6104ac6107483660046127ad565b6111d3565b6103ed61075b3660046127ad565b60086020526000908152604090205481565b6104ac61131d565b60005461010090046001600160a01b0316610507565b6103b9611353565b600080516020612c1383398151915260005260016020527fb6368b31e79ffb73a14a00fbd9c0dbbe43a3a26df7f98e18d14334693e18dfce546103ed565b61040e6107df366004612783565b611360565b6104ac6107f2366004612872565b611479565b61039b6114d6565b61040e61080d366004612783565b6114ef565b6104ac6108203660046127cf565b611555565b6103ed600d5481565b6104ac61083c366004612872565b6116ad565b6104ac6116dd565b6104ac6108573660046128f6565b61174a565b6104ac61086a366004612963565b61198e565b6103ed61087d366004612980565b600760209081526000928352604080842090915290825290205481565b61039b6119bd565b6103ed6108b03660046127ad565b6119d6565b600e54610507906001600160a01b031681565b6104ac6108d63660046127ad565b611a71565b61040e6108e93660046127ad565b611ae7565b6104ac6108fc3660046129b3565b611b01565b6104ac61090f3660046129d4565b611b89565b600e5461040e90600160a01b900460ff1681565b6060600b80548060200260200160405190810160405280929190818152602001828054801561098057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610962575b5050505050905090565b6003805461099790612a49565b80601f01602080910402602001604051908101604052809291908181526020018280546109c390612a49565b8015610a105780601f106109e557610100808354040283529160200191610a10565b820191906000526020600020905b8154815290600101906020018083116109f357829003601f168201915b505050505081565b3360008181526007602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610a739086815260200190565b60405180910390a35060015b92915050565b6060610a9e600080516020612c13833981519152611c9a565b905090565b610aac33611ae7565b610ac957604051637c3ea23f60e01b815260040160405180910390fd5b610ad281611d06565b50565b6001600160a01b03831660009081526007602090815260408083203384529091528120546000198114610b3157610b0c8382612a99565b6001600160a01b03861660009081526007602090815260408083203384529091529020555b6001600160a01b03851660009081526006602052604081208054859290610b59908490612a99565b90915550506001600160a01b0380851660008181526006602052604090819020805487019055519091871690600080516020612c5383398151915290610ba29087815260200190565b60405180910390a3506001949350505050565b600e54600090600160a01b900460ff16610be257604051634dd32fa760e11b815260040160405180910390fd5b600e546001600160a01b0316610c0b57604051630d51877360e21b815260040160405180910390fd5b50600e546001600160a01b031690565b610c23611d41565b610c2b611d87565b333214610cb25760006002600a5460ff166002811115610c4d57610c4d6128a9565b1480610c8957506001600a5460ff166002811115610c6d57610c6d6128a9565b148015610c895750336000908152600c602052604090205460ff165b905080610cb057604051630fa0970d60e11b81523360048201526024015b60405180910390fd5b505b610cbc3382611de0565b60405181815233907f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649060200160405180910390a2610d1c7f00000000000000000000000000000000000000000000000000000000000000003383611e4a565b610ad26001600955565b610d2f33611ae7565b610d4c57604051637c3ea23f60e01b815260040160405180910390fd5b6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811690831603610d9857604051634477699960e11b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03831601610dcb57610dc73382611f3c565b5050565b610dc7823383611e4a565b60007f00000000000000000000000000000000000000000000000000000000000000004614610e0757610a9e611fca565b507f000000000000000000000000000000000000000000000000000000000000000090565b610e34611d41565b610e3c611d87565b333214610ebe5760006002600a5460ff166002811115610e5e57610e5e6128a9565b1480610e9a57506001600a5460ff166002811115610e7e57610e7e6128a9565b148015610e9a5750336000908152600c602052604090205460ff165b905080610ebc57604051630fa0970d60e11b8152336004820152602401610ca7565b505b610ec6610bb5565b50600e54604051632770a7eb60e21b8152336004820152602481018390526000916001600160a01b031690639dc29fac906044016020604051808303816000875af1158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190612aac565b905080610f5d5760405163a294042360e01b815260040160405180910390fd5b60405182815233907f16aab25bf023c1724fe661e47886a1083e99fb9533f0947445acb8974a6778c89060200160405180910390a2610fbd7f00000000000000000000000000000000000000000000000000000000000000003384611e4a565b50610ad26001600955565b610fd133611ae7565b610fee57604051637c3ea23f60e01b815260040160405180910390fd5b610ff6612064565b610ffe6120ad565b565b600061100a611d41565b600061101533611163565b8061104657507f00000000000000000000000000000000000000000000000000000000000000008015611046575060005b905080611066576040516371d2156960e01b815260040160405180910390fd5b61107084846120ff565b5060019392505050565b6000611084611d41565b61108d3361117d565b6110aa5760405163085c44cb60e31b815260040160405180910390fd5b8180156110c15750600e54600160a01b900460ff16155b156110df57604051634dd32fa760e11b815260040160405180910390fd5b61110a7f00000000000000000000000000000000000000000000000000000000000000008486611e4a565b507f00000000000000000000000000000000000000000000000000000000000000009392505050565b61113c33611ae7565b61115957604051637c3ea23f60e01b815260040160405180910390fd5b610dc78282612151565b6000610a7f600080516020612c73833981519152836121a6565b6000610a7f600080516020612c13833981519152836121a6565b61119f6121d1565b610ffe6000612231565b600b81815481106111b957600080fd5b6000918252602090912001546001600160a01b0316905081565b6111dc33611ae7565b6111f957604051637c3ea23f60e01b815260040160405180910390fd5b600e546001600160a01b0316156112235760405163b347c0ad60e01b815260040160405180910390fd5b61122c8161228a565b7f000000000000000000000000000000000000000000000000000000000000000060ff16816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561128e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b29190612ac9565b60ff16146112d357604051637265cffd60e11b815260040160405180910390fd5b600e80546001600160a01b0319166001600160a01b0383169081179091556040517fb366d6f570d256c768970b0082f8d0cea95a76afa28b518f9e6eb033d84e656a90600090a250565b61132633611ae7565b61134357604051637c3ea23f60e01b815260040160405180910390fd5b61134b611d41565b610ffe6122bd565b6004805461099790612a49565b600061136a611d41565b600061137533611163565b806113a657507f000000000000000000000000000000000000000000000000000000000000000080156113a6575060005b9050806113c6576040516305fb1f3f60e51b815260040160405180910390fd5b6001600160a01b0384166113ed57604051633efa09af60e01b815260040160405180910390fd5b6001600160a01b038416600090815260076020908152604080832033845290915290205483811015611432576040516308688c9b60e01b815260040160405180910390fd5b6000198114611464576001600160a01b0385166000908152600760209081526040808320338452909152902084820390555b61146e8585611de0565b506001949350505050565b6114816121d1565b61149a600080516020612c3383398151915283836122fa565b604051811515906001600160a01b038416907fbe9474bb3e78da7e315cdffa5cfa30b767fcc95bbf44a6197da60228eea1028690600090a35050565b6060610a9e600080516020612c73833981519152611c9a565b33600090815260066020526040812080548391908390611510908490612a99565b90915550506001600160a01b03831660008181526006602052604090819020805485019055513390600080516020612c5383398151915290610a739086815260200190565b61155d611d41565b611565611d87565b3332146115e75760006002600a5460ff166002811115611587576115876128a9565b14806115c357506001600a5460ff1660028111156115a7576115a76128a9565b1480156115c35750336000908152600c602052604090205460ff165b9050806115e557604051630fa0970d60e11b8152336004820152602401610ca7565b505b8060000361160857604051636e0ccc0760e01b815260040160405180910390fd5b600d54816005546116199190612ae6565b111561163857604051637872c6e360e01b815260040160405180910390fd5b6116647f0000000000000000000000000000000000000000000000000000000000000000333084612327565b61166e33826120ff565b60405181815233907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a2610ad26001600955565b6116b633611ae7565b6116d357604051637c3ea23f60e01b815260040160405180910390fd5b610dc78282612422565b6116e633611ae7565b61170357604051637c3ea23f60e01b815260040160405180910390fd5b61171d600080516020612c338339815191523360006122fa565b60405133907f6cc2c67081f55c2fffb7c008fa995fbbf890f48c7c16fba93d8220f00dc84cc590600090a2565b4284101561179a5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610ca7565b600060016117a6610dd6565b6001600160a01b038a811660008181526008602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156118b2573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906118e85750876001600160a01b0316816001600160a01b0316145b6119255760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610ca7565b6001600160a01b0390811660009081526007602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b61199733611ae7565b6119b457604051637c3ea23f60e01b815260040160405180910390fd5b610ad281612477565b6060610a9e600080516020612c33833981519152611c9a565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03831601611a04575047919050565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015611a48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a7f9190612af9565b919050565b611a796121d1565b6001600160a01b038116611ade5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610ca7565b610ad281612231565b6000610a7f600080516020612c33833981519152836121a6565b611b0a33611ae7565b611b2757604051637c3ea23f60e01b815260040160405180910390fd5b600a805482919060ff19166001836002811115611b4657611b466128a9565b0217905550806002811115611b5d57611b5d6128a9565b6040517f332a9f1d3bd9b0f7abbd95838fed6b417589632d0eb33f2d8ae6e2aa17178efd90600090a250565b611b9233611ae7565b611baf57604051637c3ea23f60e01b815260040160405180910390fd5b60005b81811015611c955736838383818110611bcd57611bcd612b12565b9050604002019050806020016020810190611be89190612963565b15611c0257611c02611bfd60208301836127ad565b61228a565b611c2d600b600c611c1660208501856127ad565b611c266040860160208701612963565b60646124c0565b50611c3e6040820160208301612963565b1515611c4d60208301836127ad565b6001600160a01b03167f1470aed653fa8a8ce4c7b2f41287634199f7ec3c4f5fd0ace97d82cf006beec360405160405180910390a35080611c8d81612b28565b915050611bb2565b505050565b600081815260016020908152604091829020805483518184028101840190945280845260609392830182828015611cfa57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611cdc575b50505050509050919050565b600d8190556040518181527f95e8c9f3b9477918d3e5407ba96fac8e2084722c9562942bac414734bdf8f8049060200160405180910390a150565b60005460ff1615610ffe5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610ca7565b600260095403611dd95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610ca7565b6002600955565b6001600160a01b03821660009081526006602052604081208054839290611e08908490612a99565b90915550506005805482900390556040518181526000906001600160a01b03841690600080516020612c53833981519152906020015b60405180910390a35050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691611ea69190612b41565b6000604051808303816000865af19150503d8060008114611ee3576040519150601f19603f3d011682016040523d82523d6000602084013e611ee8565b606091505b50915091506000828015611f14575081511580611f14575081806020019051810190611f149190612aac565b905080611f3457604051632fdb1b7f60e11b815260040160405180910390fd5b505050505050565b604080516000808252602082019092526001600160a01b038416908390604051611f669190612b41565b60006040518083038185875af1925050503d8060008114611fa3576040519150601f19603f3d011682016040523d82523d6000602084013e611fa8565b606091505b5050905080611c9557604051632e05b05360e21b815260040160405180910390fd5b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6003604051611ffc9190612b5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60005460ff16610ffe5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610ca7565b6120b5612064565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b80600560008282546121119190612ae6565b90915550506001600160a01b038216600081815260066020908152604080832080548601905551848152600080516020612c538339815191529101611e3e565b61216a600080516020612c7383398151915283836122fa565b604051811515906001600160a01b038416907f2b535dea3b8ec7fb244a57e39a42aee5f6f4871306457173aa18f49a96e8c78090600090a35050565b60009182526002602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6000546001600160a01b03610100909104163314610ffe5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610ca7565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b6001600160a01b0381163b610ad257604051638c50d7cd60e01b81526001600160a01b0382166004820152602401610ca7565b6122c5611d41565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586120e23390565b600083815260016020908152604080832060029092529091206123219190848460646124c0565b50505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161238b9190612b41565b6000604051808303816000865af19150503d80600081146123c8576040519150601f19603f3d011682016040523d82523d6000602084013e6123cd565b606091505b509150915060008280156123f95750815115806123f95750818060200190518101906123f99190612aac565b90508061241957604051632d9d5b4160e01b815260040160405180910390fd5b50505050505050565b61243b600080516020612c1383398151915283836122fa565b604051811515906001600160a01b038416907fc6b049f4dc9561b397b0cef913ea5f18165b682b193be62c0bbbf9ca8763aeba90600090a35050565b600e805460ff60a01b1916600160a01b831515908102919091179091556040517fdf888ec24e9081be857eb58887c4c9e546edf94ee7dbc643c07f69dd32c0d13590600090a250565b6000826124d7576124d28686866124ed565b6124e3565b6124e386868685612615565b9695505050505050565b6001600160a01b0381166000908152602083905260409020805460ff1690811561260d57600180820154865490916000916125289190612a99565b90508082146125b557600087828154811061254557612545612b12565b9060005260206000200160009054906101000a90046001600160a01b031690508088848154811061257857612578612b12565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815290879052604090206001018290555b868054806125c5576125c5612bfc565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b038716825287905260408120805460ff191681556001015550505b509392505050565b6001600160a01b03821660009081526020849052604090205460ff161580156126c057845482116126595760405163b1655e3360e01b815260040160405180910390fd5b6040805180820182526001808252875460208084019182526001600160a01b03881660008181528a83529586209451855460ff1916901515178555915193830193909355885491820189558884529190922090910180546001600160a01b03191690911790555b949350505050565b6020808252825182820181905260009190848201906040850190845b818110156127095783516001600160a01b0316835292840192918401916001016126e4565b50909695505050505050565b60005b83811015612730578181015183820152602001612718565b50506000910152565b6020815260008251806020840152612758816040850160208701612715565b601f01601f19169190910160400192915050565b80356001600160a01b0381168114611a6c57600080fd5b6000806040838503121561279657600080fd5b61279f8361276c565b946020939093013593505050565b6000602082840312156127bf57600080fd5b6127c88261276c565b9392505050565b6000602082840312156127e157600080fd5b5035919050565b6000806000606084860312156127fd57600080fd5b6128068461276c565b92506128146020850161276c565b9150604084013590509250925092565b8015158114610ad257600080fd5b60008060006060848603121561284757600080fd5b833592506128576020850161276c565b9150604084013561286781612824565b809150509250925092565b6000806040838503121561288557600080fd5b61288e8361276c565b9150602083013561289e81612824565b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b60208101600383106128e157634e487b7160e01b600052602160045260246000fd5b91905290565b60ff81168114610ad257600080fd5b600080600080600080600060e0888a03121561291157600080fd5b61291a8861276c565b96506129286020890161276c565b955060408801359450606088013593506080880135612946816128e7565b9699959850939692959460a0840135945060c09093013592915050565b60006020828403121561297557600080fd5b81356127c881612824565b6000806040838503121561299357600080fd5b61299c8361276c565b91506129aa6020840161276c565b90509250929050565b6000602082840312156129c557600080fd5b8135600381106127c857600080fd5b600080602083850312156129e757600080fd5b823567ffffffffffffffff808211156129ff57600080fd5b818501915085601f830112612a1357600080fd5b813581811115612a2257600080fd5b8660208260061b8501011115612a3757600080fd5b60209290920196919550909350505050565b600181811c90821680612a5d57607f821691505b602082108103612a7d57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610a7f57610a7f612a83565b600060208284031215612abe57600080fd5b81516127c881612824565b600060208284031215612adb57600080fd5b81516127c8816128e7565b80820180821115610a7f57610a7f612a83565b600060208284031215612b0b57600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b600060018201612b3a57612b3a612a83565b5060010190565b60008251612b53818460208701612715565b9190910192915050565b600080835481600182811c915080831680612b7957607f831692505b60208084108203612b9857634e487b7160e01b86526022600452602486fd5b818015612bac5760018114612bc157612bee565b60ff1986168952841515850289019650612bee565b60008a81526020902060005b86811015612be65781548b820152908501908301612bcd565b505084890196505b509498975050505050505050565b634e487b7160e01b600052603160045260246000fdfeab6730ecea49587e6c50637868078921bc389a6c228c95e1c7259ae5a61c21746d439300980e333f0256d64be2c9f67e86f4493ce25f82498d6db7f4be3d9e6fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef98e2d91934cad395982d17afdb76da8ef5d5f4e6341e368f19914b44485e5886a26469706673582212205ec9858eacdf768515e704ecb64a37bb78e37e06b86095a8483e8e5c38e60bbd64736f6c63430008130033000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000072e28c7f34100afefc399fcc0ae041b8fe5841ae00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000e496e746572706f727420555344540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005695553445400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000007b2e3fc7510d1a51b3bef735f9854465892193540000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061038e5760003560e01c80636f307dc3116101de578063b6b55f251161010f578063e3725b15116100ad578063f3ae24151161007c578063f3ae2415146108db578063f977350c146108ee578063fe14e8c314610901578063fe8fc8d41461091457600080fd5b8063e3725b151461089a578063eedc966a146108a2578063f00ecca3146108b5578063f2fde38b146108c857600080fd5b8063c2c518e1116100e9578063c2c518e114610841578063d505accf14610849578063d883d8b11461085c578063dd62ed3e1461086f57600080fd5b8063b6b55f2514610812578063bac21a2214610825578063c116a3cb1461082e57600080fd5b80638da5cb5b1161017c5780639dc29fac116101565780639dc29fac146107d1578063a5e90eee146107e4578063a8c9a27a146107f7578063a9059cbb146107ff57600080fd5b80638da5cb5b1461077557806395d89b411461078b5780639c90dfa11461079357600080fd5b80637b25b4d4116101b85780637b25b4d4146107275780637c3d6de11461073a5780637ecebe001461074d5780638456cb591461076d57600080fd5b80636f307dc3146106d857806370a08231146106ff578063715018a61461071f57600080fd5b8063313ce567116102c3578063440d7248116102615780635c56ca35116102305780635c56ca35146106985780635c975abb146106ab578063607ab5e5146106b65780636b56a691146106d057600080fd5b8063440d72481461061f5780634b15b2a91461065f5780634ba3bf7e146106725780635c05468b1461068557600080fd5b806338d52e0f1161029d57806338d52e0f146105ca5780633ef43212146105f15780633f4ba83a1461060457806340c10f191461060c57600080fd5b8063313ce56714610545578063341328c51461057e5780633644e515146105c257600080fd5b806318160ddd116103305780632a3ffb8a1161030a5780632a3ffb8a146104c15780632c966a34146104ff5780632e1a7d4d1461051f57806330eb12781461053257600080fd5b806318160ddd146104905780631b5c1d0e1461049957806323b872dd146104ae57600080fd5b8063095ea7b31161036c578063095ea7b3146103fb5780630f9374101461041e578063103b73971461042657806317daf0b41461046457600080fd5b806304e535e21461039357806306fdde03146103b1578063093f0e27146103c6575b600080fd5b61039b610928565b6040516103a891906126c8565b60405180910390f35b6103b961098a565b6040516103a89190612739565b6103ed7f8e1b4d9fa83837c77e7143aff1b7a1a921a3382984267c0eeb8d18dfd3898fca81565b6040519081526020016103a8565b61040e610409366004612783565b610a18565b60405190151581526020016103a8565b61039b610a85565b600080516020612c3383398151915260005260016020527f3c2285c553468ca8f30447b24bb463c127f1b840e23a0cafa23caa79d906669a546103ed565b61040e6104723660046127ad565b6001600160a01b03166000908152600c602052604090205460ff1690565b6103ed60055481565b6104ac6104a73660046127cf565b610aa3565b005b61040e6104bc3660046127e8565b610ad5565b600080516020612c7383398151915260005260016020527f5a35d0a0fb7e3bcb482aad5b9886840a8073f28d39a0181c254a9e207a441094546103ed565b610507610bb5565b6040516001600160a01b0390911681526020016103a8565b6104ac61052d3660046127cf565b610c1b565b6104ac610540366004612783565b610d26565b61056c7f000000000000000000000000000000000000000000000000000000000000000681565b60405160ff90911681526020016103a8565b6105ab61058c3660046127ad565b600c602052600090815260409020805460019091015460ff9091169082565b6040805192151583526020830191909152016103a8565b6103ed610dd6565b6105077f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec781565b6104ac6105ff3660046127cf565b610e2c565b6104ac610fc8565b61040e61061a366004612783565b611000565b61040e61062d3660046127ad565b7f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec76001600160a01b0390811691161490565b61050761066d366004612832565b61107a565b6104ac610680366004612872565b611133565b61040e6106933660046127ad565b611163565b61040e6106a63660046127ad565b61117d565b60005460ff1661040e565b600a546106c39060ff1681565b6040516103a891906128bf565b600b546103ed565b6105077f000000000000000000000000000000000000000000000000000000000000000081565b6103ed61070d3660046127ad565b60066020526000908152604090205481565b6104ac611197565b6105076107353660046127cf565b6111a9565b6104ac6107483660046127ad565b6111d3565b6103ed61075b3660046127ad565b60086020526000908152604090205481565b6104ac61131d565b60005461010090046001600160a01b0316610507565b6103b9611353565b600080516020612c1383398151915260005260016020527fb6368b31e79ffb73a14a00fbd9c0dbbe43a3a26df7f98e18d14334693e18dfce546103ed565b61040e6107df366004612783565b611360565b6104ac6107f2366004612872565b611479565b61039b6114d6565b61040e61080d366004612783565b6114ef565b6104ac6108203660046127cf565b611555565b6103ed600d5481565b6104ac61083c366004612872565b6116ad565b6104ac6116dd565b6104ac6108573660046128f6565b61174a565b6104ac61086a366004612963565b61198e565b6103ed61087d366004612980565b600760209081526000928352604080842090915290825290205481565b61039b6119bd565b6103ed6108b03660046127ad565b6119d6565b600e54610507906001600160a01b031681565b6104ac6108d63660046127ad565b611a71565b61040e6108e93660046127ad565b611ae7565b6104ac6108fc3660046129b3565b611b01565b6104ac61090f3660046129d4565b611b89565b600e5461040e90600160a01b900460ff1681565b6060600b80548060200260200160405190810160405280929190818152602001828054801561098057602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610962575b5050505050905090565b6003805461099790612a49565b80601f01602080910402602001604051908101604052809291908181526020018280546109c390612a49565b8015610a105780601f106109e557610100808354040283529160200191610a10565b820191906000526020600020905b8154815290600101906020018083116109f357829003601f168201915b505050505081565b3360008181526007602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92590610a739086815260200190565b60405180910390a35060015b92915050565b6060610a9e600080516020612c13833981519152611c9a565b905090565b610aac33611ae7565b610ac957604051637c3ea23f60e01b815260040160405180910390fd5b610ad281611d06565b50565b6001600160a01b03831660009081526007602090815260408083203384529091528120546000198114610b3157610b0c8382612a99565b6001600160a01b03861660009081526007602090815260408083203384529091529020555b6001600160a01b03851660009081526006602052604081208054859290610b59908490612a99565b90915550506001600160a01b0380851660008181526006602052604090819020805487019055519091871690600080516020612c5383398151915290610ba29087815260200190565b60405180910390a3506001949350505050565b600e54600090600160a01b900460ff16610be257604051634dd32fa760e11b815260040160405180910390fd5b600e546001600160a01b0316610c0b57604051630d51877360e21b815260040160405180910390fd5b50600e546001600160a01b031690565b610c23611d41565b610c2b611d87565b333214610cb25760006002600a5460ff166002811115610c4d57610c4d6128a9565b1480610c8957506001600a5460ff166002811115610c6d57610c6d6128a9565b148015610c895750336000908152600c602052604090205460ff165b905080610cb057604051630fa0970d60e11b81523360048201526024015b60405180910390fd5b505b610cbc3382611de0565b60405181815233907f884edad9ce6fa2440d8a54cc123490eb96d2768479d49ff9c7366125a94243649060200160405180910390a2610d1c7f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec73383611e4a565b610ad26001600955565b610d2f33611ae7565b610d4c57604051637c3ea23f60e01b815260040160405180910390fd5b6001600160a01b037f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7811690831603610d9857604051634477699960e11b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03831601610dcb57610dc73382611f3c565b5050565b610dc7823383611e4a565b60007f00000000000000000000000000000000000000000000000000000000000000014614610e0757610a9e611fca565b507f057675bbbe246f201248268c22244ac160be47b9cd9ea1862ade4ac4b302655490565b610e34611d41565b610e3c611d87565b333214610ebe5760006002600a5460ff166002811115610e5e57610e5e6128a9565b1480610e9a57506001600a5460ff166002811115610e7e57610e7e6128a9565b148015610e9a5750336000908152600c602052604090205460ff165b905080610ebc57604051630fa0970d60e11b8152336004820152602401610ca7565b505b610ec6610bb5565b50600e54604051632770a7eb60e21b8152336004820152602481018390526000916001600160a01b031690639dc29fac906044016020604051808303816000875af1158015610f19573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f3d9190612aac565b905080610f5d5760405163a294042360e01b815260040160405180910390fd5b60405182815233907f16aab25bf023c1724fe661e47886a1083e99fb9533f0947445acb8974a6778c89060200160405180910390a2610fbd7f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec73384611e4a565b50610ad26001600955565b610fd133611ae7565b610fee57604051637c3ea23f60e01b815260040160405180910390fd5b610ff6612064565b610ffe6120ad565b565b600061100a611d41565b600061101533611163565b8061104657507f00000000000000000000000000000000000000000000000000000000000000008015611046575060005b905080611066576040516371d2156960e01b815260040160405180910390fd5b61107084846120ff565b5060019392505050565b6000611084611d41565b61108d3361117d565b6110aa5760405163085c44cb60e31b815260040160405180910390fd5b8180156110c15750600e54600160a01b900460ff16155b156110df57604051634dd32fa760e11b815260040160405180910390fd5b61110a7f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec78486611e4a565b507f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec79392505050565b61113c33611ae7565b61115957604051637c3ea23f60e01b815260040160405180910390fd5b610dc78282612151565b6000610a7f600080516020612c73833981519152836121a6565b6000610a7f600080516020612c13833981519152836121a6565b61119f6121d1565b610ffe6000612231565b600b81815481106111b957600080fd5b6000918252602090912001546001600160a01b0316905081565b6111dc33611ae7565b6111f957604051637c3ea23f60e01b815260040160405180910390fd5b600e546001600160a01b0316156112235760405163b347c0ad60e01b815260040160405180910390fd5b61122c8161228a565b7f000000000000000000000000000000000000000000000000000000000000000660ff16816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561128e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112b29190612ac9565b60ff16146112d357604051637265cffd60e11b815260040160405180910390fd5b600e80546001600160a01b0319166001600160a01b0383169081179091556040517fb366d6f570d256c768970b0082f8d0cea95a76afa28b518f9e6eb033d84e656a90600090a250565b61132633611ae7565b61134357604051637c3ea23f60e01b815260040160405180910390fd5b61134b611d41565b610ffe6122bd565b6004805461099790612a49565b600061136a611d41565b600061137533611163565b806113a657507f000000000000000000000000000000000000000000000000000000000000000080156113a6575060005b9050806113c6576040516305fb1f3f60e51b815260040160405180910390fd5b6001600160a01b0384166113ed57604051633efa09af60e01b815260040160405180910390fd5b6001600160a01b038416600090815260076020908152604080832033845290915290205483811015611432576040516308688c9b60e01b815260040160405180910390fd5b6000198114611464576001600160a01b0385166000908152600760209081526040808320338452909152902084820390555b61146e8585611de0565b506001949350505050565b6114816121d1565b61149a600080516020612c3383398151915283836122fa565b604051811515906001600160a01b038416907fbe9474bb3e78da7e315cdffa5cfa30b767fcc95bbf44a6197da60228eea1028690600090a35050565b6060610a9e600080516020612c73833981519152611c9a565b33600090815260066020526040812080548391908390611510908490612a99565b90915550506001600160a01b03831660008181526006602052604090819020805485019055513390600080516020612c5383398151915290610a739086815260200190565b61155d611d41565b611565611d87565b3332146115e75760006002600a5460ff166002811115611587576115876128a9565b14806115c357506001600a5460ff1660028111156115a7576115a76128a9565b1480156115c35750336000908152600c602052604090205460ff165b9050806115e557604051630fa0970d60e11b8152336004820152602401610ca7565b505b8060000361160857604051636e0ccc0760e01b815260040160405180910390fd5b600d54816005546116199190612ae6565b111561163857604051637872c6e360e01b815260040160405180910390fd5b6116647f000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7333084612327565b61166e33826120ff565b60405181815233907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a2610ad26001600955565b6116b633611ae7565b6116d357604051637c3ea23f60e01b815260040160405180910390fd5b610dc78282612422565b6116e633611ae7565b61170357604051637c3ea23f60e01b815260040160405180910390fd5b61171d600080516020612c338339815191523360006122fa565b60405133907f6cc2c67081f55c2fffb7c008fa995fbbf890f48c7c16fba93d8220f00dc84cc590600090a2565b4284101561179a5760405162461bcd60e51b815260206004820152601760248201527f5045524d49545f444541444c494e455f455850495245440000000000000000006044820152606401610ca7565b600060016117a6610dd6565b6001600160a01b038a811660008181526008602090815260409182902080546001810190915582517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98184015280840194909452938d166060840152608083018c905260a083019390935260c08083018b90528151808403909101815260e08301909152805192019190912061190160f01b6101008301526101028201929092526101228101919091526101420160408051601f198184030181528282528051602091820120600084529083018083525260ff871690820152606081018590526080810184905260a0016020604051602081039080840390855afa1580156118b2573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116158015906118e85750876001600160a01b0316816001600160a01b0316145b6119255760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa9a4a3a722a960911b6044820152606401610ca7565b6001600160a01b0390811660009081526007602090815260408083208a8516808552908352928190208990555188815291928a16917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a350505050505050565b61199733611ae7565b6119b457604051637c3ea23f60e01b815260040160405180910390fd5b610ad281612477565b6060610a9e600080516020612c33833981519152611c9a565b600073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b03831601611a04575047919050565b6040516370a0823160e01b81523060048201526001600160a01b038316906370a0823190602401602060405180830381865afa158015611a48573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a7f9190612af9565b919050565b611a796121d1565b6001600160a01b038116611ade5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610ca7565b610ad281612231565b6000610a7f600080516020612c33833981519152836121a6565b611b0a33611ae7565b611b2757604051637c3ea23f60e01b815260040160405180910390fd5b600a805482919060ff19166001836002811115611b4657611b466128a9565b0217905550806002811115611b5d57611b5d6128a9565b6040517f332a9f1d3bd9b0f7abbd95838fed6b417589632d0eb33f2d8ae6e2aa17178efd90600090a250565b611b9233611ae7565b611baf57604051637c3ea23f60e01b815260040160405180910390fd5b60005b81811015611c955736838383818110611bcd57611bcd612b12565b9050604002019050806020016020810190611be89190612963565b15611c0257611c02611bfd60208301836127ad565b61228a565b611c2d600b600c611c1660208501856127ad565b611c266040860160208701612963565b60646124c0565b50611c3e6040820160208301612963565b1515611c4d60208301836127ad565b6001600160a01b03167f1470aed653fa8a8ce4c7b2f41287634199f7ec3c4f5fd0ace97d82cf006beec360405160405180910390a35080611c8d81612b28565b915050611bb2565b505050565b600081815260016020908152604091829020805483518184028101840190945280845260609392830182828015611cfa57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611cdc575b50505050509050919050565b600d8190556040518181527f95e8c9f3b9477918d3e5407ba96fac8e2084722c9562942bac414734bdf8f8049060200160405180910390a150565b60005460ff1615610ffe5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610ca7565b600260095403611dd95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610ca7565b6002600955565b6001600160a01b03821660009081526006602052604081208054839290611e08908490612a99565b90915550506005805482900390556040518181526000906001600160a01b03841690600080516020612c53833981519152906020015b60405180910390a35050565b604080516001600160a01b038481166024830152604480830185905283518084039091018152606490920183526020820180516001600160e01b031663a9059cbb60e01b1790529151600092839290871691611ea69190612b41565b6000604051808303816000865af19150503d8060008114611ee3576040519150601f19603f3d011682016040523d82523d6000602084013e611ee8565b606091505b50915091506000828015611f14575081511580611f14575081806020019051810190611f149190612aac565b905080611f3457604051632fdb1b7f60e11b815260040160405180910390fd5b505050505050565b604080516000808252602082019092526001600160a01b038416908390604051611f669190612b41565b60006040518083038185875af1925050503d8060008114611fa3576040519150601f19603f3d011682016040523d82523d6000602084013e611fa8565b606091505b5050905080611c9557604051632e05b05360e21b815260040160405180910390fd5b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6003604051611ffc9190612b5d565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b60005460ff16610ffe5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610ca7565b6120b5612064565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b80600560008282546121119190612ae6565b90915550506001600160a01b038216600081815260066020908152604080832080548601905551848152600080516020612c538339815191529101611e3e565b61216a600080516020612c7383398151915283836122fa565b604051811515906001600160a01b038416907f2b535dea3b8ec7fb244a57e39a42aee5f6f4871306457173aa18f49a96e8c78090600090a35050565b60009182526002602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6000546001600160a01b03610100909104163314610ffe5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610ca7565b600080546001600160a01b03838116610100818102610100600160a81b0319851617855560405193049190911692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a35050565b6001600160a01b0381163b610ad257604051638c50d7cd60e01b81526001600160a01b0382166004820152602401610ca7565b6122c5611d41565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586120e23390565b600083815260016020908152604080832060029092529091206123219190848460646124c0565b50505050565b604080516001600160a01b0385811660248301528481166044830152606480830185905283518084039091018152608490920183526020820180516001600160e01b03166323b872dd60e01b179052915160009283929088169161238b9190612b41565b6000604051808303816000865af19150503d80600081146123c8576040519150601f19603f3d011682016040523d82523d6000602084013e6123cd565b606091505b509150915060008280156123f95750815115806123f95750818060200190518101906123f99190612aac565b90508061241957604051632d9d5b4160e01b815260040160405180910390fd5b50505050505050565b61243b600080516020612c1383398151915283836122fa565b604051811515906001600160a01b038416907fc6b049f4dc9561b397b0cef913ea5f18165b682b193be62c0bbbf9ca8763aeba90600090a35050565b600e805460ff60a01b1916600160a01b831515908102919091179091556040517fdf888ec24e9081be857eb58887c4c9e546edf94ee7dbc643c07f69dd32c0d13590600090a250565b6000826124d7576124d28686866124ed565b6124e3565b6124e386868685612615565b9695505050505050565b6001600160a01b0381166000908152602083905260409020805460ff1690811561260d57600180820154865490916000916125289190612a99565b90508082146125b557600087828154811061254557612545612b12565b9060005260206000200160009054906101000a90046001600160a01b031690508088848154811061257857612578612b12565b600091825260208083209190910180546001600160a01b0319166001600160a01b0394851617905592909116815290879052604090206001018290555b868054806125c5576125c5612bfc565b60008281526020808220830160001990810180546001600160a01b03191690559092019092556001600160a01b038716825287905260408120805460ff191681556001015550505b509392505050565b6001600160a01b03821660009081526020849052604090205460ff161580156126c057845482116126595760405163b1655e3360e01b815260040160405180910390fd5b6040805180820182526001808252875460208084019182526001600160a01b03881660008181528a83529586209451855460ff1916901515178555915193830193909355885491820189558884529190922090910180546001600160a01b03191690911790555b949350505050565b6020808252825182820181905260009190848201906040850190845b818110156127095783516001600160a01b0316835292840192918401916001016126e4565b50909695505050505050565b60005b83811015612730578181015183820152602001612718565b50506000910152565b6020815260008251806020840152612758816040850160208701612715565b601f01601f19169190910160400192915050565b80356001600160a01b0381168114611a6c57600080fd5b6000806040838503121561279657600080fd5b61279f8361276c565b946020939093013593505050565b6000602082840312156127bf57600080fd5b6127c88261276c565b9392505050565b6000602082840312156127e157600080fd5b5035919050565b6000806000606084860312156127fd57600080fd5b6128068461276c565b92506128146020850161276c565b9150604084013590509250925092565b8015158114610ad257600080fd5b60008060006060848603121561284757600080fd5b833592506128576020850161276c565b9150604084013561286781612824565b809150509250925092565b6000806040838503121561288557600080fd5b61288e8361276c565b9150602083013561289e81612824565b809150509250929050565b634e487b7160e01b600052602160045260246000fd5b60208101600383106128e157634e487b7160e01b600052602160045260246000fd5b91905290565b60ff81168114610ad257600080fd5b600080600080600080600060e0888a03121561291157600080fd5b61291a8861276c565b96506129286020890161276c565b955060408801359450606088013593506080880135612946816128e7565b9699959850939692959460a0840135945060c09093013592915050565b60006020828403121561297557600080fd5b81356127c881612824565b6000806040838503121561299357600080fd5b61299c8361276c565b91506129aa6020840161276c565b90509250929050565b6000602082840312156129c557600080fd5b8135600381106127c857600080fd5b600080602083850312156129e757600080fd5b823567ffffffffffffffff808211156129ff57600080fd5b818501915085601f830112612a1357600080fd5b813581811115612a2257600080fd5b8660208260061b8501011115612a3757600080fd5b60209290920196919550909350505050565b600181811c90821680612a5d57607f821691505b602082108103612a7d57634e487b7160e01b600052602260045260246000fd5b50919050565b634e487b7160e01b600052601160045260246000fd5b81810381811115610a7f57610a7f612a83565b600060208284031215612abe57600080fd5b81516127c881612824565b600060208284031215612adb57600080fd5b81516127c8816128e7565b80820180821115610a7f57610a7f612a83565b600060208284031215612b0b57600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b600060018201612b3a57612b3a612a83565b5060010190565b60008251612b53818460208701612715565b9190910192915050565b600080835481600182811c915080831680612b7957607f831692505b60208084108203612b9857634e487b7160e01b86526022600452602486fd5b818015612bac5760018114612bc157612bee565b60ff1986168952841515850289019650612bee565b60008a81526020902060005b86811015612be65781548b820152908501908301612bcd565b505084890196505b509498975050505050505050565b634e487b7160e01b600052603160045260246000fdfeab6730ecea49587e6c50637868078921bc389a6c228c95e1c7259ae5a61c21746d439300980e333f0256d64be2c9f67e86f4493ce25f82498d6db7f4be3d9e6fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef98e2d91934cad395982d17afdb76da8ef5d5f4e6341e368f19914b44485e5886a26469706673582212205ec9858eacdf768515e704ecb64a37bb78e37e06b86095a8483e8e5c38e60bbd64736f6c63430008130033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec70000000000000000000000000000000000000000000000000000000000000120000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001a00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000072e28c7f34100afefc399fcc0ae041b8fe5841ae00000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000e496e746572706f727420555344540000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005695553445400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000000007b2e3fc7510d1a51b3bef735f9854465892193540000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _asset (address): 0xdAC17F958D2ee523a2206206994597C13D831ec7
Arg [1] : _name (string): Interport USDT
Arg [2] : _symbol (string): iUSDT
Arg [3] : _assetSpenders (address[]): 0x7b2E3FC7510D1A51b3bef735F985446589219354
Arg [4] : _depositAllowed (bool): True
Arg [5] : _variableRepaymentEnabled (bool): True
Arg [6] : _owner (address): 0x72E28c7F34100AfefC399fcc0AE041B8fe5841AE

-----Encoded View---------------
16 Constructor Arguments found :
Arg [0] : 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [3] : 00000000000000000000000000000000000000000000000000000000000001a0
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [6] : 00000000000000000000000072e28c7f34100afefc399fcc0ae041b8fe5841ae
Arg [7] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [9] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [10] : 496e746572706f72742055534454000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [12] : 6955534454000000000000000000000000000000000000000000000000000000
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [14] : 0000000000000000000000007b2e3fc7510d1a51b3bef735f985446589219354
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000000


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
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.