Source Code
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 16915101 | 332 days ago | Contract Creation | 0 FRAX |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LeafHLMessageModule
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.19 <0.9.0;
import {TypeCasts} from "@hyperlane/core/contracts/libs/TypeCasts.sol";
import {IInterchainSecurityModule} from "@hyperlane/core/contracts/interfaces/IInterchainSecurityModule.sol";
import {SafeERC20} from "@openzeppelin5/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin5/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin5/contracts/access/Ownable.sol";
import {ILeafHLMessageModule, IHLHandler} from "../../interfaces/bridge/hyperlane/ILeafHLMessageModule.sol";
import {ILeafMessageBridge} from "../../interfaces/bridge/ILeafMessageBridge.sol";
import {IPoolFactory} from "../../interfaces/pools/IPoolFactory.sol";
import {ILeafGauge} from "../../interfaces/gauges/ILeafGauge.sol";
import {ILeafVoter} from "../../interfaces/voter/ILeafVoter.sol";
import {IReward} from "../../interfaces/rewards/IReward.sol";
import {Commands} from "../../libraries/Commands.sol";
import {IXERC20} from "../../interfaces/xerc20/IXERC20.sol";
import {ISpecifiesInterchainSecurityModule} from "../../interfaces/external/ISpecifiesInterchainSecurityModule.sol";
/// @title Leaf Hyperlane Message Module
/// @notice Hyperlane module used to bridge arbitrary messages between chains
contract LeafHLMessageModule is ILeafHLMessageModule, ISpecifiesInterchainSecurityModule, Ownable {
using SafeERC20 for IERC20;
using Commands for bytes;
/// @inheritdoc ILeafHLMessageModule
address public immutable bridge;
/// @inheritdoc ILeafHLMessageModule
address public immutable xerc20;
/// @inheritdoc ILeafHLMessageModule
address public immutable voter;
/// @inheritdoc ILeafHLMessageModule
address public immutable mailbox;
/// @inheritdoc ILeafHLMessageModule
IInterchainSecurityModule public securityModule;
constructor(address _owner, address _bridge, address _mailbox, address _ism) Ownable(_owner) {
bridge = _bridge;
xerc20 = ILeafMessageBridge(_bridge).xerc20();
voter = ILeafMessageBridge(_bridge).voter();
mailbox = _mailbox;
securityModule = IInterchainSecurityModule(_ism);
emit InterchainSecurityModuleSet({_new: _ism});
}
/// @inheritdoc ISpecifiesInterchainSecurityModule
function interchainSecurityModule() external view returns (IInterchainSecurityModule) {
return securityModule;
}
/// @inheritdoc ISpecifiesInterchainSecurityModule
function setInterchainSecurityModule(address _ism) external onlyOwner {
securityModule = IInterchainSecurityModule(_ism);
emit InterchainSecurityModuleSet({_new: _ism});
}
/// @inheritdoc IHLHandler
function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external payable {
if (msg.sender != mailbox) revert NotMailbox();
if (_origin != 10) revert NotRoot();
if (TypeCasts.bytes32ToAddress(_sender) != address(this)) revert NotModule();
uint256 command = _message.command();
if (command == Commands.DEPOSIT) {
address gauge = _message.toAddress();
(uint256 amount, uint256 tokenId, uint256 timestamp) = _message.voteParams();
address fvr = ILeafVoter(voter).gaugeToFees({_gauge: gauge});
IReward(fvr)._deposit({amount: amount, tokenId: tokenId, timestamp: timestamp});
address ivr = ILeafVoter(voter).gaugeToIncentive({_gauge: gauge});
IReward(ivr)._deposit({amount: amount, tokenId: tokenId, timestamp: timestamp});
} else if (command == Commands.WITHDRAW) {
address gauge = _message.toAddress();
(uint256 amount, uint256 tokenId, uint256 timestamp) = _message.voteParams();
address fvr = ILeafVoter(voter).gaugeToFees({_gauge: gauge});
IReward(fvr)._withdraw({amount: amount, tokenId: tokenId, timestamp: timestamp});
address ivr = ILeafVoter(voter).gaugeToIncentive({_gauge: gauge});
IReward(ivr)._withdraw({amount: amount, tokenId: tokenId, timestamp: timestamp});
} else if (command == Commands.GET_INCENTIVES) {
address ivr = ILeafVoter(voter).gaugeToIncentive({_gauge: _message.toAddress()});
address owner = _message.owner();
uint256 tokenId = _message.tokenId();
address[] memory tokens = _message.tokens();
IReward(ivr).getReward({_recipient: owner, _tokenId: tokenId, _tokens: tokens});
} else if (command == Commands.GET_FEES) {
address fvr = ILeafVoter(voter).gaugeToFees({_gauge: _message.toAddress()});
address owner = _message.owner();
uint256 tokenId = _message.tokenId();
address[] memory tokens = _message.tokens();
IReward(fvr).getReward({_recipient: owner, _tokenId: tokenId, _tokens: tokens});
} else if (command == Commands.CREATE_GAUGE) {
(
address poolFactory,
address votingRewardsFactory,
address gaugeFactory,
address token0,
address token1,
uint24 _poolParam
) = _message.createGaugeParams();
address pool = IPoolFactory(poolFactory).getPool({tokenA: token0, tokenB: token1, fee: _poolParam});
if (pool == address(0)) {
pool = IPoolFactory(poolFactory).createPool({tokenA: token0, tokenB: token1, fee: _poolParam});
}
ILeafVoter(voter).createGauge({
_poolFactory: poolFactory,
_pool: pool,
_votingRewardsFactory: votingRewardsFactory,
_gaugeFactory: gaugeFactory
});
} else if (command == Commands.NOTIFY) {
address gauge = _message.toAddress();
uint256 amount = _message.amount();
IXERC20(xerc20).mint({_user: address(this), _amount: amount});
IERC20(xerc20).safeIncreaseAllowance({spender: gauge, value: amount});
ILeafGauge(gauge).notifyRewardAmount({amount: amount});
} else if (command == Commands.NOTIFY_WITHOUT_CLAIM) {
address gauge = _message.toAddress();
uint256 amount = _message.amount();
IXERC20(xerc20).mint({_user: address(this), _amount: amount});
IERC20(xerc20).safeIncreaseAllowance({spender: gauge, value: amount});
ILeafGauge(gauge).notifyRewardWithoutClaim({amount: amount});
} else if (command == Commands.KILL_GAUGE) {
address gauge = _message.toAddress();
ILeafVoter(voter).killGauge({_gauge: gauge});
} else if (command == Commands.REVIVE_GAUGE) {
address gauge = _message.toAddress();
ILeafVoter(voter).reviveGauge({_gauge: gauge});
} else {
revert InvalidCommand();
}
emit ReceivedMessage({_origin: _origin, _sender: _sender, _value: msg.value, _message: string(_message)});
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
library TypeCasts {
// alignment preserving cast
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
// alignment preserving cast
function bytes32ToAddress(bytes32 _buf) internal pure returns (address) {
return address(uint160(uint256(_buf)));
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
interface IInterchainSecurityModule {
enum Types {
UNUSED,
ROUTING,
AGGREGATION,
LEGACY_MULTISIG,
MERKLE_ROOT_MULTISIG,
MESSAGE_ID_MULTISIG,
NULL, // used with relayer carrying no metadata
CCIP_READ,
ARB_L2_TO_L1
}
/**
* @notice Returns an enum that represents the type of security model
* encoded by this ISM.
* @dev Relayers infer how to fetch and format metadata.
*/
function moduleType() external view returns (uint8);
/**
* @notice Defines a security model responsible for verifying interchain
* messages based on the provided metadata.
* @param _metadata Off-chain metadata provided by a relayer, specific to
* the security model encoded by the module (e.g. validator signatures)
* @param _message Hyperlane encoded interchain message
* @return True if the message was verified
*/
function verify(
bytes calldata _metadata,
bytes calldata _message
) external returns (bool);
}
interface ISpecifiesInterchainSecurityModule {
function interchainSecurityModule()
external
view
returns (IInterchainSecurityModule);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling 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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IInterchainSecurityModule} from "@hyperlane/core/contracts/interfaces/IInterchainSecurityModule.sol";
import {IHLHandler} from "./IHLHandler.sol";
interface ILeafHLMessageModule is IHLHandler {
error InvalidCommand();
error NotModule();
/// @notice Returns the address of the bridge contract that this module is associated with
function bridge() external view returns (address);
/// @notice Returns the address of the xERC20 token that is bridged by this contract
function xerc20() external view returns (address);
/// @notice Returns voter on current chain
function voter() external view returns (address);
/// @notice Returns the address of the mailbox contract that is used to bridge by this contract
function mailbox() external view returns (address);
/// @notice Returns the address of the security module contract used by the bridge
function securityModule() external view returns (IInterchainSecurityModule);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ILeafMessageBridge {
error ZeroAddress();
event ModuleSet(address indexed _sender, address indexed _module);
/// @notice Returns the address of the xERC20 token that is bridged by this contract
function xerc20() external view returns (address);
/// @notice Returns the address of the module contract that is allowed to send messages x-chain
function module() external view returns (address);
/// @notice Returns the address of the voter contract
/// @dev Used to verify the sender of a message
function voter() external view returns (address);
/// @notice Sets the address of the module contract that is allowed to send messages x-chain
/// @dev Module handles x-chain messages
/// @param _module The address of the new module contract
function setModule(address _module) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IPoolFactory {
event SetFeeManager(address indexed feeManager);
event SetPauser(address indexed pauser);
event SetPauseState(bool indexed state);
event SetPoolAdmin(address indexed poolAdmin);
event PoolCreated(address indexed token0, address indexed token1, bool indexed stable, address pool, uint256);
event SetDefaultFee(bool indexed stable, uint256 fee);
event FeeModuleChanged(address indexed oldFeeModule, address indexed newFeeModule);
error FeeInvalid();
error FeeTooHigh();
error NotFeeManager();
error NotPauser();
error NotPoolAdmin();
error PoolAlreadyExists();
error SameAddress();
error ZeroFee();
error ZeroAddress();
/// @notice Return a single pool created by this factory
/// @return Address of pool
function allPools(uint256 index) external view returns (address);
/// @notice Returns all pools created by this factory
/// @return Array of pool addresses
function allPools() external view returns (address[] memory);
/// @notice returns the number of pools created from this factory
function allPoolsLength() external view returns (uint256);
/// @notice Is a valid pool created by this factory.
/// @param .
function isPool(address pool) external view returns (bool);
/// @notice Return address of pool created by this factory
/// @param tokenA .
/// @param tokenB .
/// @param stable True if stable, false if volatile
function getPool(address tokenA, address tokenB, bool stable) external view returns (address);
/// @notice Support for v3-style pools which wraps around getPool(tokenA,tokenB,stable)
/// @dev fee is converted to stable boolean.
/// @param tokenA .
/// @param tokenB .
/// @param fee 1 if stable, 0 if volatile, else returns address(0)
function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address);
/// @notice Set pool administrator
/// @dev Allowed to change the name and symbol of any pool created by this factory
/// @param _poolAdmin Address of the pool administrator
function setPoolAdmin(address _poolAdmin) external;
/// @notice Set the pauser for the factory contract
/// @dev The pauser can pause swaps on pools associated with the factory. Liquidity will always be withdrawable.
/// @dev Must be called by the pauser
/// @param _pauser Address of the pauser
function setPauser(address _pauser) external;
/// @notice Pause or unpause swaps on pools associated with the factory
/// @param _state True to pause, false to unpause
function setPauseState(bool _state) external;
/// @notice Set the fee manager for the factory contract
/// @dev The fee manager can set fees on pools associated with the factory.
/// @dev Must be called by the fee manager
/// @param _feeManager Address of the fee manager
function setFeeManager(address _feeManager) external;
/// @notice Updates the feeModule of the factory
/// @dev Must be called by the current fee manager
/// @param _feeModule The new feeModule of the factory
function setFeeModule(address _feeModule) external;
/// @notice Set default fee for stable and volatile pools.
/// @dev Throws if higher than maximum fee.
/// Throws if fee is zero.
/// @param _stable Stable or volatile pool.
/// @param _fee .
function setFee(bool _stable, uint256 _fee) external;
/// @notice Returns fee for a pool, as custom fees are possible.
function getFee(address _pool, bool _stable) external view returns (uint256);
/// @notice Create a pool given two tokens and if they're stable/volatile
/// @dev token order does not matter
/// @param tokenA .
/// @param tokenB .
/// @param stable .
function createPool(address tokenA, address tokenB, bool stable) external returns (address pool);
/// @notice Support for v3-style pools which wraps around createPool(tokenA,tokenB,stable)
/// @dev fee is converted to stable boolean
/// @dev token order does not matter
/// @param tokenA .
/// @param tokenB .
/// @param fee 1 if stable, 0 if volatile, else revert
function createPool(address tokenA, address tokenB, uint24 fee) external returns (address pool);
/// @notice The pool implementation used to create pools
/// @return Address of pool implementation
function implementation() external view returns (address);
/// @notice Whether the pools associated with the factory are paused or not.
/// @dev Pause only pauses swaps, liquidity will always be withdrawable.
function isPaused() external view returns (bool);
/// @notice The address of the pauser, can pause swaps on pools associated with factory.
/// @return Address of the pauser
function pauser() external view returns (address);
/// @notice The default fee for all stable pools
/// @return Default stable fee
function stableFee() external view returns (uint256);
/// @notice The default fee for all volatile pools
/// @return Default volatile fee
function volatileFee() external view returns (uint256);
/// @notice Maximum possible fee for default stable or volatile fee
/// @return 3%
function MAX_FEE() external view returns (uint256);
/// @notice Address of the fee manager, can set fees on pools associated with factory.
/// @notice This overrides the default fee for that pool.
/// @return Address of the fee manager
function feeManager() external view returns (address);
/// @notice Address of the fee module of the factory
/// @dev Can be changed by the current fee manager via setFeeModule
/// @return Address of the fee module
function feeModule() external view returns (address);
/// @notice Address of the pool administrator, can change the name and symbol of pools created by factory.
/// @return Address of the pool administrator
function poolAdmin() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ILeafGauge {
error NotAlive();
error NotAuthorized();
error NotModule();
error RewardRateTooHigh();
error ZeroAmount();
error ZeroRewardRate();
event Deposit(address indexed _sender, address indexed _to, uint256 _amount);
event Withdraw(address indexed _sender, uint256 _amount);
event NotifyReward(address indexed _sender, uint256 _amount);
event ClaimFees(address indexed _sender, uint256 _claimed0, uint256 _claimed1);
event ClaimRewards(address indexed _sender, uint256 _amount);
/// @notice Address of the pool LP token which is deposited (staked) for rewards
function stakingToken() external view returns (address);
/// @notice Address of the token (VELO v2) rewarded to stakers
function rewardToken() external view returns (address);
/// @notice Address of the FeesVotingReward contract linked to the gauge
function feesVotingReward() external view returns (address);
/// @notice Address of Velodrome v2 Voter
function voter() external view returns (address);
/// @notice Address of Velodrome v2 Bridge
function bridge() external view returns (address);
/// @notice Returns if gauge is linked to a legitimate Velodrome pool
function isPool() external view returns (bool);
/// @notice Timestamp end of current rewards period
function periodFinish() external view returns (uint256);
/// @notice Current reward rate of rewardToken to distribute per second
function rewardRate() external view returns (uint256);
/// @notice Most recent timestamp contract has updated state
function lastUpdateTime() external view returns (uint256);
/// @notice Most recent stored value of rewardPerToken
function rewardPerTokenStored() external view returns (uint256);
/// @notice Amount of stakingToken deposited for rewards
function totalSupply() external view returns (uint256);
/// @notice Get the amount of stakingToken deposited by an account
function balanceOf(address) external view returns (uint256);
/// @notice Cached rewardPerTokenStored for an account based on their most recent action
function userRewardPerTokenPaid(address) external view returns (uint256);
/// @notice Cached amount of rewardToken earned for an account
function rewards(address) external view returns (uint256);
/// @notice View to see the rewardRate given the timestamp of the start of the epoch
function rewardRateByEpoch(uint256) external view returns (uint256);
/// @notice Cached amount of fees generated from the Pool linked to the Gauge of token0
function fees0() external view returns (uint256);
/// @notice Cached amount of fees generated from the Pool linked to the Gauge of token1
function fees1() external view returns (uint256);
/// @notice Get the current reward rate per unit of stakingToken deposited
function rewardPerToken() external view returns (uint256 _rewardPerToken);
/// @notice Returns the last time the reward was modified or periodFinish if the reward has ended
function lastTimeRewardApplicable() external view returns (uint256 _time);
/// @notice Returns accrued balance to date from last claim / first deposit.
function earned(address _account) external view returns (uint256 _earned);
/// @notice Total amount of rewardToken to distribute for the current rewards period
function left() external view returns (uint256 _left);
/// @notice Retrieve rewards for an address.
/// @dev Throws if not called by same address or voter.
/// @param _account .
function getReward(address _account) external;
/// @notice Deposit LP tokens into gauge for msg.sender
/// @param _amount .
function deposit(uint256 _amount) external;
/// @notice Deposit LP tokens into gauge for any user
/// @param _amount .
/// @param _recipient Recipient to give balance to
function deposit(uint256 _amount, address _recipient) external;
/// @notice Withdraw LP tokens for user
/// @param _amount .
function withdraw(uint256 _amount) external;
/// @notice Notifies gauge of gauge rewards. Assumes gauge reward tokens is 18 decimals.
/// @dev If not 18 decimals, rewardRate may have rounding issues.
/// @param amount Amount of rewards to be deposited into gauge
function notifyRewardAmount(uint256 amount) external;
/// @notice Notifies gauge of gauge rewards without distributing its fees.
/// @dev Assumes gauge reward tokens is 18 decimals.
/// If not 18 decimals, rewardRate may have rounding issues.
/// @param amount Amount of rewards to be deposited into gauge
function notifyRewardWithoutClaim(uint256 amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ILeafVoter {
error NotAGauge();
error ZeroAddress();
error NotAuthorized();
error GaugeAlreadyKilled();
error GaugeAlreadyRevived();
event GaugeCreated(
address indexed poolFactory,
address indexed votingRewardsFactory,
address indexed gaugeFactory,
address pool,
address incentiveVotingReward,
address feeVotingReward,
address gauge
);
event GaugeKilled(address indexed gauge);
event GaugeRevived(address indexed gauge);
event WhitelistToken(address indexed token, bool indexed _bool);
/// @notice Address of bridge contract used to forward x-chain messages
function bridge() external view returns (address);
/// @dev Pool => Gauge
function gauges(address _pool) external view returns (address);
/// @dev Gauge => Pool
function poolForGauge(address _gauge) external view returns (address);
/// @dev Gauge => Fees Voting Reward
function gaugeToFees(address _gauge) external view returns (address);
/// @dev Gauge => Incentives Voting Reward
function gaugeToIncentive(address _gauge) external view returns (address);
/// @notice Check if a given address is a gauge
/// @param _gauge The address to be checked
/// @return Whether the address is a gauge or not
function isGauge(address _gauge) external view returns (bool);
/// @notice Check if a given gauge is alive
/// @param _gauge The address of the gauge to be checked
/// @return Whether the gauge is alive or not
function isAlive(address _gauge) external view returns (bool);
/// @notice Returns the number of times a token has been whitelisted
/// @param _token Address of token to view whitelist count
/// @return Number of times token has been whitelisted
function whitelistTokenCount(address _token) external view returns (uint256);
/// @notice Get all Whitelisted Tokens approved by the Voter
/// @return Array of Whitelisted Token addresses
function whitelistedTokens() external view returns (address[] memory);
/// @notice Paginated view of all Whitelisted Tokens
/// @dev Should not assume the last Token returned is at index matching given `_end`,
/// because if `_end` exceeds `length`, implementation defaults to `length`
/// @param _start Index of first Token to be fetched
/// @param _end End index for pagination
/// @return _tokens Array of whitelisted tokens
function whitelistedTokens(uint256 _start, uint256 _end) external view returns (address[] memory _tokens);
/// @notice Check if a given token is whitelisted
/// @param _token The address of the token to be checked
/// @return Whether the token is whitelisted or not
function isWhitelistedToken(address _token) external view returns (bool);
/// @notice Get the length of the whitelistedTokens array
function whitelistedTokensLength() external view returns (uint256);
/// @notice Create a new gauge
/// @dev Only callable by Message Bridge
/// @param _poolFactory .
/// @param _pool .
/// @param _votingRewardsFactory .
/// @param _gaugeFactory .
function createGauge(address _poolFactory, address _pool, address _votingRewardsFactory, address _gaugeFactory)
external
returns (address _gauge);
/// @notice Kills a gauge. The gauge will not receive any new emissions and cannot be deposited into.
/// Can still withdraw from gauge.
/// @dev Only callable by Message Bridge
/// Throws if gauge already killed.
/// @param _gauge .
function killGauge(address _gauge) external;
/// @notice Revives a killed gauge. Gauge will be able to receive emissions and deposits again.
/// @dev Only callable by Message Bridge
/// Throws if gauge is not killed.
/// @param _gauge .
function reviveGauge(address _gauge) external;
/// @notice Claim emissions from gauges.
/// @param _gauges Array of gauges to collect emissions from.
function claimRewards(address[] memory _gauges) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IReward {
error InvalidReward();
error NotAuthorized();
error NotGauge();
error NotEscrowToken();
error NotSingleToken();
error NotVotingEscrow();
error NotWhitelisted();
error ZeroAmount();
event Deposit(uint256 indexed _tokenId, uint256 _amount);
event Withdraw(uint256 indexed _tokenId, uint256 _amount);
event NotifyReward(address indexed _sender, address indexed _reward, uint256 indexed _epoch, uint256 _amount);
event ClaimRewards(address indexed _sender, address indexed _reward, uint256 _amount);
/// @notice A checkpoint for marking balance
struct Checkpoint {
uint256 timestamp;
uint256 balanceOf;
}
/// @notice A checkpoint for marking supply
struct SupplyCheckpoint {
uint256 timestamp;
uint256 supply;
}
/// @notice Epoch duration constant (7 days)
function DURATION() external view returns (uint256);
/// @notice Address of LeafVoter.sol
function voter() external view returns (address);
/// @dev Address which has permission to externally call _deposit() & _withdraw()
function authorized() external view returns (address);
/// @notice Total amount currently deposited via _deposit()
function totalSupply() external view returns (uint256);
/// @notice Current amount deposited by tokenId
function balanceOf(uint256 tokenId) external view returns (uint256);
/// @notice Amount of tokens to reward depositors for a given epoch
/// @param token Address of token to reward
/// @param epochStart Startime of rewards epoch
/// @return Amount of token
function tokenRewardsPerEpoch(address token, uint256 epochStart) external view returns (uint256);
/// @notice Most recent timestamp a veNFT has claimed their rewards
/// @param token Address of token rewarded
/// @param tokenId veNFT unique identifier
/// @return Timestamp
function lastEarn(address token, uint256 tokenId) external view returns (uint256);
/// @notice List of reward tokens
/// @param _index Index of reward token
/// @return Address of reward token
function rewards(uint256 _index) external view returns (address);
/// @notice True if a token is or has been an active reward token, else false
function isReward(address token) external view returns (bool);
/// @notice The number of checkpoints for each tokenId deposited
function numCheckpoints(uint256 tokenId) external view returns (uint256);
/// @notice The total number of checkpoints
function supplyNumCheckpoints() external view returns (uint256);
/// @notice Deposit an amount into the rewards contract to earn future rewards associated to a veNFT
/// @dev Internal notation used as only callable internally by `authorized.module()`.
/// @param amount Vote weight to deposit
/// @param tokenId Token ID of weight to deposit
/// @param timestamp Timestamp of deposit
function _deposit(uint256 amount, uint256 tokenId, uint256 timestamp) external;
/// @notice Withdraw an amount from the rewards contract associated to a veNFT
/// @dev Internal notation used as only callable internally by `authorized.module()`.
/// @param amount Vote weight to withdraw
/// @param tokenId Token ID of weight to withdraw
/// @param timestamp Timestamp of withdraw
function _withdraw(uint256 amount, uint256 tokenId, uint256 timestamp) external;
/// @notice Claim the rewards earned by a veNFT staker
/// @param _recipient Address of reward recipient
/// @param _tokenId Unique identifier of the veNFT
/// @param _tokens Array of tokens to claim rewards of
function getReward(address _recipient, uint256 _tokenId, address[] memory _tokens) external;
/// @notice Add rewards for stakers to earn
/// @param token Address of token to reward
/// @param amount Amount of token to transfer to rewards
function notifyRewardAmount(address token, uint256 amount) external;
/// @notice Determine the prior balance for an account as of a block number
/// @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
/// @param tokenId The token of the NFT to check
/// @param timestamp The timestamp to get the balance at
/// @return The balance the account had as of the given block
function getPriorBalanceIndex(uint256 tokenId, uint256 timestamp) external view returns (uint256);
/// @notice Determine the prior index of supply staked by of a timestamp
/// @dev Timestamp must be <= current timestamp
/// @param timestamp The timestamp to get the index at
/// @return Index of supply checkpoint
function getPriorSupplyIndex(uint256 timestamp) external view returns (uint256);
/// @notice Get number of rewards tokens
function rewardsListLength() external view returns (uint256);
/// @notice Calculate how much in rewards are earned for a specific token and veNFT
/// @param token Address of token to fetch rewards of
/// @param tokenId Unique identifier of the veNFT
/// @return Amount of token earned in rewards
function earned(address token, uint256 tokenId) external view returns (uint256);
function checkpoints(uint256 tokenId, uint256 index) external view returns (uint256 timestamp, uint256 balanceOf);
function supplyCheckpoints(uint256 index) external view returns (uint256 timestamp, uint256 supply);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.19 <0.9.0;
/// @notice Commands for x-chain interactions
/// @dev Existing commands cannot be modified but new commands can be added
library Commands {
uint256 public constant NOTIFY = 0x00;
uint256 public constant NOTIFY_WITHOUT_CLAIM = 0x01;
uint256 public constant GET_INCENTIVES = 0x02;
uint256 public constant GET_FEES = 0x03;
uint256 public constant DEPOSIT = 0x04;
uint256 public constant WITHDRAW = 0x05;
uint256 public constant CREATE_GAUGE = 0x06;
uint256 public constant KILL_GAUGE = 0x07;
uint256 public constant REVIVE_GAUGE = 0x08;
uint256 private constant COMMAND_OFFSET = 0;
uint256 private constant ADDRESS_OFFSET = 1;
/// @dev Second and Third offset are used in messages with multiple consecutive addresses
uint256 private constant SECOND_OFFSET = ADDRESS_OFFSET + 20;
uint256 private constant THIRD_OFFSET = SECOND_OFFSET + 20;
// Offsets for Create Gauge Command
uint256 private constant TOKEN0_OFFSET = THIRD_OFFSET + 20;
uint256 private constant TOKEN1_OFFSET = TOKEN0_OFFSET + 20;
uint256 private constant POOL_PARAM_OFFSET = TOKEN1_OFFSET + 20;
// Offsets for Reward Claims
uint256 private constant LENGTH_OFFSET = THIRD_OFFSET + 32;
uint256 private constant TOKENS_OFFSET = LENGTH_OFFSET + 1;
// Offset for Deposit/Withdraw
uint256 private constant TOKEN_ID_OFFSET = ADDRESS_OFFSET + 20 + 32;
uint256 private constant TIMESTAMP_OFFSET = TOKEN_ID_OFFSET + 32;
// Offset for Send Token
uint256 private constant AMOUNT_OFFSET = COMMAND_OFFSET + 20;
uint256 private constant TOKEN_ID_WITHOUT_COMMAND_OFFSET = AMOUNT_OFFSET + 32;
/// @notice Returns the command encoded in the message
/// @dev Assumes message is encoded as (command, ...)
/// @param _message The message to be decoded
function command(bytes calldata _message) internal pure returns (uint256) {
return uint256(uint8(bytes1(_message[COMMAND_OFFSET:COMMAND_OFFSET + 1])));
}
/// @notice Returns the address encoded in the message
/// @dev Assumes message is encoded as (command, address, ...)
/// @param _message The message to be decoded
function toAddress(bytes calldata _message) internal pure returns (address) {
return address(bytes20(_message[ADDRESS_OFFSET:ADDRESS_OFFSET + 20]));
}
/// @notice Returns the message without the encoded command
/// @dev Assumes message is encoded as (command, message)
/// @param _message The message to be decoded
function messageWithoutCommand(bytes calldata _message) internal pure returns (bytes calldata) {
return bytes(_message[COMMAND_OFFSET + 1:]);
}
/// @notice Returns the amount encoded in the message
/// @dev Assumes message is encoded as (command, amount, ...)
/// @param _message The message to be decoded
function amount(bytes calldata _message) internal pure returns (uint256) {
return uint256(bytes32(_message[SECOND_OFFSET:SECOND_OFFSET + 32]));
}
/// @notice Returns the amount, tokenId and timestamp encoded in the message
/// @dev Assumes message is encoded as (command, amount, tokenId, timestamp, ...)
/// @param _message The message to be decoded
function voteParams(bytes calldata _message) internal pure returns (uint256, uint256, uint256) {
return (
uint256(bytes32(_message[SECOND_OFFSET:SECOND_OFFSET + 32])),
uint256(bytes32(_message[TOKEN_ID_OFFSET:TIMESTAMP_OFFSET])),
uint256(uint40(bytes5(_message[TIMESTAMP_OFFSET:TIMESTAMP_OFFSET + 5])))
);
}
/// @notice Returns the parameters necessary for gauge creation, encoded in the message
/// @dev Assumes message is encoded as (command, address, address, address, address, uint24)
/// @param _message The message to be decoded
function createGaugeParams(bytes calldata _message)
internal
pure
returns (address, address, address, address, address, uint24)
{
return (
address(bytes20(_message[ADDRESS_OFFSET:ADDRESS_OFFSET + 20])),
address(bytes20(_message[SECOND_OFFSET:SECOND_OFFSET + 20])),
address(bytes20(_message[THIRD_OFFSET:THIRD_OFFSET + 20])),
address(bytes20(_message[TOKEN0_OFFSET:TOKEN0_OFFSET + 20])),
address(bytes20(_message[TOKEN1_OFFSET:TOKEN1_OFFSET + 20])),
uint24(bytes3(_message[POOL_PARAM_OFFSET:POOL_PARAM_OFFSET + 3]))
);
}
/// @notice Returns the owner encoded in the message
/// @dev Assumes message is encoded as (command, address, owner, ...)
/// @param _message The message to be decoded
function owner(bytes calldata _message) internal pure returns (address) {
return address(bytes20(_message[SECOND_OFFSET:SECOND_OFFSET + 20]));
}
/// @notice Returns the tokenId encoded in a reward claiming message
/// @dev Assumes message is encoded as (command, address, tokenId, ...)
/// @param _message The message to be decoded
function tokenId(bytes calldata _message) internal pure returns (uint256) {
return uint256(bytes32(_message[THIRD_OFFSET:THIRD_OFFSET + 32]));
}
/// @notice Returns the token addresses encoded in the message
/// @dev Assumes message has length and token addresses encoded
/// @param _message The message to be decoded
function tokens(bytes calldata _message) internal pure returns (address[] memory _tokens) {
uint256 length = uint8(bytes1(_message[LENGTH_OFFSET:LENGTH_OFFSET + 1]));
_tokens = new address[](length);
for (uint256 i = 0; i < length; i++) {
_tokens[i] =
address(uint160(uint256(bytes32(_message[TOKENS_OFFSET + (i * 32):TOKENS_OFFSET + ((i + 1) * 32)]))));
}
}
// Token Bridge
// Send Token - (address, uint256)
uint256 public constant SEND_TOKEN_LENGTH = 52;
// Send Token and Lock - (address, uint256, uint256)
uint256 public constant SEND_TOKEN_AND_LOCK_LENGTH = 84;
/// @notice Returns the recipient and amount encoded in the message
/// @dev Assumes no command is encoded and message is encoded as (address, amount)
/// @param _message The message to be decoded
function recipientAndAmount(bytes calldata _message) internal pure returns (address, uint256) {
return (
address(bytes20(_message[COMMAND_OFFSET:COMMAND_OFFSET + 20])),
uint256(bytes32(_message[AMOUNT_OFFSET:AMOUNT_OFFSET + 32]))
);
}
/// @notice Returns the recipient, amount and tokenId encoded in the message
/// @dev Assumes no command is encoded and message is encoded as (address, amount, tokenId)
/// @param _message The message to be decoded
function sendTokenAndLockParams(bytes calldata _message) internal pure returns (address, uint256, uint256) {
return (
address(bytes20(_message[COMMAND_OFFSET:COMMAND_OFFSET + 20])),
uint256(bytes32(_message[AMOUNT_OFFSET:AMOUNT_OFFSET + 32])),
uint256(bytes32(_message[TOKEN_ID_WITHOUT_COMMAND_OFFSET:TOKEN_ID_WITHOUT_COMMAND_OFFSET + 32]))
);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../xerc20/MintLimits.sol";
import {RateLimitMidPoint} from "../../libraries/rateLimits/RateLimitMidpointCommonLibrary.sol";
interface IXERC20 {
/// @notice Emits when a limit is set
/// @param _bridge The address of the bridge we are setting the limit to
/// @param _bufferCap The updated buffer cap for the bridge
event BridgeLimitsSet(address indexed _bridge, uint256 _bufferCap);
/// @notice The address of the lockbox contract
function lockbox() external view returns (address);
/// @notice Maps bridge address to bridge rate limits
/// @param _bridge The bridge we are viewing the limits of
/// @return _rateLimit The limits of the bridge
function rateLimits(address _bridge) external view returns (RateLimitMidPoint memory _rateLimit);
/// @notice Returns the max limit of a bridge
/// @param _bridge The bridge we are viewing the limits of
/// @return _limit The limit the bridge has
function mintingMaxLimitOf(address _bridge) external view returns (uint256 _limit);
/// @notice Returns the max limit of a bridge
/// @param _bridge the bridge we are viewing the limits of
/// @return _limit The limit the bridge has
function burningMaxLimitOf(address _bridge) external view returns (uint256 _limit);
/// @notice Returns the current limit of a bridge
/// @param _bridge The bridge we are viewing the limits of
/// @return _limit The limit the bridge has
function mintingCurrentLimitOf(address _bridge) external view returns (uint256 _limit);
/// @notice Returns the current limit of a bridge
/// @param _bridge the bridge we are viewing the limits of
/// @return _limit The limit the bridge has
function burningCurrentLimitOf(address _bridge) external view returns (uint256 _limit);
/// @notice Mints tokens for a user
/// @dev Can only be called by a bridge
/// @param _user The address of the user who needs tokens minted
/// @param _amount The amount of tokens being minted
function mint(address _user, uint256 _amount) external;
/// @notice Burns tokens for a user
/// @dev Can only be called by a bridge
/// @param _user The address of the user who needs tokens burned
/// @param _amount The amount of tokens being burned
function burn(address _user, uint256 _amount) external;
/// @notice Conform to the xERC20 setLimits interface
/// @dev Can only be called if the bridge already has a buffer cap
/// @param _bridge The bridge we are setting the limits of
/// @param _newBufferCap The new buffer cap, uint112 max for unlimited
function setBufferCap(address _bridge, uint256 _newBufferCap) external;
/// @notice Sets rate limit per second for a bridge
/// @dev Can only be called if the bridge already has a buffer cap
/// @param _bridge The bridge we are setting the limits of
/// @param _newRateLimitPerSecond The new rate limit per second
function setRateLimitPerSecond(address _bridge, uint128 _newRateLimitPerSecond) external;
/// @notice Adds a new bridge to the currently active bridges
/// @param _newBridge The bridge to add
function addBridge(MintLimits.RateLimitMidPointInfo memory _newBridge) external;
/// @notice Removes a bridge from the currently active bridges
/// deleting its buffer stored, buffer cap, mid point and last
/// buffer used time
/// @param _bridge The bridge to remove
function removeBridge(address _bridge) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IInterchainSecurityModule} from "@hyperlane/core/contracts/interfaces/IInterchainSecurityModule.sol";
interface ISpecifiesInterchainSecurityModule {
event InterchainSecurityModuleSet(address indexed _new);
// @notice The currently set InterchainSecurityModule.
function interchainSecurityModule() external view returns (IInterchainSecurityModule);
// @notice Sets the new InterchainSecurityModule.
/// @dev Throws if not called by owner.
/// @param _ism .
function setInterchainSecurityModule(address _ism) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IMessageRecipient} from "@hyperlane/core/contracts/interfaces/IMessageRecipient.sol";
interface IHLHandler is IMessageRecipient {
error NotMailbox();
error NotRoot();
event ReceivedMessage(uint32 indexed _origin, bytes32 indexed _sender, uint256 _value, string _message);
/// @notice Callback function used by the mailbox contract to handle incoming messages
/// @param _origin The domain from which the message originates
/// @param _sender The address of the sender of the message
/// @param _message The message payload
function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external payable override;
}// SPDX-License-Identifier: BSD-3.0
pragma solidity >=0.8.19 <0.9.0;
import {
RateLimitMidPoint,
RateLimitMidpointCommonLibrary
} from "../libraries/rateLimits/RateLimitMidpointCommonLibrary.sol";
import {RateLimitedMidpointLibrary} from "../libraries/rateLimits/RateLimitedMidpointLibrary.sol";
/// @dev Modified lightly from Zelt at commit 30b2ba0, with the following changes:
/// - Updated the Solidity compiler version used;
/// - Refactored the `_rateLimits` mapping to be internal;
/// - Removed internal `_addLimits(...)` & `_removeLimits(...)` helpers.
/// Can refer to: (https://github.com/solidity-labs-io/zelt/blob/30b2ba0352422471c03b233d55feddfbdba198a3/src/impl/MintLimits.sol)
abstract contract MintLimits {
using RateLimitMidpointCommonLibrary for RateLimitMidPoint;
using RateLimitedMidpointLibrary for RateLimitMidPoint;
/// @notice struct for initializing rate limit
struct RateLimitMidPointInfo {
/// @notice the buffer cap for this bridge
uint112 bufferCap;
/// @notice the rate limit per second for this bridge
uint128 rateLimitPerSecond;
/// @notice the bridge address
address bridge;
}
/// @notice rate limit for each bridge contract
mapping(address bridge => RateLimitMidPoint bridgeRateLimit) internal _rateLimits;
/// @notice emitted when a rate limit is added or removed
/// @param bridge the bridge address
/// @param bufferCap the new buffer cap for this bridge
/// @param rateLimitPerSecond the new rate limit per second for this bridge
event ConfigurationChanged(address indexed bridge, uint112 bufferCap, uint128 rateLimitPerSecond);
//// ------------------------------------------------------------
//// ------------------------------------------------------------
//// -------------------- View Functions ------------------------
//// ------------------------------------------------------------
//// ------------------------------------------------------------
/// @notice the amount of action used before hitting limit
/// @dev replenishes at rateLimitPerSecond per second up to bufferCap
function buffer(address from) public view returns (uint256) {
return _rateLimits[from].buffer();
}
/// @notice the cap of the buffer for this address
/// @param from address to get buffer cap for
function bufferCap(address from) public view returns (uint256) {
return _rateLimits[from].bufferCap;
}
/// @notice the amount the buffer replenishes towards the midpoint per second
/// @param from address to get rate limit for
function rateLimitPerSecond(address from) public view returns (uint256) {
return _rateLimits[from].rateLimitPerSecond;
}
//// ------------------------------------------------------------
//// ------------------------------------------------------------
//// -------------- Internal Helper Functions -------------------
//// ------------------------------------------------------------
//// ------------------------------------------------------------
//// ----------- Depleting and Replenishing Buffer --------------
/// @notice the method that enforces the rate limit.
/// Decreases buffer by "amount".
/// If buffer is <= amount, revert
/// @param amount to decrease buffer by
function _depleteBuffer(address from, uint256 amount) internal {
require(amount != 0, "MintLimits: deplete amount cannot be 0");
_rateLimits[from].depleteBuffer(amount);
}
/// @notice function to replenish buffer
/// @param from address to set rate limit for
/// @param amount to increase buffer by if under buffer cap
function _replenishBuffer(address from, uint256 amount) internal {
require(amount != 0, "MintLimits: replenish amount cannot be 0");
_rateLimits[from].replenishBuffer(amount);
}
//// -------------- Modifying Existing Limits -------------------
/// @notice function to set rate limit per second
/// @dev updates the current buffer and last buffer used time first,
/// then sets the new rate limit per second
/// @param from address to set rate limit for
/// @param newRateLimitPerSecond new rate limit per second
function _setRateLimitPerSecond(address from, uint128 newRateLimitPerSecond) internal {
require(newRateLimitPerSecond <= maxRateLimitPerSecond(), "MintLimits: rateLimitPerSecond too high");
require(_rateLimits[from].bufferCap != 0, "MintLimits: non-existent rate limit");
_rateLimits[from].setRateLimitPerSecond(newRateLimitPerSecond);
emit ConfigurationChanged(from, _rateLimits[from].bufferCap, newRateLimitPerSecond);
}
/// @notice function to set buffer cap
/// @dev updates the current buffer and last buffer used time first,
/// then sets the new buffer cap
/// @param from address to set the buffer cap for
/// @param newBufferCap new buffer cap
function _setBufferCap(address from, uint112 newBufferCap) internal {
require(newBufferCap != 0, "MintLimits: bufferCap cannot be 0");
require(_rateLimits[from].bufferCap != 0, "MintLimits: non-existent rate limit");
require(newBufferCap > minBufferCap(), "MintLimits: buffer cap below min");
_rateLimits[from].setBufferCap(newBufferCap);
emit ConfigurationChanged(from, newBufferCap, _rateLimits[from].rateLimitPerSecond);
}
//// -------------- Adding Limits -------------------
/// @notice add an individual rate limit
/// @param rateLimit cap on buffer size for this rate limited instance
function _addLimit(RateLimitMidPointInfo memory rateLimit) internal {
require(rateLimit.rateLimitPerSecond <= maxRateLimitPerSecond(), "MintLimits: rateLimitPerSecond too high");
require(rateLimit.bridge != address(0), "MintLimits: invalid bridge address");
require(_rateLimits[rateLimit.bridge].bufferCap == 0, "MintLimits: rate limit already exists");
require(rateLimit.bufferCap > minBufferCap(), "MintLimits: buffer cap below min");
_rateLimits[rateLimit.bridge] = RateLimitMidPoint({
bufferCap: rateLimit.bufferCap,
lastBufferUsedTime: uint32(block.timestamp),
bufferStored: uint112(rateLimit.bufferCap / 2),
midPoint: uint112(rateLimit.bufferCap / 2),
rateLimitPerSecond: rateLimit.rateLimitPerSecond
});
emit ConfigurationChanged(rateLimit.bridge, rateLimit.bufferCap, rateLimit.rateLimitPerSecond);
}
//// -------------- Removing Limits -------------------
/// @notice remove a bridge from the rate limit mapping, deleting all data
/// @param bridge the bridge address to remove
function _removeLimit(address bridge) internal {
require(_rateLimits[bridge].bufferCap != 0, "MintLimits: cannot remove non-existent rate limit");
delete _rateLimits[bridge];
emit ConfigurationChanged(bridge, 0, 0);
}
//// ------------------------------------------------------------
//// ------------------------------------------------------------
//// ---------------------- Virtual Function --------------------
//// ------------------------------------------------------------
//// ------------------------------------------------------------
/// @notice the maximum rate limit per second allowed in any bridge
/// must be overridden by child contract
function maxRateLimitPerSecond() public pure virtual returns (uint128);
/// @notice the minimum buffer cap, non inclusive
/// must be overridden by child contract
function minBufferCap() public pure virtual returns (uint112);
}// SPDX-License-Identifier: BSD-3.0
pragma solidity >=0.8.19 <0.9.0;
import {Math} from "@openzeppelin5/contracts/utils/math/Math.sol";
/// @notice two rate storage slots per rate limit
struct RateLimitMidPoint {
//// -------------------------------------------- ////
//// ------------------ SLOT 0 ------------------ ////
//// -------------------------------------------- ////
/// @notice the rate per second for this contract
uint128 rateLimitPerSecond;
/// @notice the cap of the buffer that can be used at once
uint112 bufferCap;
//// -------------------------------------------- ////
//// ------------------ SLOT 1 ------------------ ////
//// -------------------------------------------- ////
/// @notice the last time the buffer was used by the contract
uint32 lastBufferUsedTime;
/// @notice the buffer at the timestamp of lastBufferUsedTime
uint112 bufferStored;
/// @notice the mid point of the buffer
uint112 midPoint;
}
/// @title abstract contract for putting a rate limit on how fast a contract
/// can perform an action e.g. Minting
/// @author Elliot Friedman
/// @dev Modified lightly from Zelt at commit 30b2ba0 to update the Solidity Compiler version used
/// Can refer to: (https://github.com/solidity-labs-io/zelt/blob/30b2ba0352422471c03b233d55feddfbdba198a3/src/lib/RateLimitMidpointCommonLibrary.sol)
library RateLimitMidpointCommonLibrary {
/// @notice event emitted when buffer cap is updated
event BufferCapUpdate(uint256 oldBufferCap, uint256 newBufferCap);
/// @notice event emitted when rate limit per second is updated
event RateLimitPerSecondUpdate(uint256 oldRateLimitPerSecond, uint256 newRateLimitPerSecond);
/// @notice the amount of action available before hitting the rate limit
/// @dev replenishes at rateLimitPerSecond per second back to midPoint
/// @param limit pointer to the rate limit object
function buffer(RateLimitMidPoint storage limit) public view returns (uint256) {
uint256 elapsed;
unchecked {
elapsed = uint32(block.timestamp) - limit.lastBufferUsedTime;
}
uint256 accrued = uint256(limit.rateLimitPerSecond) * elapsed;
if (limit.bufferStored < limit.midPoint) {
return Math.min(uint256(limit.bufferStored) + accrued, uint256(limit.midPoint));
} else if (limit.bufferStored > limit.midPoint) {
/// past midpoint so subtract accrued off bufferStored back down to midpoint
/// second part of if statement will not be evaluated if first part is true
if (accrued > limit.bufferStored || limit.bufferStored - accrued < limit.midPoint) {
/// if accrued is more than buffer stored, subtracting will underflow,
/// and we are at the midpoint, so return that
return limit.midPoint;
} else {
return limit.bufferStored - accrued;
}
} else {
/// no change
return limit.bufferStored;
}
}
/// @notice syncs the buffer to the current time
/// @dev should be called before any action that
/// updates buffer cap or rate limit per second
/// @param limit pointer to the rate limit object
function sync(RateLimitMidPoint storage limit) internal {
uint112 newBuffer = uint112(buffer(limit));
uint32 blockTimestamp = uint32(block.timestamp);
limit.lastBufferUsedTime = blockTimestamp;
limit.bufferStored = newBuffer;
}
/// @notice set the rate limit per second
/// @param limit pointer to the rate limit object
/// @param newRateLimitPerSecond the new rate limit per second
function setRateLimitPerSecond(RateLimitMidPoint storage limit, uint128 newRateLimitPerSecond) internal {
sync(limit);
uint256 oldRateLimitPerSecond = limit.rateLimitPerSecond;
limit.rateLimitPerSecond = newRateLimitPerSecond;
emit RateLimitPerSecondUpdate(oldRateLimitPerSecond, newRateLimitPerSecond);
}
/// @notice set the buffer cap, but first sync to accrue all rate limits accrued
/// @param limit pointer to the rate limit object
/// @param newBufferCap the new buffer cap to set
function setBufferCap(RateLimitMidPoint storage limit, uint112 newBufferCap) internal {
sync(limit);
uint256 oldBufferCap = limit.bufferCap;
limit.bufferCap = newBufferCap;
limit.midPoint = uint112(newBufferCap / 2);
/// if buffer stored is gt buffer cap, then we need set buffer stored to buffer cap
if (limit.bufferStored > newBufferCap) {
limit.bufferStored = newBufferCap;
}
emit BufferCapUpdate(oldBufferCap, newBufferCap);
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
interface IMessageRecipient {
function handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) external payable;
}// SPDX-License-Identifier: BSD-3.0
pragma solidity >=0.8.19 <0.9.0;
import {Math} from "@openzeppelin5/contracts/utils/math/Math.sol";
import {RateLimitMidPoint, RateLimitMidpointCommonLibrary} from "./RateLimitMidpointCommonLibrary.sol";
/// @title library for putting a rate limit on how fast a contract
/// can perform an action e.g. Minting and Burning with a midpoint
/// @author Elliot Friedman
/// @dev Modified lightly from Zelt at commit 30b2ba0 to update the Solidity Compiler version used
/// Can refer to: (https://github.com/solidity-labs-io/zelt/blob/30b2ba0352422471c03b233d55feddfbdba198a3/src/lib/RateLimitedMidpointLibrary.sol)
library RateLimitedMidpointLibrary {
using RateLimitMidpointCommonLibrary for RateLimitMidPoint;
/// @notice event emitted when buffer gets eaten into
event BufferUsed(uint256 amountUsed, uint256 bufferRemaining);
/// @notice event emitted when buffer gets replenished
event BufferReplenished(uint256 amountReplenished, uint256 bufferRemaining);
/// @notice the method that enforces the rate limit.
/// Decreases buffer by "amount".
/// If buffer is <= amount, revert
/// @param limit pointer to the rate limit object
/// @param amount to decrease buffer by
function depleteBuffer(RateLimitMidPoint storage limit, uint256 amount) internal {
/// SLOAD 2x
uint256 newBuffer = limit.buffer();
require(amount <= newBuffer, "RateLimited: rate limit hit");
uint32 blockTimestamp = uint32(block.timestamp);
uint112 newBufferStored = uint112(newBuffer - amount);
/// gas optimization to only use a single SSTORE
limit.lastBufferUsedTime = blockTimestamp;
limit.bufferStored = newBufferStored;
emit BufferUsed(amount, newBufferStored);
}
/// @notice function to replenish buffer
/// @param amount to increase buffer by if under buffer cap
/// @param limit pointer to the rate limit object
function replenishBuffer(RateLimitMidPoint storage limit, uint256 amount) internal {
/// SLOAD 2x
uint256 buffer = limit.buffer();
/// warm SLOAD
uint256 _bufferCap = limit.bufferCap;
uint256 newBuffer = buffer + amount;
require(newBuffer <= _bufferCap, "RateLimited: buffer cap overflow");
uint32 blockTimestamp = uint32(block.timestamp);
/// ensure that bufferStored cannot be gt buffer cap
uint112 newBufferStored = uint112(newBuffer);
/// gas optimization to only use a single SSTORE
limit.lastBufferUsedTime = blockTimestamp;
limit.bufferStored = newBufferStored;
emit BufferReplenished(amount, newBufferStored);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}{
"remappings": [
"@openzeppelin5/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/src/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"createX/=lib/createX/src/",
"@nomad-xyz/=lib/ExcessivelySafeCall/",
"@hyperlane/=node_modules/@hyperlane-xyz/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"ExcessivelySafeCall/=lib/ExcessivelySafeCall/src/",
"openzeppelin/=lib/createX/lib/openzeppelin-contracts/contracts/",
"solady/=lib/createX/lib/solady/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_bridge","type":"address"},{"internalType":"address","name":"_mailbox","type":"address"},{"internalType":"address","name":"_ism","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InvalidCommand","type":"error"},{"inputs":[],"name":"NotMailbox","type":"error"},{"inputs":[],"name":"NotModule","type":"error"},{"inputs":[],"name":"NotRoot","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_new","type":"address"}],"name":"InterchainSecurityModuleSet","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":true,"internalType":"uint32","name":"_origin","type":"uint32"},{"indexed":true,"internalType":"bytes32","name":"_sender","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"string","name":"_message","type":"string"}],"name":"ReceivedMessage","type":"event"},{"inputs":[],"name":"bridge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"_origin","type":"uint32"},{"internalType":"bytes32","name":"_sender","type":"bytes32"},{"internalType":"bytes","name":"_message","type":"bytes"}],"name":"handle","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"interchainSecurityModule","outputs":[{"internalType":"contract IInterchainSecurityModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mailbox","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"securityModule","outputs":[{"internalType":"contract IInterchainSecurityModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_ism","type":"address"}],"name":"setInterchainSecurityModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xerc20","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
610100604052348015610010575f5ffd5b50604051611ec9380380611ec983398101604081905261002f91610214565b836001600160a01b03811661005d57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b610066816101aa565b506001600160a01b03831660808190526040805160016203427360e41b03198152905163ffcbd8d0916004808201926020929091908290030181865afa1580156100b2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100d69190610265565b6001600160a01b031660a0816001600160a01b031681525050826001600160a01b03166346c96aac6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012b573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061014f9190610265565b6001600160a01b0390811660c05282811660e052600180546001600160a01b03191691831691821790556040517ffec811ed4e60aebdaf7a79cad8a97196bf56e35362f039705598226d30c9d248905f90a250505050610285565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b038116811461020f575f5ffd5b919050565b5f5f5f5f60808587031215610227575f5ffd5b610230856101f9565b935061023e602086016101f9565b925061024c604086016101f9565b915061025a606086016101f9565b905092959194509250565b5f60208284031215610275575f5ffd5b61027e826101f9565b9392505050565b60805160a05160c05160e051611baf61031a5f395f818161018101526102a101525f818161010b01528181610387015281816104760152818161059d0152818161068c0152818161073f0152818161087701528181610a1901528181610ce70152610d7c01525f818161022301528181610abf01528181610b2b01528181610bf00152610c5c01525f6101d10152611baf5ff3fe60806040526004361061009a575f3560e01c80638da5cb5b116100625780638da5cb5b14610154578063d5438eae14610170578063de523cf3146101a3578063e78cea92146101c0578063f2fde38b146101f3578063ffcbd8d014610212575f5ffd5b80630e72cc061461009e578063380ef3fe146100bf57806346c96aac146100fa57806356d5d4751461012d578063715018a614610140575b5f5ffd5b3480156100a9575f5ffd5b506100bd6100b8366004611843565b610245565b005b3480156100ca575f5ffd5b506001546100de906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b348015610105575f5ffd5b506100de7f000000000000000000000000000000000000000000000000000000000000000081565b6100bd61013b36600461185e565b610296565b34801561014b575f5ffd5b506100bd610e10565b34801561015f575f5ffd5b505f546001600160a01b03166100de565b34801561017b575f5ffd5b506100de7f000000000000000000000000000000000000000000000000000000000000000081565b3480156101ae575f5ffd5b506001546001600160a01b03166100de565b3480156101cb575f5ffd5b506100de7f000000000000000000000000000000000000000000000000000000000000000081565b3480156101fe575f5ffd5b506100bd61020d366004611843565b610e23565b34801561021d575f5ffd5b506100de7f000000000000000000000000000000000000000000000000000000000000000081565b61024d610e65565b600180546001600160a01b0319166001600160a01b0383169081179091556040517ffec811ed4e60aebdaf7a79cad8a97196bf56e35362f039705598226d30c9d248905f90a250565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146102df5760405163079ddc6d60e31b815260040160405180910390fd5b8363ffffffff16600a146103065760405163028ab64560e41b815260040160405180910390fd5b30836001600160a01b03161461032f57604051634dd0641d60e11b815260040160405180910390fd5b5f61033a8383610e91565b905060048103610552575f61034f8484610ec2565b90505f5f5f61035e8787610ef2565b60405163c4f0816560e01b81526001600160a01b03888116600483015293965091945092505f917f0000000000000000000000000000000000000000000000000000000000000000169063c4f0816590602401602060405180830381865afa1580156103cc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f091906118eb565b60405163150fe20b60e11b81526004810186905260248101859052604481018490529091506001600160a01b03821690632a1fc416906064015f604051808303815f87803b158015610440575f5ffd5b505af1158015610452573d5f5f3e3d5ffd5b5050604051631918e7f760e11b81526001600160a01b0388811660048301525f93507f0000000000000000000000000000000000000000000000000000000000000000169150633231cfee90602401602060405180830381865afa1580156104bc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104e091906118eb565b60405163150fe20b60e11b81526004810187905260248101869052604481018590529091506001600160a01b03821690632a1fc416906064015b5f604051808303815f87803b158015610531575f5ffd5b505af1158015610543573d5f5f3e3d5ffd5b50505050505050505050610dc6565b60058103610734575f6105658484610ec2565b90505f5f5f6105748787610ef2565b60405163c4f0816560e01b81526001600160a01b03888116600483015293965091945092505f917f0000000000000000000000000000000000000000000000000000000000000000169063c4f0816590602401602060405180830381865afa1580156105e2573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061060691906118eb565b6040516303912b3160e51b81526004810186905260248101859052604481018490529091506001600160a01b038216906372256620906064015f604051808303815f87803b158015610656575f5ffd5b505af1158015610668573d5f5f3e3d5ffd5b5050604051631918e7f760e11b81526001600160a01b0388811660048301525f93507f0000000000000000000000000000000000000000000000000000000000000000169150633231cfee90602401602060405180830381865afa1580156106d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f691906118eb565b6040516303912b3160e51b81526004810187905260248101869052604481018590529091506001600160a01b0382169063722566209060640161051a565b6002810361086c575f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633231cfee6107768686610ec2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156107b8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107dc91906118eb565b90505f6107e98585610ffc565b90505f6107f68686611023565b90505f610803878761107d565b60405163a44d113f60e01b81529091506001600160a01b0385169063a44d113f9061083690869086908690600401611906565b5f604051808303815f87803b15801561084d575f5ffd5b505af115801561085f573d5f5f3e3d5ffd5b5050505050505050610dc6565b600381036108ae575f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c4f081656107768686610ec2565b60068103610a83575f5f5f5f5f5f6108c68989611220565b604051630b4c774160e11b81526001600160a01b038085166004830152808416602483015262ffffff83166044830152969c50949a50929850909650945092505f91881690631698ee8290606401602060405180830381865afa15801561092f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061095391906118eb565b90506001600160a01b0381166109e25760405163a167129560e01b81526001600160a01b038581166004830152848116602483015262ffffff8416604483015288169063a1671295906064016020604051808303815f875af11580156109bb573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109df91906118eb565b90505b60405163ba9225f760e01b81526001600160a01b0388811660048301528281166024830152878116604483015286811660648301527f0000000000000000000000000000000000000000000000000000000000000000169063ba9225f7906084016020604051808303815f875af1158015610a5f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085f91906118eb565b80610bb1575f610a938484610ec2565b90505f610aa0858561147b565b6040516340c10f1960e01b8152306004820152602481018290529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906340c10f19906044015f604051808303815f87803b158015610b08575f5ffd5b505af1158015610b1a573d5f5f3e3d5ffd5b50610b549250506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690508383611497565b604051633c6b16ab60e01b8152600481018290526001600160a01b03831690633c6b16ab906024015b5f604051808303815f87803b158015610b94575f5ffd5b505af1158015610ba6573d5f5f3e3d5ffd5b505050505050610dc6565b60018103610cb2575f610bc48484610ec2565b90505f610bd1858561147b565b6040516340c10f1960e01b8152306004820152602481018290529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906340c10f19906044015f604051808303815f87803b158015610c39575f5ffd5b505af1158015610c4b573d5f5f3e3d5ffd5b50610c859250506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690508383611497565b604051633737063760e21b8152600481018290526001600160a01b0383169063dcdc18dc90602401610b7d565b60078103610d47575f610cc58484610ec2565b60405163992a793360e01b81526001600160a01b0380831660048301529192507f00000000000000000000000000000000000000000000000000000000000000009091169063992a7933906024015b5f604051808303815f87803b158015610d2b575f5ffd5b505af1158015610d3d573d5f5f3e3d5ffd5b5050505050610dc6565b60088103610dad575f610d5a8484610ec2565b604051639f06247b60e01b81526001600160a01b0380831660048301529192507f000000000000000000000000000000000000000000000000000000000000000090911690639f06247b90602401610d14565b6040516312f269e560e01b815260040160405180910390fd5b838563ffffffff167fecdc36fa3681f5d9c559dcbc399417db6f0ac0d81a78529685a1150265971d55348686604051610e019392919061196b565b60405180910390a35050505050565b610e18610e65565b610e215f611524565b565b610e2b610e65565b6001600160a01b038116610e5957604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b610e6281611524565b50565b5f546001600160a01b03163314610e215760405163118cdaa760e01b8152336004820152602401610e50565b5f828183610ea08260016119b4565b92610ead939291906119c7565b610eb6916119ee565b60f81c90505b92915050565b5f82600183610ed28260146119b4565b92610edf939291906119c7565b610ee891611a26565b60601c9392505050565b5f80808484610f03600160146119b4565b90610f10600160146119b4565b610f1b9060206119b4565b92610f28939291906119c7565b610f3191611a66565b8585610f3f600160146119b4565b610f4a9060206119b4565b90610f57600160146119b4565b610f629060206119b4565b610f6d9060206119b4565b92610f7a939291906119c7565b610f8391611a66565b8686610f91600160146119b4565b610f9c9060206119b4565b610fa79060206119b4565b90610fb4600160146119b4565b610fbf9060206119b4565b610fca9060206119b4565b610fd59060056119b4565b92610fe2939291906119c7565b610feb91611a83565b919450925060d81c90509250925092565b5f828261100b600160146119b4565b90611018600160146119b4565b610ed29060146119b4565b5f8282611032600160146119b4565b61103d9060146119b4565b9061104a600160146119b4565b6110559060146119b4565b6110609060206119b4565b9261106d939291906119c7565b61107691611a66565b9392505050565b60605f838361108e600160146119b4565b6110999060146119b4565b6110a49060206119b4565b906110b1600160146119b4565b6110bc9060146119b4565b6110c79060206119b4565b6110d29060016119b4565b926110df939291906119c7565b6110e8916119ee565b60f81c90508067ffffffffffffffff81111561110657611106611ab9565b60405190808252806020026020018201604052801561112f578160200160208202803683370190505b5091505f5b81811015611218578484611149836020611acd565b611155600160146119b4565b6111609060146119b4565b61116b9060206119b4565b6111769060016119b4565b61118091906119b4565b9061118c8460016119b4565b611197906020611acd565b6111a3600160146119b4565b6111ae9060146119b4565b6111b99060206119b4565b6111c49060016119b4565b6111ce91906119b4565b926111db939291906119c7565b6111e491611a66565b5f1c8382815181106111f8576111f8611ae4565b6001600160a01b0390921660209283029190910190910152600101611134565b505092915050565b5f8080808080876001886112358260146119b4565b92611242939291906119c7565b61124b91611a26565b60601c888861125c600160146119b4565b90611269600160146119b4565b6112749060146119b4565b92611281939291906119c7565b61128a91611a26565b60601c898961129b600160146119b4565b6112a69060146119b4565b906112b3600160146119b4565b6112be9060146119b4565b6112c99060146119b4565b926112d6939291906119c7565b6112df91611a26565b60601c8a8a6112f0600160146119b4565b6112fb9060146119b4565b6113069060146119b4565b90611313600160146119b4565b61131e9060146119b4565b6113299060146119b4565b6113349060146119b4565b92611341939291906119c7565b61134a91611a26565b60601c8b8b61135b600160146119b4565b6113669060146119b4565b6113719060146119b4565b61137c9060146119b4565b90611389600160146119b4565b6113949060146119b4565b61139f9060146119b4565b6113aa9060146119b4565b6113b59060146119b4565b926113c2939291906119c7565b6113cb91611a26565b60601c8c8c6113dc600160146119b4565b6113e79060146119b4565b6113f29060146119b4565b6113fd9060146119b4565b6114089060146119b4565b90611415600160146119b4565b6114209060146119b4565b61142b9060146119b4565b6114369060146119b4565b6114419060146119b4565b61144c9060036119b4565b92611459939291906119c7565b61146291611af8565b60e81c9550955095509550955095509295509295509295565b5f828261148a600160146119b4565b90611055600160146119b4565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa1580156114e4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115089190611b2d565b905061151e848461151985856119b4565b611573565b50505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526115c48482611626565b61151e57604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261161c9085906116c7565b61151e84826116c7565b5f5f5f846001600160a01b0316846040516116419190611b44565b5f604051808303815f865af19150503d805f811461167a576040519150601f19603f3d011682016040523d82523d5f602084013e61167f565b606091505b50915091508180156116a95750805115806116a95750808060200190518101906116a99190611b5a565b80156116be57505f856001600160a01b03163b115b95945050505050565b5f6116db6001600160a01b0384168361172d565b905080515f141580156116ff5750808060200190518101906116fd9190611b5a565b155b1561172857604051635274afe760e01b81526001600160a01b0384166004820152602401610e50565b505050565b606061107683835f845f5f856001600160a01b031684866040516117519190611b44565b5f6040518083038185875af1925050503d805f811461178b576040519150601f19603f3d011682016040523d82523d5f602084013e611790565b606091505b50915091506117a08683836117aa565b9695505050505050565b6060826117bf576117ba82611806565b611076565b81511580156117d657506001600160a01b0384163b155b156117ff57604051639996b31560e01b81526001600160a01b0385166004820152602401610e50565b5080611076565b8051156118165780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114610e62575f5ffd5b5f60208284031215611853575f5ffd5b81356110768161182f565b5f5f5f5f60608587031215611871575f5ffd5b843563ffffffff81168114611884575f5ffd5b935060208501359250604085013567ffffffffffffffff8111156118a6575f5ffd5b8501601f810187136118b6575f5ffd5b803567ffffffffffffffff8111156118cc575f5ffd5b8760208284010111156118dd575f5ffd5b949793965060200194505050565b5f602082840312156118fb575f5ffd5b81516110768161182f565b6001600160a01b0384168152602080820184905260606040830181905283519083018190525f918401906080840190835b8181101561195e5783516001600160a01b0316835260209384019390920191600101611937565b5090979650505050505050565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610ebc57610ebc6119a0565b5f5f858511156119d5575f5ffd5b838611156119e1575f5ffd5b5050820193919092039150565b80356001600160f81b03198116906001841015611a1f576001600160f81b0319600185900360031b81901b82161691505b5092915050565b80356bffffffffffffffffffffffff198116906014841015611a1f576bffffffffffffffffffffffff1960149490940360031b84901b1690921692915050565b80356020831015610ebc575f19602084900360031b1b1692915050565b80356001600160d81b03198116906005841015611a1f576001600160d81b031960059490940360031b84901b1690921692915050565b634e487b7160e01b5f52604160045260245ffd5b8082028115828204841417610ebc57610ebc6119a0565b634e487b7160e01b5f52603260045260245ffd5b80356001600160e81b03198116906003841015611a1f576001600160e81b0319600394850390941b84901b1690921692915050565b5f60208284031215611b3d575f5ffd5b5051919050565b5f82518060208501845e5f920191825250919050565b5f60208284031215611b6a575f5ffd5b81518015158114611076575f5ffdfea264697066735822122022c74df8a207dff4d88f347f71904a6833618c03cdf8e3a1cd785a72f4df767364736f6c634300081b0033000000000000000000000000607eba808ef2685fac3da68ab96de961fa8f3312000000000000000000000000f278761576f45472bdd721eaca19317ce159c0110000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e30000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x60806040526004361061009a575f3560e01c80638da5cb5b116100625780638da5cb5b14610154578063d5438eae14610170578063de523cf3146101a3578063e78cea92146101c0578063f2fde38b146101f3578063ffcbd8d014610212575f5ffd5b80630e72cc061461009e578063380ef3fe146100bf57806346c96aac146100fa57806356d5d4751461012d578063715018a614610140575b5f5ffd5b3480156100a9575f5ffd5b506100bd6100b8366004611843565b610245565b005b3480156100ca575f5ffd5b506001546100de906001600160a01b031681565b6040516001600160a01b03909116815260200160405180910390f35b348015610105575f5ffd5b506100de7f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a112381565b6100bd61013b36600461185e565b610296565b34801561014b575f5ffd5b506100bd610e10565b34801561015f575f5ffd5b505f546001600160a01b03166100de565b34801561017b575f5ffd5b506100de7f0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e381565b3480156101ae575f5ffd5b506001546001600160a01b03166100de565b3480156101cb575f5ffd5b506100de7f000000000000000000000000f278761576f45472bdd721eaca19317ce159c01181565b3480156101fe575f5ffd5b506100bd61020d366004611843565b610e23565b34801561021d575f5ffd5b506100de7f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a8181565b61024d610e65565b600180546001600160a01b0319166001600160a01b0383169081179091556040517ffec811ed4e60aebdaf7a79cad8a97196bf56e35362f039705598226d30c9d248905f90a250565b336001600160a01b037f0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e316146102df5760405163079ddc6d60e31b815260040160405180910390fd5b8363ffffffff16600a146103065760405163028ab64560e41b815260040160405180910390fd5b30836001600160a01b03161461032f57604051634dd0641d60e11b815260040160405180910390fd5b5f61033a8383610e91565b905060048103610552575f61034f8484610ec2565b90505f5f5f61035e8787610ef2565b60405163c4f0816560e01b81526001600160a01b03888116600483015293965091945092505f917f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123169063c4f0816590602401602060405180830381865afa1580156103cc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103f091906118eb565b60405163150fe20b60e11b81526004810186905260248101859052604481018490529091506001600160a01b03821690632a1fc416906064015f604051808303815f87803b158015610440575f5ffd5b505af1158015610452573d5f5f3e3d5ffd5b5050604051631918e7f760e11b81526001600160a01b0388811660048301525f93507f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123169150633231cfee90602401602060405180830381865afa1580156104bc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104e091906118eb565b60405163150fe20b60e11b81526004810187905260248101869052604481018590529091506001600160a01b03821690632a1fc416906064015b5f604051808303815f87803b158015610531575f5ffd5b505af1158015610543573d5f5f3e3d5ffd5b50505050505050505050610dc6565b60058103610734575f6105658484610ec2565b90505f5f5f6105748787610ef2565b60405163c4f0816560e01b81526001600160a01b03888116600483015293965091945092505f917f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123169063c4f0816590602401602060405180830381865afa1580156105e2573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061060691906118eb565b6040516303912b3160e51b81526004810186905260248101859052604481018490529091506001600160a01b038216906372256620906064015f604051808303815f87803b158015610656575f5ffd5b505af1158015610668573d5f5f3e3d5ffd5b5050604051631918e7f760e11b81526001600160a01b0388811660048301525f93507f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123169150633231cfee90602401602060405180830381865afa1580156106d2573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106f691906118eb565b6040516303912b3160e51b81526004810187905260248101869052604481018590529091506001600160a01b0382169063722566209060640161051a565b6002810361086c575f7f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a11236001600160a01b0316633231cfee6107768686610ec2565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156107b8573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906107dc91906118eb565b90505f6107e98585610ffc565b90505f6107f68686611023565b90505f610803878761107d565b60405163a44d113f60e01b81529091506001600160a01b0385169063a44d113f9061083690869086908690600401611906565b5f604051808303815f87803b15801561084d575f5ffd5b505af115801561085f573d5f5f3e3d5ffd5b5050505050505050610dc6565b600381036108ae575f7f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a11236001600160a01b031663c4f081656107768686610ec2565b60068103610a83575f5f5f5f5f5f6108c68989611220565b604051630b4c774160e11b81526001600160a01b038085166004830152808416602483015262ffffff83166044830152969c50949a50929850909650945092505f91881690631698ee8290606401602060405180830381865afa15801561092f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061095391906118eb565b90506001600160a01b0381166109e25760405163a167129560e01b81526001600160a01b038581166004830152848116602483015262ffffff8416604483015288169063a1671295906064016020604051808303815f875af11580156109bb573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906109df91906118eb565b90505b60405163ba9225f760e01b81526001600160a01b0388811660048301528281166024830152878116604483015286811660648301527f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123169063ba9225f7906084016020604051808303815f875af1158015610a5f573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061085f91906118eb565b80610bb1575f610a938484610ec2565b90505f610aa0858561147b565b6040516340c10f1960e01b8152306004820152602481018290529091507f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a816001600160a01b0316906340c10f19906044015f604051808303815f87803b158015610b08575f5ffd5b505af1158015610b1a573d5f5f3e3d5ffd5b50610b549250506001600160a01b037f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a811690508383611497565b604051633c6b16ab60e01b8152600481018290526001600160a01b03831690633c6b16ab906024015b5f604051808303815f87803b158015610b94575f5ffd5b505af1158015610ba6573d5f5f3e3d5ffd5b505050505050610dc6565b60018103610cb2575f610bc48484610ec2565b90505f610bd1858561147b565b6040516340c10f1960e01b8152306004820152602481018290529091507f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a816001600160a01b0316906340c10f19906044015f604051808303815f87803b158015610c39575f5ffd5b505af1158015610c4b573d5f5f3e3d5ffd5b50610c859250506001600160a01b037f0000000000000000000000007f9adfbd38b669f03d1d11000bc76b9aaea28a811690508383611497565b604051633737063760e21b8152600481018290526001600160a01b0383169063dcdc18dc90602401610b7d565b60078103610d47575f610cc58484610ec2565b60405163992a793360e01b81526001600160a01b0380831660048301529192507f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a11239091169063992a7933906024015b5f604051808303815f87803b158015610d2b575f5ffd5b505af1158015610d3d573d5f5f3e3d5ffd5b5050505050610dc6565b60088103610dad575f610d5a8484610ec2565b604051639f06247b60e01b81526001600160a01b0380831660048301529192507f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a112390911690639f06247b90602401610d14565b6040516312f269e560e01b815260040160405180910390fd5b838563ffffffff167fecdc36fa3681f5d9c559dcbc399417db6f0ac0d81a78529685a1150265971d55348686604051610e019392919061196b565b60405180910390a35050505050565b610e18610e65565b610e215f611524565b565b610e2b610e65565b6001600160a01b038116610e5957604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b610e6281611524565b50565b5f546001600160a01b03163314610e215760405163118cdaa760e01b8152336004820152602401610e50565b5f828183610ea08260016119b4565b92610ead939291906119c7565b610eb6916119ee565b60f81c90505b92915050565b5f82600183610ed28260146119b4565b92610edf939291906119c7565b610ee891611a26565b60601c9392505050565b5f80808484610f03600160146119b4565b90610f10600160146119b4565b610f1b9060206119b4565b92610f28939291906119c7565b610f3191611a66565b8585610f3f600160146119b4565b610f4a9060206119b4565b90610f57600160146119b4565b610f629060206119b4565b610f6d9060206119b4565b92610f7a939291906119c7565b610f8391611a66565b8686610f91600160146119b4565b610f9c9060206119b4565b610fa79060206119b4565b90610fb4600160146119b4565b610fbf9060206119b4565b610fca9060206119b4565b610fd59060056119b4565b92610fe2939291906119c7565b610feb91611a83565b919450925060d81c90509250925092565b5f828261100b600160146119b4565b90611018600160146119b4565b610ed29060146119b4565b5f8282611032600160146119b4565b61103d9060146119b4565b9061104a600160146119b4565b6110559060146119b4565b6110609060206119b4565b9261106d939291906119c7565b61107691611a66565b9392505050565b60605f838361108e600160146119b4565b6110999060146119b4565b6110a49060206119b4565b906110b1600160146119b4565b6110bc9060146119b4565b6110c79060206119b4565b6110d29060016119b4565b926110df939291906119c7565b6110e8916119ee565b60f81c90508067ffffffffffffffff81111561110657611106611ab9565b60405190808252806020026020018201604052801561112f578160200160208202803683370190505b5091505f5b81811015611218578484611149836020611acd565b611155600160146119b4565b6111609060146119b4565b61116b9060206119b4565b6111769060016119b4565b61118091906119b4565b9061118c8460016119b4565b611197906020611acd565b6111a3600160146119b4565b6111ae9060146119b4565b6111b99060206119b4565b6111c49060016119b4565b6111ce91906119b4565b926111db939291906119c7565b6111e491611a66565b5f1c8382815181106111f8576111f8611ae4565b6001600160a01b0390921660209283029190910190910152600101611134565b505092915050565b5f8080808080876001886112358260146119b4565b92611242939291906119c7565b61124b91611a26565b60601c888861125c600160146119b4565b90611269600160146119b4565b6112749060146119b4565b92611281939291906119c7565b61128a91611a26565b60601c898961129b600160146119b4565b6112a69060146119b4565b906112b3600160146119b4565b6112be9060146119b4565b6112c99060146119b4565b926112d6939291906119c7565b6112df91611a26565b60601c8a8a6112f0600160146119b4565b6112fb9060146119b4565b6113069060146119b4565b90611313600160146119b4565b61131e9060146119b4565b6113299060146119b4565b6113349060146119b4565b92611341939291906119c7565b61134a91611a26565b60601c8b8b61135b600160146119b4565b6113669060146119b4565b6113719060146119b4565b61137c9060146119b4565b90611389600160146119b4565b6113949060146119b4565b61139f9060146119b4565b6113aa9060146119b4565b6113b59060146119b4565b926113c2939291906119c7565b6113cb91611a26565b60601c8c8c6113dc600160146119b4565b6113e79060146119b4565b6113f29060146119b4565b6113fd9060146119b4565b6114089060146119b4565b90611415600160146119b4565b6114209060146119b4565b61142b9060146119b4565b6114369060146119b4565b6114419060146119b4565b61144c9060036119b4565b92611459939291906119c7565b61146291611af8565b60e81c9550955095509550955095509295509295509295565b5f828261148a600160146119b4565b90611055600160146119b4565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa1580156114e4573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906115089190611b2d565b905061151e848461151985856119b4565b611573565b50505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b1790526115c48482611626565b61151e57604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b17905261161c9085906116c7565b61151e84826116c7565b5f5f5f846001600160a01b0316846040516116419190611b44565b5f604051808303815f865af19150503d805f811461167a576040519150601f19603f3d011682016040523d82523d5f602084013e61167f565b606091505b50915091508180156116a95750805115806116a95750808060200190518101906116a99190611b5a565b80156116be57505f856001600160a01b03163b115b95945050505050565b5f6116db6001600160a01b0384168361172d565b905080515f141580156116ff5750808060200190518101906116fd9190611b5a565b155b1561172857604051635274afe760e01b81526001600160a01b0384166004820152602401610e50565b505050565b606061107683835f845f5f856001600160a01b031684866040516117519190611b44565b5f6040518083038185875af1925050503d805f811461178b576040519150601f19603f3d011682016040523d82523d5f602084013e611790565b606091505b50915091506117a08683836117aa565b9695505050505050565b6060826117bf576117ba82611806565b611076565b81511580156117d657506001600160a01b0384163b155b156117ff57604051639996b31560e01b81526001600160a01b0385166004820152602401610e50565b5080611076565b8051156118165780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114610e62575f5ffd5b5f60208284031215611853575f5ffd5b81356110768161182f565b5f5f5f5f60608587031215611871575f5ffd5b843563ffffffff81168114611884575f5ffd5b935060208501359250604085013567ffffffffffffffff8111156118a6575f5ffd5b8501601f810187136118b6575f5ffd5b803567ffffffffffffffff8111156118cc575f5ffd5b8760208284010111156118dd575f5ffd5b949793965060200194505050565b5f602082840312156118fb575f5ffd5b81516110768161182f565b6001600160a01b0384168152602080820184905260606040830181905283519083018190525f918401906080840190835b8181101561195e5783516001600160a01b0316835260209384019390920191600101611937565b5090979650505050505050565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610ebc57610ebc6119a0565b5f5f858511156119d5575f5ffd5b838611156119e1575f5ffd5b5050820193919092039150565b80356001600160f81b03198116906001841015611a1f576001600160f81b0319600185900360031b81901b82161691505b5092915050565b80356bffffffffffffffffffffffff198116906014841015611a1f576bffffffffffffffffffffffff1960149490940360031b84901b1690921692915050565b80356020831015610ebc575f19602084900360031b1b1692915050565b80356001600160d81b03198116906005841015611a1f576001600160d81b031960059490940360031b84901b1690921692915050565b634e487b7160e01b5f52604160045260245ffd5b8082028115828204841417610ebc57610ebc6119a0565b634e487b7160e01b5f52603260045260245ffd5b80356001600160e81b03198116906003841015611a1f576001600160e81b0319600394850390941b84901b1690921692915050565b5f60208284031215611b3d575f5ffd5b5051919050565b5f82518060208501845e5f920191825250919050565b5f60208284031215611b6a575f5ffd5b81518015158114611076575f5ffdfea264697066735822122022c74df8a207dff4d88f347f71904a6833618c03cdf8e3a1cd785a72f4df767364736f6c634300081b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000607eba808ef2685fac3da68ab96de961fa8f3312000000000000000000000000f278761576f45472bdd721eaca19317ce159c0110000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e30000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _owner (address): 0x607EbA808EF2685fAc3da68aB96De961fa8F3312
Arg [1] : _bridge (address): 0xF278761576f45472bdD721EACA19317cE159c011
Arg [2] : _mailbox (address): 0x2f9DB5616fa3fAd1aB06cB2C906830BA63d135e3
Arg [3] : _ism (address): 0x0000000000000000000000000000000000000000
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000607eba808ef2685fac3da68ab96de961fa8f3312
Arg [1] : 000000000000000000000000f278761576f45472bdd721eaca19317ce159c011
Arg [2] : 0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e3
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in FRAX
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
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.