Overview
ETH Balance
0 ETH
Eth Value
$0.00Token Holdings
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
21555329 | 25 hrs ago | 0.00032099 ETH | ||||
21555329 | 25 hrs ago | 0.00032099 ETH | ||||
21487304 | 10 days ago | 0.00032099 ETH | ||||
21487304 | 10 days ago | 0.00032099 ETH | ||||
21443291 | 16 days ago | 0.00159552 ETH | ||||
21443291 | 16 days ago | 0.00159552 ETH | ||||
21409655 | 21 days ago | 0.00062133 ETH | ||||
21409655 | 21 days ago | 0.00062133 ETH | ||||
21387477 | 24 days ago | 0.00175476 ETH | ||||
21387477 | 24 days ago | 0.00175476 ETH | ||||
21369709 | 26 days ago | 0.00146782 ETH | ||||
21369709 | 26 days ago | 0.00146782 ETH | ||||
21337094 | 31 days ago | 0.00131123 ETH | ||||
21337094 | 31 days ago | 0.00131123 ETH | ||||
21334999 | 31 days ago | 0.00131123 ETH | ||||
21334999 | 31 days ago | 0.00131123 ETH | ||||
21333939 | 31 days ago | 0.00131123 ETH | ||||
21333939 | 31 days ago | 0.00131123 ETH | ||||
21285998 | 38 days ago | 0.00046342 ETH | ||||
21285998 | 38 days ago | 0.00046342 ETH | ||||
21261706 | 42 days ago | 0.0004964 ETH | ||||
21261706 | 42 days ago | 0.0004964 ETH | ||||
21203236 | 50 days ago | 0.00066483 ETH | ||||
21203236 | 50 days ago | 0.00066483 ETH | ||||
21155103 | 56 days ago | 0.0004814 ETH |
Loading...
Loading
Contract Name:
Vault
Compiler Version
v0.8.13+commit.abaa5c0e
Optimization Enabled:
Yes with 999999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.8.13; import "lib/solmate/src/utils/SafeTransferLib.sol"; import "../common/Ownable.sol"; import {Gauge} from "../common/Gauge.sol"; import {IConnector, IHub} from "./ConnectorPlug.sol"; import {RescueFundsLib} from "../libraries/RescueFundsLib.sol"; // @todo: separate our connecter plugs contract Vault is Gauge, IHub, Ownable(msg.sender) { using SafeTransferLib for ERC20; ERC20 public immutable token__; struct UpdateLimitParams { bool isLock; address connector; uint256 maxLimit; uint256 ratePerSecond; } // connector => receiver => pendingUnlock mapping(address => mapping(address => uint256)) public pendingUnlocks; // connector => amount mapping(address => uint256) public connectorPendingUnlocks; // connector => lockLimitParams mapping(address => LimitParams) _lockLimitParams; // connector => unlockLimitParams mapping(address => LimitParams) _unlockLimitParams; error ConnectorUnavailable(); error ZeroAmount(); event LimitParamsUpdated(UpdateLimitParams[] updates); event TokensDeposited( address connector, address depositor, address receiver, uint256 depositAmount ); event PendingTokensTransferred( address connector, address receiver, uint256 unlockedAmount, uint256 pendingAmount ); event TokensPending( address connector, address receiver, uint256 pendingAmount, uint256 totalPendingAmount ); event TokensUnlocked( address connector, address receiver, uint256 unlockedAmount ); constructor(address token_) { token__ = ERC20(token_); } function updateLimitParams( UpdateLimitParams[] calldata updates_ ) external onlyOwner { for (uint256 i; i < updates_.length; i++) { if (updates_[i].isLock) { _consumePartLimit(0, _lockLimitParams[updates_[i].connector]); // to keep current limit in sync _lockLimitParams[updates_[i].connector].maxLimit = updates_[i] .maxLimit; _lockLimitParams[updates_[i].connector] .ratePerSecond = updates_[i].ratePerSecond; } else { _consumePartLimit(0, _unlockLimitParams[updates_[i].connector]); // to keep current limit in sync _unlockLimitParams[updates_[i].connector].maxLimit = updates_[i] .maxLimit; _unlockLimitParams[updates_[i].connector] .ratePerSecond = updates_[i].ratePerSecond; } } emit LimitParamsUpdated(updates_); } function depositToAppChain( address receiver_, uint256 amount_, uint256 msgGasLimit_, address connector_ ) external payable { if (amount_ == 0) revert ZeroAmount(); if (_lockLimitParams[connector_].maxLimit == 0) revert ConnectorUnavailable(); _consumeFullLimit(amount_, _lockLimitParams[connector_]); // reverts on limit hit token__.safeTransferFrom(msg.sender, address(this), amount_); IConnector(connector_).outbound{value: msg.value}( msgGasLimit_, abi.encode(receiver_, amount_) ); emit TokensDeposited(connector_, msg.sender, receiver_, amount_); } function unlockPendingFor(address receiver_, address connector_) external { if (_unlockLimitParams[connector_].maxLimit == 0) revert ConnectorUnavailable(); uint256 pendingUnlock = pendingUnlocks[connector_][receiver_]; (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( pendingUnlock, _unlockLimitParams[connector_] ); pendingUnlocks[connector_][receiver_] = pendingAmount; connectorPendingUnlocks[connector_] -= consumedAmount; token__.safeTransfer(receiver_, consumedAmount); emit PendingTokensTransferred( connector_, receiver_, consumedAmount, pendingAmount ); } // receive inbound assuming connector called function receiveInbound(bytes memory payload_) external override { if (_unlockLimitParams[msg.sender].maxLimit == 0) revert ConnectorUnavailable(); (address receiver, uint256 unlockAmount) = abi.decode( payload_, (address, uint256) ); (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( unlockAmount, _unlockLimitParams[msg.sender] ); if (pendingAmount > 0) { // add instead of overwrite to handle case where already pending amount is left pendingUnlocks[msg.sender][receiver] += pendingAmount; connectorPendingUnlocks[msg.sender] += pendingAmount; emit TokensPending( msg.sender, receiver, pendingAmount, pendingUnlocks[msg.sender][receiver] ); } token__.safeTransfer(receiver, consumedAmount); emit TokensUnlocked(msg.sender, receiver, consumedAmount); } function getMinFees( address connector_, uint256 msgGasLimit_ ) external view returns (uint256 totalFees) { return IConnector(connector_).getMinFees(msgGasLimit_); } function getCurrentLockLimit( address connector_ ) external view returns (uint256) { return _getCurrentLimit(_lockLimitParams[connector_]); } function getCurrentUnlockLimit( address connector_ ) external view returns (uint256) { return _getCurrentLimit(_unlockLimitParams[connector_]); } function getLockLimitParams( address connector_ ) external view returns (LimitParams memory) { return _lockLimitParams[connector_]; } function getUnlockLimitParams( address connector_ ) external view returns (LimitParams memory) { return _unlockLimitParams[connector_]; } /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyOwner { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; import "./Ownable.sol"; /** * @title AccessControl * @dev This abstract contract implements access control mechanism based on roles. * Each role can have one or more addresses associated with it, which are granted * permission to execute functions with the onlyRole modifier. */ abstract contract AccessControl is Ownable { /** * @dev A mapping of roles to a mapping of addresses to boolean values indicating whether or not they have the role. */ mapping(bytes32 => mapping(address => bool)) private _permits; /** * @dev Emitted when a role is granted to an address. */ event RoleGranted(bytes32 indexed role, address indexed grantee); /** * @dev Emitted when a role is revoked from an address. */ event RoleRevoked(bytes32 indexed role, address indexed revokee); /** * @dev Error message thrown when an address does not have permission to execute a function with onlyRole modifier. */ error NoPermit(bytes32 role); /** * @dev Constructor that sets the owner of the contract. */ constructor(address owner_) Ownable(owner_) {} /** * @dev Modifier that restricts access to addresses having roles * Throws an error if the caller do not have permit */ modifier onlyRole(bytes32 role) { if (!_permits[role][msg.sender]) revert NoPermit(role); _; } /** * @dev Checks and reverts if an address do not have a specific role. * @param role_ The role to check. * @param address_ The address to check. */ function _checkRole(bytes32 role_, address address_) internal virtual { if (!_hasRole(role_, address_)) revert NoPermit(role_); } /** * @dev Grants a role to a given address. * @param role_ The role to grant. * @param grantee_ The address to grant the role to. * Emits a RoleGranted event. * Can only be called by the owner of the contract. */ function grantRole( bytes32 role_, address grantee_ ) external virtual onlyOwner { _grantRole(role_, grantee_); } /** * @dev Revokes a role from a given address. * @param role_ The role to revoke. * @param revokee_ The address to revoke the role from. * Emits a RoleRevoked event. * Can only be called by the owner of the contract. */ function revokeRole( bytes32 role_, address revokee_ ) external virtual onlyOwner { _revokeRole(role_, revokee_); } /** * @dev Internal function to grant a role to a given address. * @param role_ The role to grant. * @param grantee_ The address to grant the role to. * Emits a RoleGranted event. */ function _grantRole(bytes32 role_, address grantee_) internal { _permits[role_][grantee_] = true; emit RoleGranted(role_, grantee_); } /** * @dev Internal function to revoke a role from a given address. * @param role_ The role to revoke. * @param revokee_ The address to revoke the role from. * Emits a RoleRevoked event. */ function _revokeRole(bytes32 role_, address revokee_) internal { _permits[role_][revokee_] = false; emit RoleRevoked(role_, revokee_); } /** * @dev Checks whether an address has a specific role. * @param role_ The role to check. * @param address_ The address to check. * @return A boolean value indicating whether or not the address has the role. */ function hasRole( bytes32 role_, address address_ ) external view returns (bool) { return _hasRole(role_, address_); } function _hasRole( bytes32 role_, address address_ ) internal view returns (bool) { return _permits[role_][address_]; } }
pragma solidity 0.8.13; abstract contract Gauge { struct LimitParams { uint256 lastUpdateTimestamp; uint256 ratePerSecond; uint256 maxLimit; uint256 lastUpdateLimit; } error AmountOutsideLimit(); function _getCurrentLimit( LimitParams storage _params ) internal view returns (uint256 _limit) { uint256 timeElapsed = block.timestamp - _params.lastUpdateTimestamp; uint256 limitIncrease = timeElapsed * _params.ratePerSecond; if (limitIncrease + _params.lastUpdateLimit > _params.maxLimit) { _limit = _params.maxLimit; } else { _limit = limitIncrease + _params.lastUpdateLimit; } } function _consumePartLimit( uint256 amount_, LimitParams storage _params ) internal returns (uint256 consumedAmount, uint256 pendingAmount) { uint256 currentLimit = _getCurrentLimit(_params); _params.lastUpdateTimestamp = block.timestamp; if (currentLimit >= amount_) { _params.lastUpdateLimit = currentLimit - amount_; consumedAmount = amount_; pendingAmount = 0; } else { _params.lastUpdateLimit = 0; consumedAmount = currentLimit; pendingAmount = amount_ - currentLimit; } } function _consumeFullLimit( uint256 amount_, LimitParams storage _params ) internal { uint256 currentLimit = _getCurrentLimit(_params); if (currentLimit >= amount_) { _params.lastUpdateTimestamp = block.timestamp; _params.lastUpdateLimit = currentLimit - amount_; } else { revert AmountOutsideLimit(); } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; /** * @title Ownable * @dev The Ownable contract provides a simple way to manage ownership of a contract * and allows for ownership to be transferred to a nominated address. */ abstract contract Ownable { address private _owner; address private _nominee; event OwnerNominated(address indexed nominee); event OwnerClaimed(address indexed claimer); error OnlyOwner(); error OnlyNominee(); /** * @dev Sets the contract's owner to the address that is passed to the constructor. */ constructor(address owner_) { _claimOwner(owner_); } /** * @dev Modifier that restricts access to only the contract's owner. * Throws an error if the caller is not the owner. */ modifier onlyOwner() { if (msg.sender != _owner) revert OnlyOwner(); _; } /** * @dev Returns the current owner of the contract. */ function owner() external view returns (address) { return _owner; } /** * @dev Returns the current nominee for ownership of the contract. */ function nominee() external view returns (address) { return _nominee; } /** * @dev Allows the current owner to nominate a new owner for the contract. * Throws an error if the caller is not the owner. * Emits an `OwnerNominated` event with the address of the nominee. */ function nominateOwner(address nominee_) external { if (msg.sender != _owner) revert OnlyOwner(); _nominee = nominee_; emit OwnerNominated(_nominee); } /** * @dev Allows the nominated owner to claim ownership of the contract. * Throws an error if the caller is not the nominee. * Sets the nominated owner as the new owner of the contract. * Emits an `OwnerClaimed` event with the address of the new owner. */ function claimOwner() external { if (msg.sender != _nominee) revert OnlyNominee(); _claimOwner(msg.sender); } /** * @dev Internal function that sets the owner of the contract to the specified address * and sets the nominee to address(0). */ function _claimOwner(address claimer_) internal { _owner = claimer_; _nominee = address(0); emit OwnerClaimed(claimer_); } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; /** * @title IPlug * @notice Interface for a plug contract that executes the message received from a source chain. */ interface IPlug { /** * @dev this should be only executable by socket * @notice executes the message received from source chain * @notice It is expected to have original sender checks in the destination plugs using payload * @param srcChainSlug_ chain slug of source * @param payload_ the data which is needed by plug at inbound call on remote */ function inbound( uint32 srcChainSlug_, bytes calldata payload_ ) external payable; }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; /** * @title ISocket * @notice An interface for a cross-chain communication contract * @dev This interface provides methods for transmitting and executing messages between chains, * connecting a plug to a remote chain and setting up switchboards for the message transmission * This interface also emits events for important operations such as message transmission, execution status, * and plug connection */ interface ISocket { /** * @notice A struct containing fees required for message transmission and execution * @param transmissionFees fees needed for transmission * @param switchboardFees fees needed by switchboard * @param executionFee fees needed for execution */ struct Fees { uint128 transmissionFees; uint128 executionFee; uint128 switchboardFees; } /** * @title MessageDetails * @dev This struct defines the details of a message to be executed in a Decapacitor contract. */ struct MessageDetails { // A unique identifier for the message. bytes32 msgId; // The fee to be paid for executing the message. uint256 executionFee; // The maximum amount of gas that can be used to execute the message. uint256 minMsgGasLimit; // The extra params which provides msg value and additional info needed for message exec bytes32 executionParams; // The payload data to be executed in the message. bytes payload; } /** * @title ExecutionDetails * @dev This struct defines the execution details */ struct ExecutionDetails { // packet id bytes32 packetId; // proposal count uint256 proposalCount; // gas limit needed to execute inbound uint256 executionGasLimit; // proof data required by the Decapacitor contract to verify the message's authenticity bytes decapacitorProof; // signature of executor bytes signature; } /** * @notice emits the message details when a new message arrives at outbound * @param localChainSlug local chain slug * @param localPlug local plug address * @param dstChainSlug remote chain slug * @param dstPlug remote plug address * @param msgId message id packed with remoteChainSlug and nonce * @param minMsgGasLimit gas limit needed to execute the inbound at remote * @param payload the data which will be used by inbound at remote */ event MessageOutbound( uint32 localChainSlug, address localPlug, uint32 dstChainSlug, address dstPlug, bytes32 msgId, uint256 minMsgGasLimit, bytes32 executionParams, bytes32 transmissionParams, bytes payload, Fees fees ); /** * @notice emits the status of message after inbound call * @param msgId msg id which is executed */ event ExecutionSuccess(bytes32 msgId); /** * @notice emits the config set by a plug for a remoteChainSlug * @param plug address of plug on current chain * @param siblingChainSlug sibling chain slug * @param siblingPlug address of plug on sibling chain * @param inboundSwitchboard inbound switchboard (select from registered options) * @param outboundSwitchboard outbound switchboard (select from registered options) * @param capacitor capacitor selected based on outbound switchboard * @param decapacitor decapacitor selected based on inbound switchboard */ event PlugConnected( address plug, uint32 siblingChainSlug, address siblingPlug, address inboundSwitchboard, address outboundSwitchboard, address capacitor, address decapacitor ); /** * @notice registers a message * @dev Packs the message and includes it in a packet with capacitor * @param remoteChainSlug_ the remote chain slug * @param minMsgGasLimit_ the gas limit needed to execute the payload on remote * @param payload_ the data which is needed by plug at inbound call on remote */ function outbound( uint32 remoteChainSlug_, uint256 minMsgGasLimit_, bytes32 executionParams_, bytes32 transmissionParams_, bytes memory payload_ ) external payable returns (bytes32 msgId); /** * @notice executes a message * @param executionDetails_ the packet details, proof and signature needed for message execution * @param messageDetails_ the message details */ function execute( ISocket.ExecutionDetails calldata executionDetails_, ISocket.MessageDetails calldata messageDetails_ ) external payable; /** * @notice sets the config specific to the plug * @param siblingChainSlug_ the sibling chain slug * @param siblingPlug_ address of plug present at sibling chain to call inbound * @param inboundSwitchboard_ the address of switchboard to use for receiving messages * @param outboundSwitchboard_ the address of switchboard to use for sending messages */ function connect( uint32 siblingChainSlug_, address siblingPlug_, address inboundSwitchboard_, address outboundSwitchboard_ ) external; /** * @notice Retrieves the minimum fees required for a message with a specified gas limit and destination chain. * @param minMsgGasLimit_ The gas limit of the message. * @param remoteChainSlug_ The slug of the destination chain for the message. * @param plug_ The address of the plug through which the message is sent. * @return totalFees The minimum fees required for the specified message. */ function getMinFees( uint256 minMsgGasLimit_, uint256 payloadSize_, bytes32 executionParams_, bytes32 transmissionParams_, uint32 remoteChainSlug_, address plug_ ) external view returns (uint256 totalFees); /** * @notice returns chain slug * @return chainSlug current chain slug */ function chainSlug() external view returns (uint32 chainSlug); function globalMessageCount() external view returns (uint64); /** * @notice returns the config for given `plugAddress_` and `siblingChainSlug_` * @param siblingChainSlug_ the sibling chain slug * @param plugAddress_ address of plug present at current chain */ function getPlugConfig( address plugAddress_, uint32 siblingChainSlug_ ) external view returns ( address siblingPlug, address inboundSwitchboard__, address outboundSwitchboard__, address capacitor__, address decapacitor__ ); }
// SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity 0.8.13; library ExcessivelySafeCall { uint constant LOW_28_MASK = 0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff; /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeCall( address _target, uint _gas, uint16 _maxCopy, bytes memory _calldata ) internal returns (bool, bytes memory) { // set up for assembly call uint _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := call( _gas, // gas _target, // recipient 0, // ether value add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /// @notice Use when you _really_ really _really_ don't trust the called /// contract. This prevents the called contract from causing reversion of /// the caller in as many ways as we can. /// @dev The main difference between this and a solidity low-level call is /// that we limit the number of bytes that the callee can cause to be /// copied to caller memory. This prevents stupid things like malicious /// contracts returning 10,000,000 bytes causing a local OOG when copying /// to memory. /// @param _target The address to call /// @param _gas The amount of gas to forward to the remote contract /// @param _maxCopy The maximum number of bytes of returndata to copy /// to memory. /// @param _calldata The data to send to the remote contract /// @return success and returndata, as `.call()`. Returndata is capped to /// `_maxCopy` bytes. function excessivelySafeStaticCall( address _target, uint _gas, uint16 _maxCopy, bytes memory _calldata ) internal view returns (bool, bytes memory) { // set up for assembly call uint _toCopy; bool _success; bytes memory _returnData = new bytes(_maxCopy); // dispatch message to recipient // by assembly calling "handle" function // we call via assembly to avoid memcopying a very large returndata // returned by a malicious contract assembly { _success := staticcall( _gas, // gas _target, // recipient add(_calldata, 0x20), // inloc mload(_calldata), // inlen 0, // outloc 0 // outlen ) // limit our copy to 256 bytes _toCopy := returndatasize() if gt(_toCopy, _maxCopy) { _toCopy := _maxCopy } // Store the length of the copied bytes mstore(_returnData, _toCopy) // copy the bytes from returndata[0:_toCopy] returndatacopy(add(_returnData, 0x20), 0, _toCopy) } return (_success, _returnData); } /** * @notice Swaps function selectors in encoded contract calls * @dev Allows reuse of encoded calldata for functions with identical * argument types but different names. It simply swaps out the first 4 bytes * for the new selector. This function modifies memory in place, and should * only be used with caution. * @param _newSelector The new 4-byte selector * @param _buf The encoded contract args */ function swapSelector( bytes4 _newSelector, bytes memory _buf ) internal pure { require(_buf.length >= 4); uint _mask = LOW_28_MASK; assembly { // load the first word of let _word := mload(add(_buf, 0x20)) // mask out the top 4 bytes // /x _word := and(_word, _mask) _word := or(_newSelector, _word) mstore(add(_buf, 0x20), _word) } } }
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.8.13; import "lib/solmate/src/utils/SafeTransferLib.sol"; error ZeroAddress(); /** * @title RescueFundsLib * @dev A library that provides a function to rescue funds from a contract. */ library RescueFundsLib { /** * @dev The address used to identify ETH. */ address public constant ETH_ADDRESS = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); /** * @dev thrown when the given token address don't have any code */ error InvalidTokenAddress(); /** * @dev Rescues funds from a contract. * @param token_ The address of the token contract. * @param rescueTo_ The address of the user. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) internal { if (rescueTo_ == address(0)) revert ZeroAddress(); if (token_ == ETH_ADDRESS) { SafeTransferLib.safeTransferETH(rescueTo_, amount_); } else { if (token_.code.length == 0) revert InvalidTokenAddress(); SafeTransferLib.safeTransfer(ERC20(token_), rescueTo_, amount_); } } }
pragma solidity 0.8.13; import "../common/Ownable.sol"; import {ISocket} from "../interfaces/ISocket.sol"; import {IPlug} from "../interfaces/IPlug.sol"; import {RescueFundsLib} from "../libraries/RescueFundsLib.sol"; interface IHub { function receiveInbound(bytes memory payload_) external; } interface IConnector { function outbound( uint256 msgGasLimit_, bytes memory payload_ ) external payable; function siblingChainSlug() external view returns (uint32); function getMinFees( uint256 msgGasLimit_ ) external view returns (uint256 totalFees); } contract ConnectorPlug is IConnector, IPlug, Ownable { IHub public immutable hub__; ISocket public immutable socket__; uint32 public immutable siblingChainSlug; error NotHub(); error NotSocket(); event ConnectorPlugDisconnected(); constructor( address hub_, address socket_, uint32 siblingChainSlug_ ) Ownable(msg.sender) { hub__ = IHub(hub_); socket__ = ISocket(socket_); siblingChainSlug = siblingChainSlug_; } function outbound( uint256 msgGasLimit_, bytes memory payload_ ) external payable override { if (msg.sender != address(hub__)) revert NotHub(); socket__.outbound{value: msg.value}( siblingChainSlug, msgGasLimit_, bytes32(0), bytes32(0), payload_ ); } function inbound( uint32 /* siblingChainSlug_ */, // cannot be connected for any other slug, immutable variable bytes calldata payload_ ) external payable override { if (msg.sender != address(socket__)) revert NotSocket(); hub__.receiveInbound(payload_); } function getMinFees( uint256 msgGasLimit_ ) external view override returns (uint256 totalFees) { return socket__.getMinFees( msgGasLimit_, 64, bytes32(0), bytes32(0), siblingChainSlug, address(this) ); } function connect( address siblingPlug_, address switchboard_ ) external onlyOwner { socket__.connect( siblingChainSlug, siblingPlug_, switchboard_, switchboard_ ); } function disconnect() external onlyOwner { ( , address inboundSwitchboard, address outboundSwitchboard, , ) = socket__.getPlugConfig(address(this), siblingChainSlug); socket__.connect( siblingChainSlug, address(0), inboundSwitchboard, outboundSwitchboard ); emit ConnectorPlugDisconnected(); } /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyOwner { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } }
pragma solidity 0.8.13; import {IExchangeRate} from "./ExchangeRate.sol"; import {Ownable} from "../common/Ownable.sol"; import {Gauge} from "../common/Gauge.sol"; import {IConnector, IHub} from "./ConnectorPlug.sol"; import {IMintableERC20} from "./IMintableERC20.sol"; import {RescueFundsLib} from "../libraries/RescueFundsLib.sol"; contract Controller is IHub, Gauge, Ownable(msg.sender) { IMintableERC20 public immutable token__; IExchangeRate public exchangeRate__; struct UpdateLimitParams { bool isMint; address connector; uint256 maxLimit; uint256 ratePerSecond; } // connectorPoolId => totalLockedAmount mapping(uint256 => uint256) public poolLockedAmounts; // connector => connectorPoolId mapping(address => uint256) public connectorPoolIds; // connector => mintLimitParams mapping(address => LimitParams) _mintLimitParams; // connector => burnLimitParams mapping(address => LimitParams) _burnLimitParams; // connector => receiver => amount mapping(address => mapping(address => uint256)) public pendingMints; // connector => amount mapping(address => uint256) public connectorPendingMints; uint256 public totalMinted; error ConnectorUnavailable(); error InvalidPoolId(); error ZeroAmount(); event ExchangeRateUpdated(address exchangeRate); event ConnectorPoolIdUpdated(address connector, uint256 poolId); event LimitParamsUpdated(UpdateLimitParams[] updates); event TokensWithdrawn( address connector, address withdrawer, address receiver, uint256 burnAmount ); event PendingTokensMinted( address connector, address receiver, uint256 mintAmount, uint256 pendingAmount ); event TokensPending( address connecter, address receiver, uint256 pendingAmount, uint256 totalPendingAmount ); event TokensMinted(address connecter, address receiver, uint256 mintAmount); constructor(address token_, address exchangeRate_) { token__ = IMintableERC20(token_); exchangeRate__ = IExchangeRate(exchangeRate_); } function updateExchangeRate(address exchangeRate_) external onlyOwner { exchangeRate__ = IExchangeRate(exchangeRate_); emit ExchangeRateUpdated(exchangeRate_); } function updateConnectorPoolId( address[] calldata connectors, uint256[] calldata poolIds ) external onlyOwner { uint256 length = connectors.length; for (uint256 i; i < length; i++) { if (poolIds[i] == 0) revert InvalidPoolId(); connectorPoolIds[connectors[i]] = poolIds[i]; emit ConnectorPoolIdUpdated(connectors[i], poolIds[i]); } } function updateLimitParams( UpdateLimitParams[] calldata updates_ ) external onlyOwner { for (uint256 i; i < updates_.length; i++) { if (updates_[i].isMint) { _consumePartLimit(0, _mintLimitParams[updates_[i].connector]); // to keep current limit in sync _mintLimitParams[updates_[i].connector].maxLimit = updates_[i] .maxLimit; _mintLimitParams[updates_[i].connector] .ratePerSecond = updates_[i].ratePerSecond; } else { _consumePartLimit(0, _burnLimitParams[updates_[i].connector]); // to keep current limit in sync _burnLimitParams[updates_[i].connector].maxLimit = updates_[i] .maxLimit; _burnLimitParams[updates_[i].connector] .ratePerSecond = updates_[i].ratePerSecond; } } emit LimitParamsUpdated(updates_); } // do we throttle burn amount or unlock amount? burn for now function withdrawFromAppChain( address receiver_, uint256 burnAmount_, uint256 msgGasLimit_, address connector_ ) external payable { if (burnAmount_ == 0) revert ZeroAmount(); if (_burnLimitParams[connector_].maxLimit == 0) revert ConnectorUnavailable(); _consumeFullLimit(burnAmount_, _burnLimitParams[connector_]); // reverts on limit hit totalMinted -= burnAmount_; _burn(msg.sender, burnAmount_); uint256 connectorPoolId = connectorPoolIds[connector_]; if (connectorPoolId == 0) revert InvalidPoolId(); uint256 unlockAmount = exchangeRate__.getUnlockAmount( burnAmount_, poolLockedAmounts[connectorPoolId] ); poolLockedAmounts[connectorPoolId] -= unlockAmount; // underflow revert expected IConnector(connector_).outbound{value: msg.value}( msgGasLimit_, abi.encode(receiver_, unlockAmount) ); emit TokensWithdrawn(connector_, msg.sender, receiver_, burnAmount_); } function _burn(address user_, uint256 burnAmount_) internal virtual { token__.burn(user_, burnAmount_); } function mintPendingFor(address receiver_, address connector_) external { if (_mintLimitParams[connector_].maxLimit == 0) revert ConnectorUnavailable(); uint256 pendingMint = pendingMints[connector_][receiver_]; (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( pendingMint, _mintLimitParams[connector_] ); pendingMints[connector_][receiver_] = pendingAmount; connectorPendingMints[connector_] -= consumedAmount; totalMinted += consumedAmount; token__.mint(receiver_, consumedAmount); emit PendingTokensMinted( connector_, receiver_, consumedAmount, pendingAmount ); } // receive inbound assuming connector called function receiveInbound(bytes memory payload_) external override { if (_mintLimitParams[msg.sender].maxLimit == 0) revert ConnectorUnavailable(); (address receiver, uint256 lockAmount) = abi.decode( payload_, (address, uint256) ); uint256 connectorPoolId = connectorPoolIds[msg.sender]; if (connectorPoolId == 0) revert InvalidPoolId(); poolLockedAmounts[connectorPoolId] += lockAmount; uint256 mintAmount = exchangeRate__.getMintAmount( lockAmount, poolLockedAmounts[connectorPoolId] ); (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( mintAmount, _mintLimitParams[msg.sender] ); if (pendingAmount > 0) { // add instead of overwrite to handle case where already pending amount is left pendingMints[msg.sender][receiver] += pendingAmount; connectorPendingMints[msg.sender] += pendingAmount; emit TokensPending( msg.sender, receiver, pendingAmount, pendingMints[msg.sender][receiver] ); } totalMinted += consumedAmount; token__.mint(receiver, consumedAmount); emit TokensMinted(msg.sender, receiver, consumedAmount); } function getMinFees( address connector_, uint256 msgGasLimit_ ) external view returns (uint256 totalFees) { return IConnector(connector_).getMinFees(msgGasLimit_); } function getCurrentMintLimit( address connector_ ) external view returns (uint256) { return _getCurrentLimit(_mintLimitParams[connector_]); } function getCurrentBurnLimit( address connector_ ) external view returns (uint256) { return _getCurrentLimit(_burnLimitParams[connector_]); } function getMintLimitParams( address connector_ ) external view returns (LimitParams memory) { return _mintLimitParams[connector_]; } function getBurnLimitParams( address connector_ ) external view returns (LimitParams memory) { return _burnLimitParams[connector_]; } /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyOwner { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } }
pragma solidity 0.8.13; import "../common/Ownable.sol"; import {RescueFundsLib} from "../libraries/RescueFundsLib.sol"; interface IExchangeRate { // not marked pure, may involve state interactions in future function getMintAmount( uint256 lockAmount, uint256 totalLockedAmount ) external returns (uint256 mintAmount); // not marked pure, may involve state interactions in future function getUnlockAmount( uint256 burnAmount, uint256 totalLockedAmount ) external returns (uint256 unlockAmount); } contract ExchangeRate is IExchangeRate, Ownable(msg.sender) { // chainId input needed? what else? slippage? function getMintAmount( uint256 lockAmount, uint256 /* totalLockedAmount */ ) external pure returns (uint256 mintAmount) { return lockAmount; } function getUnlockAmount( uint256 burnAmount, uint256 /* totalLockedAmount */ ) external pure returns (uint256 unlockAmount) { return burnAmount; } /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyOwner { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } }
pragma solidity 0.8.13; import "lib/solmate/src/utils/SafeTransferLib.sol"; import {Controller} from "../Controller.sol"; import {IMintableERC20} from "../IMintableERC20.sol"; import {IFiatTokenV2_1_Mintable} from "./IFiatTokenV2_1_Mintable.sol"; contract FiatTokenV2_1_Controller is Controller { using SafeTransferLib for IMintableERC20; constructor( address token_, address exchangeRate_ ) Controller(token_, exchangeRate_) {} function _burn(address user_, uint256 burnAmount_) internal override { token__.safeTransferFrom(user_, address(this), burnAmount_); IFiatTokenV2_1_Mintable(address(token__)).burn(burnAmount_); } }
pragma solidity 0.8.13; import "lib/solmate/src/tokens/ERC20.sol"; // USDC's standard token abstract contract IFiatTokenV2_1_Mintable is ERC20 { function mint(address receiver_, uint256 amount_) external virtual; function burn(uint256 _amount) external virtual; }
pragma solidity 0.8.13; import "lib/solmate/src/tokens/ERC20.sol"; abstract contract IMintableERC20 is ERC20 { function mint(address receiver_, uint256 amount_) external virtual; function burn(address burner_, uint256 amount_) external virtual; }
pragma solidity 0.8.13; import "lib/solmate/src/utils/ReentrancyGuard.sol"; import {RescueFundsLib} from "../libraries/RescueFundsLib.sol"; import {Gauge} from "../common/Gauge.sol"; import {AccessControl} from "../common/AccessControl.sol"; import "./interfaces/ISuperTokenOrVault.sol"; import "./interfaces/IMessageBridge.sol"; /** * @title Base contract for super token and vault * @notice It contains relevant execution payload storages. * @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge * to support any type of message bridge. */ abstract contract Base is ReentrancyGuard, Gauge, ISuperTokenOrVault, AccessControl { bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); error ZeroAddressReceiver(); error ZeroAmount(); /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyRole(RESCUE_ROLE) { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } }
pragma solidity 0.8.13; /** * @title IMessageBridge * @notice It should be implemented by message bridge integrated to Super token and Vault. */ interface IMessageBridge { /** * @notice calls socket's outbound function which transmits msg to `siblingChainSlug_`. * @dev Only super token or vault can call this contract * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param msgGasLimit_ min gas limit needed to execute the message on sibling * @param payload_ payload which should be executed at the sibling chain. * @param options_ extra bytes memory can be used by other protocol plugs for additional options */ function outbound( uint32 siblingChainSlug_, uint256 msgGasLimit_, bytes memory payload_, bytes memory options_ ) external payable returns (bytes32 messageId_); /** * @notice this function is used to calculate message id before sending outbound(). * @param siblingChainSlug_ The unique identifier of the sibling chain. * @return message id */ function getMessageId( uint32 siblingChainSlug_ ) external view returns (bytes32); }
pragma solidity 0.8.13; /** * @title ISuperTokenOrVault * @notice It should be implemented Super token and Vault for plugs to communicate. */ interface ISuperTokenOrVault { /** * @dev this should be only executable by socket. * @notice executes the message received from source chain. * @notice It is expected to have original sender checks in the destination plugs using payload. * @param siblingChainSlug_ chain slug of source. * @param payload_ the data which is needed to decode receiver, amount, msgId and payload. */ function inbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable; }
pragma solidity 0.8.13; import "./ExecutionHelper.sol"; import "../Base.sol"; abstract contract ExecutablePayloadBase is Base { /** * @notice this struct stores relevant details for a pending payload execution * @param receiver address of receiver where payload executes. * @param payload payload to be executed * @param isAmountPending if amount to be bridged is pending */ struct PendingExecutionDetails { bool isAmountPending; uint32 siblingChainSlug; address receiver; bytes payload; } ExecutionHelper public executionHelper__; // messageId => PendingExecutionDetails mapping(bytes32 => PendingExecutionDetails) public pendingExecutions; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// error InvalidExecutionRetry(); error PendingAmount(); error CannotExecuteOnBridgeContracts(); // emitted when a execution helper is updated event ExecutionHelperUpdated(address executionHelper); /** * @notice this function is used to update execution helper contract * @dev it can only be updated by owner * @param executionHelper_ new execution helper address */ function updateExecutionHelper( address executionHelper_ ) external onlyOwner { executionHelper__ = ExecutionHelper(executionHelper_); emit ExecutionHelperUpdated(executionHelper_); } /** * @notice this function can be used to retry a payload execution if it was not successful. * @param msgId_ The unique identifier of the bridging message. */ function retryPayloadExecution(bytes32 msgId_) external nonReentrant { PendingExecutionDetails storage details = pendingExecutions[msgId_]; if (details.isAmountPending) revert PendingAmount(); if (details.receiver == address(0)) revert InvalidExecutionRetry(); bool success = executionHelper__.execute( details.receiver, details.payload ); if (success) _clearPayload(msgId_); } /** * @notice this function caches the execution payload details if the amount to be bridged * is not pending or execution is reverting */ function _cachePayload( bytes32 msgId_, bool isAmountPending_, uint32 siblingChainSlug_, address receiver_, bytes memory payload_ ) internal { pendingExecutions[msgId_].receiver = receiver_; pendingExecutions[msgId_].payload = payload_; pendingExecutions[msgId_].siblingChainSlug = siblingChainSlug_; pendingExecutions[msgId_].isAmountPending = isAmountPending_; } /** * @notice this function clears the payload details once execution succeeds */ function _clearPayload(bytes32 msgId_) internal { pendingExecutions[msgId_].receiver = address(0); pendingExecutions[msgId_].payload = bytes(""); pendingExecutions[msgId_].siblingChainSlug = 0; pendingExecutions[msgId_].isAmountPending = false; } }
pragma solidity 0.8.13; import "../../libraries/ExcessivelySafeCall.sol"; /** * @title ExecutionHelper * @notice It is an untrusted contract used for payload execution by Super token and Vault. */ contract ExecutionHelper { using ExcessivelySafeCall for address; uint16 private constant MAX_COPY_BYTES = 0; /** * @notice this function is used to execute a payload at target_ * @dev receiver address cannot be this contract address. * @param target_ address of target. * @param payload_ payload to be executed at target. */ function execute( address target_, bytes memory payload_ ) external returns (bool success) { if (target_ == address(this)) return false; (success, ) = target_.excessivelySafeCall( gasleft(), MAX_COPY_BYTES, payload_ ); } }
pragma solidity 0.8.13; import {ISocket} from "../../interfaces/ISocket.sol"; import {IPlug} from "../../interfaces/IPlug.sol"; import {AccessControl} from "../../common/AccessControl.sol"; import {RescueFundsLib} from "../../libraries/RescueFundsLib.sol"; import {IMessageBridge} from "../interfaces/IMessageBridge.sol"; import {ISuperTokenOrVault} from "../interfaces/ISuperTokenOrVault.sol"; /** * @title SocketPlug * @notice It enables message bridging in Super token and Super Token Vault. * @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge * to support any type of message bridge. */ contract SocketPlug is IPlug, AccessControl, IMessageBridge { bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); // socket address ISocket public immutable socket__; // super token or vault address ISuperTokenOrVault public tokenOrVault__; // chain slug of current chain uint32 public immutable chainSlug; // map of sibling chain slugs with the plug addresses mapping(uint32 => address) public siblingPlugs; //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// // emitted when a plug is disconnected event SocketPlugDisconnected(uint32 siblingChainSlug); // emitted when a super token or vault address is set event SuperTokenOrVaultSet(); //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// error NotSuperTokenOrVault(); error NotSocket(); error TokenOrVaultAlreadySet(); /** * @notice constructor for creating a new SocketPlug. * @param socket_ The address of the Socket contract used to transmit messages. * @param owner_ The address of the owner who has the initial admin role. * @param chainSlug_ The unique identifier of the chain this plug is deployed on. */ constructor( address socket_, address owner_, uint32 chainSlug_ ) AccessControl(owner_) { socket__ = ISocket(socket_); chainSlug = chainSlug_; } /** * @notice calls socket's outbound function which transmits msg to `siblingChainSlug_`. * @dev Only super token or vault can call this function * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param msgGasLimit_ min gas limit needed to execute the message on sibling * @param payload_ payload which should be executed at the sibling chain. * @return messageId_ identifier used to get message details from Socket. */ function outbound( uint32 siblingChainSlug_, uint256 msgGasLimit_, bytes memory payload_, bytes memory ) external payable returns (bytes32 messageId_) { if (msg.sender != address(tokenOrVault__)) revert NotSuperTokenOrVault(); return socket__.outbound{value: msg.value}( siblingChainSlug_, msgGasLimit_, bytes32(0), bytes32(0), payload_ ); } /** * @notice this function receives the message from sibling chain. * @dev Only socket can call this function. * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param payload_ payload which should be executed at the super token or vault. */ function inbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable override { if (msg.sender != address(socket__)) revert NotSocket(); tokenOrVault__.inbound(siblingChainSlug_, payload_); } /** * @notice this function calculates the fees needed to send the message to Socket. * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param msgGasLimit_ min gas limit needed at destination chain to execute the message. */ function getMinFees( uint32 siblingChainSlug_, uint256 msgGasLimit_, uint256 payloadSize_ ) external view returns (uint256 totalFees) { return socket__.getMinFees( msgGasLimit_, payloadSize_, bytes32(0), bytes32(0), siblingChainSlug_, address(this) ); } /** * @notice this function is used to set the Super token or Vault address * @dev only owner can set the token address. * @dev this can be called only once. * @param tokenOrVault_ The super token or vault address connected to this plug. */ function setSuperTokenOrVault(address tokenOrVault_) external onlyOwner { if (address(tokenOrVault__) != address(0)) revert TokenOrVaultAlreadySet(); tokenOrVault__ = ISuperTokenOrVault(tokenOrVault_); emit SuperTokenOrVaultSet(); } /** * @notice this function is used to connect Socket for a `siblingChainSlug_`. * @dev only owner can connect Socket with preferred switchboard address. * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param siblingPlug_ address of plug present at siblingChainSlug_ to call at inbound * @param inboundSwitchboard_ the address of switchboard to use for verifying messages at inbound * @param outboundSwitchboard_ the address of switchboard to use for sending messages */ function connect( uint32 siblingChainSlug_, address siblingPlug_, address inboundSwitchboard_, address outboundSwitchboard_ ) external onlyOwner { siblingPlugs[siblingChainSlug_] = siblingPlug_; socket__.connect( siblingChainSlug_, siblingPlug_, inboundSwitchboard_, outboundSwitchboard_ ); } /** * @notice this function is used to disconnect Socket for a `siblingChainSlug_`. * @dev only owner can disconnect Socket * @dev it sets sibling plug as address(0) which makes it revert at `outbound()` hence * @dev stopping it from sending any message. * @param siblingChainSlug_ The unique identifier of the sibling chain. */ function disconnect(uint32 siblingChainSlug_) external onlyOwner { delete siblingPlugs[siblingChainSlug_]; ( , address inboundSwitchboard, address outboundSwitchboard, , ) = socket__.getPlugConfig(address(this), siblingChainSlug_); socket__.connect( siblingChainSlug_, address(0), inboundSwitchboard, outboundSwitchboard ); emit SocketPlugDisconnected(siblingChainSlug_); } /** * @notice this function is used to calculate message id before sending outbound(). * @param siblingChainSlug_ The unique identifier of the sibling chain. * @return message id */ function getMessageId( uint32 siblingChainSlug_ ) public view returns (bytes32) { return bytes32( (uint256(chainSlug) << 224) | (uint256(uint160(siblingPlugs[siblingChainSlug_])) << 64) | (ISocket(socket__).globalMessageCount()) ); } /** * @notice Rescues funds from the contract if they are locked by mistake. * @param token_ The address of the token contract. * @param rescueTo_ The address where rescued tokens need to be sent. * @param amount_ The amount of tokens to be rescued. */ function rescueFunds( address token_, address rescueTo_, uint256 amount_ ) external onlyRole(RESCUE_ROLE) { RescueFundsLib.rescueFunds(token_, rescueTo_, amount_); } }
pragma solidity 0.8.13; import "lib/solmate/src/tokens/ERC20.sol"; import "./Base.sol"; /** * @title SuperToken * @notice An ERC20 contract which enables bridging a token to its sibling chains. * @dev This contract implements ISuperTokenOrVault to support message bridging through IMessageBridge compliant contracts. */ contract SuperToken is ERC20, Base { struct UpdateLimitParams { bool isMint; uint32 siblingChainSlug; uint256 maxLimit; uint256 ratePerSecond; } bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE"); // bridge contract address which provides AMB support IMessageBridge public bridge__; // siblingChainSlug => mintLimitParams mapping(uint32 => LimitParams) _receivingLimitParams; // siblingChainSlug => burnLimitParams mapping(uint32 => LimitParams) _sendingLimitParams; // siblingChainSlug => receiver => identifier => amount mapping(uint32 => mapping(address => mapping(bytes32 => uint256))) public pendingMints; // siblingChainSlug => amount mapping(uint32 => uint256) public siblingPendingMints; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// error SiblingNotSupported(); error MessageIdMisMatched(); error NotMessageBridge(); error InvalidSiblingChainSlug(); //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// // emitted when limit params are updated event LimitParamsUpdated(UpdateLimitParams[] updates); // emitted when message bridge is updated event MessageBridgeUpdated(address newBridge); // emitted at source when tokens are bridged to a sibling chain event BridgeTokens( uint32 siblingChainSlug, address withdrawer, address receiver, uint256 bridgedAmount, bytes32 identifier ); // emitted when pending tokens are minted to the receiver event PendingTokensBridged( uint32 siblingChainSlug, address receiver, uint256 mintAmount, uint256 pendingAmount, bytes32 identifier ); // emitted when transfer reaches limit and token mint is added to pending queue event TokensPending( uint32 siblingChainSlug, address receiver, uint256 pendingAmount, uint256 totalPendingAmount, bytes32 identifier ); // emitted when pending tokens are minted as limits are replenished event TokensBridged( uint32 siblingChainSlug, address receiver, uint256 mintAmount, uint256 totalAmount, bytes32 identifier ); /** * @notice constructor for creating a new SuperToken. * @param name_ token name * @param symbol_ token symbol * @param decimals_ token decimals (should be same on all chains) * @param initialSupplyHolder_ address to which initial supply will be minted * @param owner_ owner of this contract * @param initialSupply_ initial supply of super token * @param bridge_ message bridge address */ constructor( string memory name_, string memory symbol_, uint8 decimals_, address initialSupplyHolder_, address owner_, uint256 initialSupply_, address bridge_ ) ERC20(name_, symbol_, decimals_) AccessControl(owner_) { _mint(initialSupplyHolder_, initialSupply_); bridge__ = IMessageBridge(bridge_); } /** * @notice this function is used to update message bridge * @dev it can only be updated by owner * @dev should be carefully migrated as it can risk user funds * @param bridge_ new bridge address */ function updateMessageBridge(address bridge_) external onlyOwner { bridge__ = IMessageBridge(bridge_); emit MessageBridgeUpdated(bridge_); } /** * @notice this function is used to set bridge limits * @dev it can only be updated by owner * @param updates_ can be used to set mint and burn limits for all siblings in one call. */ function updateLimitParams( UpdateLimitParams[] calldata updates_ ) external onlyRole(LIMIT_UPDATER_ROLE) { for (uint256 i; i < updates_.length; i++) { if (updates_[i].isMint) { _consumePartLimit( 0, _receivingLimitParams[updates_[i].siblingChainSlug] ); // to keep current limit in sync _receivingLimitParams[updates_[i].siblingChainSlug] .maxLimit = updates_[i].maxLimit; _receivingLimitParams[updates_[i].siblingChainSlug] .ratePerSecond = updates_[i].ratePerSecond; } else { _consumePartLimit( 0, _sendingLimitParams[updates_[i].siblingChainSlug] ); // to keep current limit in sync _sendingLimitParams[updates_[i].siblingChainSlug] .maxLimit = updates_[i].maxLimit; _sendingLimitParams[updates_[i].siblingChainSlug] .ratePerSecond = updates_[i].ratePerSecond; } } emit LimitParamsUpdated(updates_); } /** * @notice this function is called by users to bridge their funds to a sibling chain * @dev it is payable to receive message bridge fees to be paid. * @param receiver_ address receiving bridged tokens * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param sendingAmount_ amount bridged * @param msgGasLimit_ min gas limit needed for execution at destination * @param options_ additional message bridge options can be provided using this param */ function bridge( address receiver_, uint32 siblingChainSlug_, uint256 sendingAmount_, uint256 msgGasLimit_, bytes calldata options_ ) external payable { if (receiver_ == address(0)) revert ZeroAddressReceiver(); if (sendingAmount_ == 0) revert ZeroAmount(); if (_sendingLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingNotSupported(); _consumeFullLimit( sendingAmount_, _sendingLimitParams[siblingChainSlug_] ); // reverts on limit hit _burn(msg.sender, sendingAmount_); bytes32 messageId = bridge__.getMessageId(siblingChainSlug_); // important to get message id as it is used as an // identifier for pending amount and payload caching bytes32 returnedMessageId = bridge__.outbound{value: msg.value}( siblingChainSlug_, msgGasLimit_, abi.encode(receiver_, sendingAmount_, messageId), options_ ); if (returnedMessageId != messageId) revert MessageIdMisMatched(); emit BridgeTokens( siblingChainSlug_, msg.sender, receiver_, sendingAmount_, messageId ); } /** * @notice this function can be used to mint funds which were in pending state due to limits * @param receiver_ address receiving bridged tokens * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param identifier_ message identifier where message was received to mint funds */ function mintPendingFor( address receiver_, uint32 siblingChainSlug_, bytes32 identifier_ ) external nonReentrant { if (_receivingLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingNotSupported(); uint256 pendingMint = pendingMints[siblingChainSlug_][receiver_][ identifier_ ]; (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( pendingMint, _receivingLimitParams[siblingChainSlug_] ); pendingMints[siblingChainSlug_][receiver_][identifier_] = pendingAmount; siblingPendingMints[siblingChainSlug_] -= consumedAmount; _mint(receiver_, consumedAmount); emit PendingTokensBridged( siblingChainSlug_, receiver_, consumedAmount, pendingAmount, identifier_ ); } /** * @notice this function receives the message from message bridge * @dev Only bridge can call this function. * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param payload_ payload which is decoded to get `receiver`, `amount to mint`, `message id` and `payload` to execute after token transfer. */ function inbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable override nonReentrant { if (msg.sender != address(bridge__)) revert NotMessageBridge(); if (_receivingLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingNotSupported(); (address receiver, uint256 mintAmount, bytes32 identifier) = abi.decode( payload_, (address, uint256, bytes32) ); (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( mintAmount, _receivingLimitParams[siblingChainSlug_] ); _mint(receiver, consumedAmount); if (pendingAmount > 0) { pendingMints[siblingChainSlug_][receiver][ identifier ] = pendingAmount; siblingPendingMints[siblingChainSlug_] += pendingAmount; emit TokensPending( siblingChainSlug_, receiver, pendingAmount, pendingMints[siblingChainSlug_][receiver][identifier], identifier ); } emit TokensBridged( siblingChainSlug_, receiver, consumedAmount, mintAmount, identifier ); } function getCurrentReceivingLimit( uint32 siblingChainSlug_ ) external view returns (uint256) { return _getCurrentLimit(_receivingLimitParams[siblingChainSlug_]); } function getCurrentSendingLimit( uint32 siblingChainSlug_ ) external view returns (uint256) { return _getCurrentLimit(_sendingLimitParams[siblingChainSlug_]); } function getReceivingLimitParams( uint32 siblingChainSlug_ ) external view returns (LimitParams memory) { return _receivingLimitParams[siblingChainSlug_]; } function getSendingLimitParams( uint32 siblingChainSlug_ ) external view returns (LimitParams memory) { return _sendingLimitParams[siblingChainSlug_]; } }
pragma solidity 0.8.13; import "lib/solmate/src/utils/SafeTransferLib.sol"; import "./Base.sol"; /** * @title SuperTokenVault * @notice Vault contract which is used to lock/unlock token and enable bridging to its sibling chains. * @dev This contract implements ISuperTokenOrVault to support message bridging through IMessageBridge compliant contracts. */ contract SuperTokenVault is Base { using SafeTransferLib for ERC20; struct UpdateLimitParams { bool isLock; uint32 siblingChainSlug; uint256 maxLimit; uint256 ratePerSecond; } bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE"); ERC20 public immutable token__; IMessageBridge public bridge__; // siblingChainSlug => receiver => identifier => pendingUnlock mapping(uint32 => mapping(address => mapping(bytes32 => uint256))) public pendingUnlocks; // siblingChainSlug => amount mapping(uint32 => uint256) public siblingPendingUnlocks; // siblingChainSlug => lockLimitParams mapping(uint32 => LimitParams) _lockLimitParams; // siblingChainSlug => unlockLimitParams mapping(uint32 => LimitParams) _unlockLimitParams; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// error SiblingChainSlugUnavailable(); error NotMessageBridge(); error InvalidSiblingChainSlug(); error MessageIdMisMatched(); error InvalidTokenContract(); //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// // emitted when a message bridge is updated event MessageBridgeUpdated(address bridge); // emitted when limit params are updated event LimitParamsUpdated(UpdateLimitParams[] updates); // emitted at source when tokens are deposited to be bridged to a sibling chain event TokensDeposited( uint32 siblingChainSlug, address depositor, address receiver, uint256 depositAmount, bytes32 identifier ); // emitted when pending tokens are transferred to the receiver event PendingTokensTransferred( uint32 siblingChainSlug, address receiver, uint256 unlockedAmount, uint256 pendingAmount, bytes32 identifier ); // emitted when transfer reaches limit and token transfer is added to pending queue event TokensPending( uint32 siblingChainSlug, address receiver, uint256 pendingAmount, uint256 totalPendingAmount, bytes32 identifier ); // emitted when pending tokens are unlocked as limits are replenished event TokensUnlocked( uint32 siblingChainSlug, address receiver, uint256 unlockedAmount, bytes32 identifier ); /** * @notice constructor for creating a new SuperTokenVault. * @param token_ token contract address which is to be bridged. * @param owner_ owner of this contract * @param bridge_ message bridge address */ constructor( address token_, address owner_, address bridge_ ) AccessControl(owner_) { if (token_.code.length == 0) revert InvalidTokenContract(); token__ = ERC20(token_); bridge__ = IMessageBridge(bridge_); } /** * @notice this function is used to update message bridge * @dev it can only be updated by owner * @dev should be carefully migrated as it can risk user funds * @param bridge_ new bridge address */ function updateMessageBridge(address bridge_) external onlyOwner { bridge__ = IMessageBridge(bridge_); emit MessageBridgeUpdated(bridge_); } /** * @notice this function is used to set bridge limits * @dev it can only be updated by owner * @param updates_ can be used to set mint and burn limits for all siblings in one call. */ function updateLimitParams( UpdateLimitParams[] calldata updates_ ) external onlyRole(LIMIT_UPDATER_ROLE) { for (uint256 i; i < updates_.length; i++) { if (updates_[i].isLock) { _consumePartLimit( 0, _lockLimitParams[updates_[i].siblingChainSlug] ); // to keep current limit in sync _lockLimitParams[updates_[i].siblingChainSlug] .maxLimit = updates_[i].maxLimit; _lockLimitParams[updates_[i].siblingChainSlug] .ratePerSecond = updates_[i].ratePerSecond; } else { _consumePartLimit( 0, _unlockLimitParams[updates_[i].siblingChainSlug] ); // to keep current limit in sync _unlockLimitParams[updates_[i].siblingChainSlug] .maxLimit = updates_[i].maxLimit; _unlockLimitParams[updates_[i].siblingChainSlug] .ratePerSecond = updates_[i].ratePerSecond; } } emit LimitParamsUpdated(updates_); } /** * @notice this function is called by users to bridge their funds to a sibling chain * @dev it is payable to receive message bridge fees to be paid. * @param receiver_ address receiving bridged tokens * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param amount_ amount bridged * @param msgGasLimit_ min gas limit needed for execution at destination * @param options_ additional message bridge options can be provided using this param */ function bridge( address receiver_, uint32 siblingChainSlug_, uint256 amount_, uint256 msgGasLimit_, bytes calldata options_ ) external payable { if (receiver_ == address(0)) revert ZeroAddressReceiver(); if (amount_ == 0) revert ZeroAmount(); if (_lockLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingChainSlugUnavailable(); _consumeFullLimit(amount_, _lockLimitParams[siblingChainSlug_]); // reverts on limit hit token__.safeTransferFrom(msg.sender, address(this), amount_); bytes32 messageId = bridge__.getMessageId(siblingChainSlug_); bytes32 returnedMessageId = bridge__.outbound{value: msg.value}( siblingChainSlug_, msgGasLimit_, abi.encode(receiver_, amount_, messageId), options_ ); if (returnedMessageId != messageId) revert MessageIdMisMatched(); emit TokensDeposited( siblingChainSlug_, msg.sender, receiver_, amount_, messageId ); } /** * @notice this function can be used to unlock funds which were in pending state due to limits * @param receiver_ address receiving bridged tokens * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param identifier_ message identifier where message was received to unlock funds */ function unlockPendingFor( address receiver_, uint32 siblingChainSlug_, bytes32 identifier_ ) external nonReentrant { if (_unlockLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingChainSlugUnavailable(); uint256 pendingUnlock = pendingUnlocks[siblingChainSlug_][receiver_][ identifier_ ]; (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( pendingUnlock, _unlockLimitParams[siblingChainSlug_] ); pendingUnlocks[siblingChainSlug_][receiver_][ identifier_ ] = pendingAmount; siblingPendingUnlocks[siblingChainSlug_] -= consumedAmount; token__.safeTransfer(receiver_, consumedAmount); emit PendingTokensTransferred( siblingChainSlug_, receiver_, consumedAmount, pendingAmount, identifier_ ); } /** * @notice this function receives the message from message bridge * @dev Only bridge can call this function. * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param payload_ payload which is decoded to get `receiver`, `amount to mint`, `message id` and `payload` to execute after token transfer. */ function inbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable override nonReentrant { if (msg.sender != address(bridge__)) revert NotMessageBridge(); if (_unlockLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingChainSlugUnavailable(); (address receiver, uint256 unlockAmount, bytes32 identifier) = abi .decode(payload_, (address, uint256, bytes32)); (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( unlockAmount, _unlockLimitParams[siblingChainSlug_] ); token__.safeTransfer(receiver, consumedAmount); if (pendingAmount > 0) { pendingUnlocks[siblingChainSlug_][receiver][ identifier ] = pendingAmount; siblingPendingUnlocks[siblingChainSlug_] += pendingAmount; emit TokensPending( siblingChainSlug_, receiver, pendingAmount, pendingUnlocks[siblingChainSlug_][receiver][identifier], identifier ); } emit TokensUnlocked( siblingChainSlug_, receiver, consumedAmount, identifier ); } function getCurrentLockLimit( uint32 siblingChainSlug_ ) external view returns (uint256) { return _getCurrentLimit(_lockLimitParams[siblingChainSlug_]); } function getCurrentUnlockLimit( uint32 siblingChainSlug_ ) external view returns (uint256) { return _getCurrentLimit(_unlockLimitParams[siblingChainSlug_]); } function getLockLimitParams( uint32 siblingChainSlug_ ) external view returns (LimitParams memory) { return _lockLimitParams[siblingChainSlug_]; } function getUnlockLimitParams( uint32 siblingChainSlug_ ) external view returns (LimitParams memory) { return _unlockLimitParams[siblingChainSlug_]; } }
pragma solidity 0.8.13; import "lib/solmate/src/utils/SafeTransferLib.sol"; import "./plugins/ExecutablePayloadBase.sol"; /** * @title SuperTokenVault * @notice Vault contract which is used to lock/unlock token and enable bridging to its sibling chains. * @dev This contract implements ISuperTokenOrVault to support message bridging through IMessageBridge compliant contracts. */ contract SuperTokenVaultWithExecutionPayload is ExecutablePayloadBase { using SafeTransferLib for ERC20; struct UpdateLimitParams { bool isLock; uint32 siblingChainSlug; uint256 maxLimit; uint256 ratePerSecond; } bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE"); ERC20 public immutable token__; IMessageBridge public bridge__; // siblingChainSlug => receiver => identifier => pendingUnlock mapping(uint32 => mapping(address => mapping(bytes32 => uint256))) public pendingUnlocks; // siblingChainSlug => amount mapping(uint32 => uint256) public siblingPendingUnlocks; // siblingChainSlug => lockLimitParams mapping(uint32 => LimitParams) _lockLimitParams; // siblingChainSlug => unlockLimitParams mapping(uint32 => LimitParams) _unlockLimitParams; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// error SiblingChainSlugUnavailable(); error NotMessageBridge(); error InvalidReceiver(); error InvalidSiblingChainSlug(); error MessageIdMisMatched(); error InvalidTokenContract(); //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// // emitted when a message bridge is updated event MessageBridgeUpdated(address bridge); // emitted when limit params are updated event LimitParamsUpdated(UpdateLimitParams[] updates); // emitted at source when tokens are deposited to be bridged to a sibling chain event TokensDeposited( uint32 siblingChainSlug, address depositor, address receiver, uint256 depositAmount, bytes32 identifier ); // emitted when pending tokens are transferred to the receiver event PendingTokensTransferred( uint32 siblingChainSlug, address receiver, uint256 unlockedAmount, uint256 pendingAmount, bytes32 identifier ); // emitted when transfer reaches limit and token transfer is added to pending queue event TokensPending( uint32 siblingChainSlug, address receiver, uint256 pendingAmount, uint256 totalPendingAmount, bytes32 identifier ); // emitted when pending tokens are unlocked as limits are replenished event TokensUnlocked( uint32 siblingChainSlug, address receiver, uint256 unlockedAmount, bytes32 identifier ); /** * @notice constructor for creating a new SuperTokenVault. * @param token_ token contract address which is to be bridged. * @param owner_ owner of this contract * @param bridge_ message bridge address */ constructor( address token_, address owner_, address bridge_, address executionHelper_ ) AccessControl(owner_) { if (token_.code.length == 0) revert InvalidTokenContract(); token__ = ERC20(token_); bridge__ = IMessageBridge(bridge_); executionHelper__ = ExecutionHelper(executionHelper_); } /** * @notice this function is used to update message bridge * @dev it can only be updated by owner * @dev should be carefully migrated as it can risk user funds * @param bridge_ new bridge address */ function updateMessageBridge(address bridge_) external onlyOwner { bridge__ = IMessageBridge(bridge_); emit MessageBridgeUpdated(bridge_); } /** * @notice this function is used to set bridge limits * @dev it can only be updated by owner * @param updates_ can be used to set mint and burn limits for all siblings in one call. */ function updateLimitParams( UpdateLimitParams[] calldata updates_ ) external onlyRole(LIMIT_UPDATER_ROLE) { for (uint256 i; i < updates_.length; i++) { if (updates_[i].isLock) { _consumePartLimit( 0, _lockLimitParams[updates_[i].siblingChainSlug] ); // to keep current limit in sync _lockLimitParams[updates_[i].siblingChainSlug] .maxLimit = updates_[i].maxLimit; _lockLimitParams[updates_[i].siblingChainSlug] .ratePerSecond = updates_[i].ratePerSecond; } else { _consumePartLimit( 0, _unlockLimitParams[updates_[i].siblingChainSlug] ); // to keep current limit in sync _unlockLimitParams[updates_[i].siblingChainSlug] .maxLimit = updates_[i].maxLimit; _unlockLimitParams[updates_[i].siblingChainSlug] .ratePerSecond = updates_[i].ratePerSecond; } } emit LimitParamsUpdated(updates_); } /** * @notice this function is called by users to bridge their funds to a sibling chain * @dev it is payable to receive message bridge fees to be paid. * @param receiver_ address receiving bridged tokens * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param amount_ amount bridged * @param msgGasLimit_ min gas limit needed for execution at destination * @param payload_ payload which is executed at destination with bridged amount at receiver address. * @param options_ additional message bridge options can be provided using this param */ function bridge( address receiver_, uint32 siblingChainSlug_, uint256 amount_, uint256 msgGasLimit_, bytes calldata payload_, bytes calldata options_ ) external payable { if (receiver_ == address(0)) revert ZeroAddressReceiver(); if (amount_ == 0) revert ZeroAmount(); if (_lockLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingChainSlugUnavailable(); _consumeFullLimit(amount_, _lockLimitParams[siblingChainSlug_]); // reverts on limit hit token__.safeTransferFrom(msg.sender, address(this), amount_); bytes32 messageId = bridge__.getMessageId(siblingChainSlug_); bytes32 returnedMessageId = bridge__.outbound{value: msg.value}( siblingChainSlug_, msgGasLimit_, abi.encode(receiver_, amount_, messageId, payload_), options_ ); if (returnedMessageId != messageId) revert MessageIdMisMatched(); emit TokensDeposited( siblingChainSlug_, msg.sender, receiver_, amount_, messageId ); } /** * @notice this function can be used to unlock funds which were in pending state due to limits * @param receiver_ address receiving bridged tokens * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param identifier_ message identifier where message was received to unlock funds */ function unlockPendingFor( address receiver_, uint32 siblingChainSlug_, bytes32 identifier_ ) external nonReentrant { if (_unlockLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingChainSlugUnavailable(); uint256 pendingUnlock = pendingUnlocks[siblingChainSlug_][receiver_][ identifier_ ]; (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( pendingUnlock, _unlockLimitParams[siblingChainSlug_] ); pendingUnlocks[siblingChainSlug_][receiver_][ identifier_ ] = pendingAmount; siblingPendingUnlocks[siblingChainSlug_] -= consumedAmount; token__.safeTransfer(receiver_, consumedAmount); address receiver = pendingExecutions[identifier_].receiver; if (pendingAmount == 0 && receiver != address(0)) { if (receiver_ != receiver) revert InvalidReceiver(); uint32 siblingChainSlug = pendingExecutions[identifier_] .siblingChainSlug; if (siblingChainSlug != siblingChainSlug_) revert InvalidSiblingChainSlug(); // execute pendingExecutions[identifier_].isAmountPending = false; bool success = executionHelper__.execute( receiver_, pendingExecutions[identifier_].payload ); if (success) _clearPayload(identifier_); } emit PendingTokensTransferred( siblingChainSlug_, receiver_, consumedAmount, pendingAmount, identifier_ ); } /** * @notice this function receives the message from message bridge * @dev Only bridge can call this function. * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param payload_ payload which is decoded to get `receiver`, `amount to mint`, `message id` and `payload` to execute after token transfer. */ function inbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable override nonReentrant { if (msg.sender != address(bridge__)) revert NotMessageBridge(); if (_unlockLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingChainSlugUnavailable(); ( address receiver, uint256 unlockAmount, bytes32 identifier, bytes memory execPayload ) = abi.decode(payload_, (address, uint256, bytes32, bytes)); if ( receiver == address(this) || receiver == address(bridge__) || receiver == address(token__) ) revert CannotExecuteOnBridgeContracts(); (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( unlockAmount, _unlockLimitParams[siblingChainSlug_] ); token__.safeTransfer(receiver, consumedAmount); if (pendingAmount > 0) { pendingUnlocks[siblingChainSlug_][receiver][ identifier ] = pendingAmount; siblingPendingUnlocks[siblingChainSlug_] += pendingAmount; // cache payload if (execPayload.length > 0) _cachePayload( identifier, true, siblingChainSlug_, receiver, execPayload ); emit TokensPending( siblingChainSlug_, receiver, pendingAmount, pendingUnlocks[siblingChainSlug_][receiver][identifier], identifier ); } else if (execPayload.length > 0) { // execute bool success = executionHelper__.execute(receiver, execPayload); if (!success) _cachePayload( identifier, false, siblingChainSlug_, receiver, execPayload ); } emit TokensUnlocked( siblingChainSlug_, receiver, consumedAmount, identifier ); } function getCurrentLockLimit( uint32 siblingChainSlug_ ) external view returns (uint256) { return _getCurrentLimit(_lockLimitParams[siblingChainSlug_]); } function getCurrentUnlockLimit( uint32 siblingChainSlug_ ) external view returns (uint256) { return _getCurrentLimit(_unlockLimitParams[siblingChainSlug_]); } function getLockLimitParams( uint32 siblingChainSlug_ ) external view returns (LimitParams memory) { return _lockLimitParams[siblingChainSlug_]; } function getUnlockLimitParams( uint32 siblingChainSlug_ ) external view returns (LimitParams memory) { return _unlockLimitParams[siblingChainSlug_]; } }
pragma solidity 0.8.13; import "lib/solmate/src/tokens/ERC20.sol"; import "./plugins/ExecutablePayloadBase.sol"; /** * @title SuperToken * @notice An ERC20 contract which enables bridging a token to its sibling chains. * @dev This contract implements ISuperTokenOrVault to support message bridging through IMessageBridge compliant contracts. */ contract SuperTokenWithExecutionPayload is ERC20, ExecutablePayloadBase { struct UpdateLimitParams { bool isMint; uint32 siblingChainSlug; uint256 maxLimit; uint256 ratePerSecond; } bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE"); // bridge contract address which provides AMB support IMessageBridge public bridge__; // siblingChainSlug => mintLimitParams mapping(uint32 => LimitParams) _receivingLimitParams; // siblingChainSlug => burnLimitParams mapping(uint32 => LimitParams) _sendingLimitParams; // siblingChainSlug => receiver => identifier => amount mapping(uint32 => mapping(address => mapping(bytes32 => uint256))) public pendingMints; // siblingChainSlug => amount mapping(uint32 => uint256) public siblingPendingMints; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// error SiblingNotSupported(); error MessageIdMisMatched(); error NotMessageBridge(); error InvalidReceiver(); error InvalidSiblingChainSlug(); //////////////////////////////////////////////////////// ////////////////////// EVENTS ////////////////////////// //////////////////////////////////////////////////////// // emitted when limit params are updated event LimitParamsUpdated(UpdateLimitParams[] updates); // emitted when message bridge is updated event MessageBridgeUpdated(address newBridge); // emitted at source when tokens are bridged to a sibling chain event BridgeTokens( uint32 siblingChainSlug, address withdrawer, address receiver, uint256 bridgedAmount, bytes32 identifier ); // emitted when pending tokens are minted to the receiver event PendingTokensBridged( uint32 siblingChainSlug, address receiver, uint256 mintAmount, uint256 pendingAmount, bytes32 identifier ); // emitted when transfer reaches limit and token mint is added to pending queue event TokensPending( uint32 siblingChainSlug, address receiver, uint256 pendingAmount, uint256 totalPendingAmount, bytes32 identifier ); // emitted when pending tokens are minted as limits are replenished event TokensBridged( uint32 siblingChainSlug, address receiver, uint256 mintAmount, uint256 totalAmount, bytes32 identifier ); /** * @notice constructor for creating a new SuperToken. * @param name_ token name * @param symbol_ token symbol * @param decimals_ token decimals (should be same on all chains) * @param initialSupplyHolder_ address to which initial supply will be minted * @param owner_ owner of this contract * @param initialSupply_ initial supply of super token * @param bridge_ message bridge address */ constructor( string memory name_, string memory symbol_, uint8 decimals_, address initialSupplyHolder_, address owner_, uint256 initialSupply_, address bridge_, address executionHelper_ ) ERC20(name_, symbol_, decimals_) AccessControl(owner_) { _mint(initialSupplyHolder_, initialSupply_); bridge__ = IMessageBridge(bridge_); executionHelper__ = ExecutionHelper(executionHelper_); } /** * @notice this function is used to update message bridge * @dev it can only be updated by owner * @dev should be carefully migrated as it can risk user funds * @param bridge_ new bridge address */ function updateMessageBridge(address bridge_) external onlyOwner { bridge__ = IMessageBridge(bridge_); emit MessageBridgeUpdated(bridge_); } /** * @notice this function is used to set bridge limits * @dev it can only be updated by owner * @param updates_ can be used to set mint and burn limits for all siblings in one call. */ function updateLimitParams( UpdateLimitParams[] calldata updates_ ) external onlyRole(LIMIT_UPDATER_ROLE) { for (uint256 i; i < updates_.length; i++) { if (updates_[i].isMint) { _consumePartLimit( 0, _receivingLimitParams[updates_[i].siblingChainSlug] ); // to keep current limit in sync _receivingLimitParams[updates_[i].siblingChainSlug] .maxLimit = updates_[i].maxLimit; _receivingLimitParams[updates_[i].siblingChainSlug] .ratePerSecond = updates_[i].ratePerSecond; } else { _consumePartLimit( 0, _sendingLimitParams[updates_[i].siblingChainSlug] ); // to keep current limit in sync _sendingLimitParams[updates_[i].siblingChainSlug] .maxLimit = updates_[i].maxLimit; _sendingLimitParams[updates_[i].siblingChainSlug] .ratePerSecond = updates_[i].ratePerSecond; } } emit LimitParamsUpdated(updates_); } /** * @notice this function is called by users to bridge their funds to a sibling chain * @dev it is payable to receive message bridge fees to be paid. * @param receiver_ address receiving bridged tokens * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param sendingAmount_ amount bridged * @param msgGasLimit_ min gas limit needed for execution at destination * @param payload_ payload which is executed at destination with bridged amount at receiver address. * @param options_ additional message bridge options can be provided using this param */ function bridge( address receiver_, uint32 siblingChainSlug_, uint256 sendingAmount_, uint256 msgGasLimit_, bytes calldata payload_, bytes calldata options_ ) external payable { if (receiver_ == address(0)) revert ZeroAddressReceiver(); if (sendingAmount_ == 0) revert ZeroAmount(); if (_sendingLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingNotSupported(); _consumeFullLimit( sendingAmount_, _sendingLimitParams[siblingChainSlug_] ); // reverts on limit hit _burn(msg.sender, sendingAmount_); bytes32 messageId = bridge__.getMessageId(siblingChainSlug_); // important to get message id as it is used as an // identifier for pending amount and payload caching bytes32 returnedMessageId = bridge__.outbound{value: msg.value}( siblingChainSlug_, msgGasLimit_, abi.encode(receiver_, sendingAmount_, messageId, payload_), options_ ); if (returnedMessageId != messageId) revert MessageIdMisMatched(); emit BridgeTokens( siblingChainSlug_, msg.sender, receiver_, sendingAmount_, messageId ); } /** * @notice this function can be used to mint funds which were in pending state due to limits * @param receiver_ address receiving bridged tokens * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param identifier_ message identifier where message was received to mint funds */ function mintPendingFor( address receiver_, uint32 siblingChainSlug_, bytes32 identifier_ ) external nonReentrant { if (_receivingLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingNotSupported(); uint256 pendingMint = pendingMints[siblingChainSlug_][receiver_][ identifier_ ]; (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( pendingMint, _receivingLimitParams[siblingChainSlug_] ); pendingMints[siblingChainSlug_][receiver_][identifier_] = pendingAmount; siblingPendingMints[siblingChainSlug_] -= consumedAmount; _mint(receiver_, consumedAmount); address receiver = pendingExecutions[identifier_].receiver; if (pendingAmount == 0 && receiver != address(0)) { if (receiver_ != receiver) revert InvalidReceiver(); uint32 siblingChainSlug = pendingExecutions[identifier_] .siblingChainSlug; if (siblingChainSlug != siblingChainSlug_) revert InvalidSiblingChainSlug(); // execute pendingExecutions[identifier_].isAmountPending = false; bool success = executionHelper__.execute( receiver_, pendingExecutions[identifier_].payload ); if (success) _clearPayload(identifier_); } emit PendingTokensBridged( siblingChainSlug_, receiver_, consumedAmount, pendingAmount, identifier_ ); } /** * @notice this function receives the message from message bridge * @dev Only bridge can call this function. * @param siblingChainSlug_ The unique identifier of the sibling chain. * @param payload_ payload which is decoded to get `receiver`, `amount to mint`, `message id` and `payload` to execute after token transfer. */ function inbound( uint32 siblingChainSlug_, bytes memory payload_ ) external payable override nonReentrant { if (msg.sender != address(bridge__)) revert NotMessageBridge(); if (_receivingLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingNotSupported(); ( address receiver, uint256 mintAmount, bytes32 identifier, bytes memory execPayload ) = abi.decode(payload_, (address, uint256, bytes32, bytes)); (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( mintAmount, _receivingLimitParams[siblingChainSlug_] ); if (receiver == address(this) || receiver == address(bridge__)) revert CannotExecuteOnBridgeContracts(); _mint(receiver, consumedAmount); if (pendingAmount > 0) { pendingMints[siblingChainSlug_][receiver][ identifier ] = pendingAmount; siblingPendingMints[siblingChainSlug_] += pendingAmount; // if pending amount is more than 0, payload is cached if (execPayload.length > 0) _cachePayload( identifier, true, siblingChainSlug_, receiver, execPayload ); emit TokensPending( siblingChainSlug_, receiver, pendingAmount, pendingMints[siblingChainSlug_][receiver][identifier], identifier ); } else if (execPayload.length > 0) { // execute bool success = executionHelper__.execute(receiver, execPayload); if (!success) _cachePayload( identifier, false, siblingChainSlug_, receiver, execPayload ); } emit TokensBridged( siblingChainSlug_, receiver, consumedAmount, mintAmount, identifier ); } function getCurrentReceivingLimit( uint32 siblingChainSlug_ ) external view returns (uint256) { return _getCurrentLimit(_receivingLimitParams[siblingChainSlug_]); } function getCurrentSendingLimit( uint32 siblingChainSlug_ ) external view returns (uint256) { return _getCurrentLimit(_sendingLimitParams[siblingChainSlug_]); } function getReceivingLimitParams( uint32 siblingChainSlug_ ) external view returns (LimitParams memory) { return _receivingLimitParams[siblingChainSlug_]; } function getSendingLimitParams( uint32 siblingChainSlug_ ) external view returns (LimitParams memory) { return _sendingLimitParams[siblingChainSlug_]; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() virtual { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { /*////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; /// @solidity memory-safe-assembly assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument. mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; /// @solidity memory-safe-assembly assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type. success := and( // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
{ "optimizer": { "enabled": true, "runs": 999999 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"token_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AmountOutsideLimit","type":"error"},{"inputs":[],"name":"ConnectorUnavailable","type":"error"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"OnlyNominee","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bool","name":"isLock","type":"bool"},{"internalType":"address","name":"connector","type":"address"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"}],"indexed":false,"internalType":"struct Vault.UpdateLimitParams[]","name":"updates","type":"tuple[]"}],"name":"LimitParamsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"OwnerClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nominee","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"unlockedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pendingAmount","type":"uint256"}],"name":"PendingTokensTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"}],"name":"TokensDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"pendingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalPendingAmount","type":"uint256"}],"name":"TokensPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"unlockedAmount","type":"uint256"}],"name":"TokensUnlocked","type":"event"},{"inputs":[],"name":"claimOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"connectorPendingUnlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"msgGasLimit_","type":"uint256"},{"internalType":"address","name":"connector_","type":"address"}],"name":"depositToAppChain","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"}],"name":"getCurrentLockLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"}],"name":"getCurrentUnlockLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"}],"name":"getLockLimitParams","outputs":[{"components":[{"internalType":"uint256","name":"lastUpdateTimestamp","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"lastUpdateLimit","type":"uint256"}],"internalType":"struct Gauge.LimitParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"},{"internalType":"uint256","name":"msgGasLimit_","type":"uint256"}],"name":"getMinFees","outputs":[{"internalType":"uint256","name":"totalFees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"}],"name":"getUnlockLimitParams","outputs":[{"components":[{"internalType":"uint256","name":"lastUpdateTimestamp","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"lastUpdateLimit","type":"uint256"}],"internalType":"struct Gauge.LimitParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nominee_","type":"address"}],"name":"nominateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominee","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"pendingUnlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload_","type":"bytes"}],"name":"receiveInbound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"rescueTo_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token__","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"connector_","type":"address"}],"name":"unlockPendingFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"isLock","type":"bool"},{"internalType":"address","name":"connector","type":"address"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"}],"internalType":"struct Vault.UpdateLimitParams[]","name":"updates_","type":"tuple[]"}],"name":"updateLimitParams","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b5060405162001b8f38038062001b8f833981016040819052610031916100a0565b3361003b8161004d565b506001600160a01b03166080526100d0565b600080546001600160a01b0383166001600160a01b0319918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b6000602082840312156100b257600080fd5b81516001600160a01b03811681146100c957600080fd5b9392505050565b608051611a8e62000101600039600081816102600152818161055d01528181610d050152610ff80152611a8e6000f3fe60806040526004361061010e5760003560e01c80635b94db27116100a557806384f289cc116100745780638da5cb5b116100595780638da5cb5b146103685780639b4cd94014610393578063f2b7c5cd146103b357600080fd5b806384f289cc14610335578063864f6a7a1461035557600080fd5b80635b94db27146102825780636ccae054146102a2578063755e1e24146102c25780638367080f1461031557600080fd5b80632fef4a8b116100e15780632fef4a8b146101ec5780633bd1adec146102195780634603f9f41461022e57806349eec51e1461024e57600080fd5b806310c56ed914610113578063186669e81461013557806320f99c0a146101805780632bc9c08a146101cc575b600080fd5b34801561011f57600080fd5b5061013361012e36600461156b565b6103d3565b005b34801561014157600080fd5b5061016d61015036600461165f565b600260209081526000928352604080842090915290825290205481565b6040519081526020015b60405180910390f35b34801561018c57600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610177565b3480156101d857600080fd5b506101336101e7366004611698565b6105e2565b3480156101f857600080fd5b5061016d61020736600461170d565b60036020526000908152604090205481565b34801561022557600080fd5b50610133610922565b34801561023a57600080fd5b5061016d61024936600461170d565b61097e565b34801561025a57600080fd5b506101a77f000000000000000000000000000000000000000000000000000000000000000081565b34801561028e57600080fd5b5061013361029d36600461170d565b6109b2565b3480156102ae57600080fd5b506101336102bd36600461172a565b610a72565b3480156102ce57600080fd5b506102e26102dd36600461170d565b610ad3565b60405161017791908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b34801561032157600080fd5b5061016d61033036600461176b565b610b59565b34801561034157600080fd5b5061016d61035036600461170d565b610bf2565b610133610363366004611797565b610c20565b34801561037457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101a7565b34801561039f57600080fd5b506102e26103ae36600461170d565b610e4b565b3480156103bf57600080fd5b506101336103ce36600461165f565b610ed1565b33600090815260056020526040812060020154900361041e576040517fb1efb84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808280602001905181019061043591906117e1565b33600090815260056020526040812092945090925090819061045890849061107b565b909250905080156105435733600090815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff88168452909152812080548392906104a390849061183e565b909155505033600090815260036020526040812080548392906104c790849061183e565b909155505033600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff891680855290835292819020548151948552918401929092528282018490526060830152517ffef75efa635291b302f2c3dfcc6ec92874e699d186cee8bd68c36c86ad35232c9181900360800190a15b61058473ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001685846110cf565b6040805133815273ffffffffffffffffffffffffffffffffffffffff861660208201529081018390527fece684e11f49f06d351439e63189ad1703238b8040d90cf994901ca2b3da8d44906060015b60405180910390a15050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610633576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156108e45782828281811061065057610650611856565b610666926020608090920201908101915061189a565b156107cc576106dd60006004600086868681811061068657610686611856565b905060800201602001602081019061069e919061170d565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002061107b565b50508282828181106106f1576106f1611856565b905060800201604001356004600085858581811061071157610711611856565b9050608002016020016020810190610729919061170d565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002015582828281811061076557610765611856565b905060800201606001356004600085858581811061078557610785611856565b905060800201602001602081019061079d919061170d565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020600101556108d2565b6107e760006005600086868681811061068657610686611856565b50508282828181106107fb576107fb611856565b905060800201604001356005600085858581811061081b5761081b611856565b9050608002016020016020810190610833919061170d565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002015582828281811061086f5761086f611856565b905060800201606001356005600085858581811061088f5761088f611856565b90506080020160200160208101906108a7919061170d565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020600101555b806108dc816118b5565b915050610636565b507f19863caed14ed012a54f927e56250018b7ac3c04fd197e0edf23369f75a6ccd482826040516109169291906118ed565b60405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610973576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61097c336111a9565b565b73ffffffffffffffffffffffffffffffffffffffff811660009081526004602052604081206109ac90611221565b92915050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a03576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ac3576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ace838383611288565b505050565b610afe6040518060800160405280600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260046020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b6040517f89c1cf9a0000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff8416906389c1cf9a90602401602060405180830381865afa158015610bc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610beb9190611970565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604081206109ac90611221565b82600003610c5a576040517f1f2a200500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166000908152600460205260408120600201549003610cbb576040517fb1efb84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166000908152600460205260409020610ceb908490611378565b610d2d73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163330866113d5565b8073ffffffffffffffffffffffffffffffffffffffff16631cbdf12b34848787604051602001610d7f92919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b6040516020818303038152906040526040518463ffffffff1660e01b8152600401610dab929190611989565b6000604051808303818588803b158015610dc457600080fd5b505af1158015610dd8573d6000803e3d6000fd5b50506040805173ffffffffffffffffffffffffffffffffffffffff8087168252336020830152891691810191909152606081018790527f9474e087d8a0e83962ac44e292b4aba027426203ea66adfb1dd9f65795ff599a93506080019150610e3d9050565b60405180910390a150505050565b610e766040518060800160405280600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260056020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600560205260408120600201549003610f32576040517fb1efb84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8082166000818152600260209081526040808320948716835293815283822054928252600590529182209091908190610f8190849061107b565b73ffffffffffffffffffffffffffffffffffffffff8087166000818152600260209081526040808320948c168352938152838220859055918152600390915290812080549395509193508492610fd8908490611a04565b9091555061101f905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001686846110cf565b6040805173ffffffffffffffffffffffffffffffffffffffff808716825287166020820152908101839052606081018290527fc77b48bb4899bc689b5733d1b7b8bfc0f70e7660295ad5f94841902db16ddae2906080016105d3565b600080600061108984611221565b42855590508481106110b05761109f8582611a04565b6003850155849250600091506110c7565b600060038501559150816110c48186611a04565b91505b509250929050565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806111a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064015b60405180910390fd5b50505050565b6000805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b805460009081906112329042611a04565b905060008360010154826112469190611a1b565b9050836002015484600301548261125d919061183e565b111561126f5783600201549250611281565b600384015461127e908261183e565b92505b5050919050565b73ffffffffffffffffffffffffffffffffffffffff82166112d5576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff84160161131c57610ace82826114c7565b8273ffffffffffffffffffffffffffffffffffffffff163b60000361136d576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ace8383836110cf565b600061138382611221565b90508281106113a3574282556113998382611a04565b6003830155505050565b6040517f47ebad2000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806114c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000604482015260640161119a565b5050505050565b600080600080600085875af1905080610ace576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c454400000000000000000000000000604482015260640161119a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561157d57600080fd5b813567ffffffffffffffff8082111561159557600080fd5b818401915084601f8301126115a957600080fd5b8135818111156115bb576115bb61153c565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156116015761160161153c565b8160405282815287602084870101111561161a57600080fd5b826020860160208301376000928101602001929092525095945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461165c57600080fd5b50565b6000806040838503121561167257600080fd5b823561167d8161163a565b9150602083013561168d8161163a565b809150509250929050565b600080602083850312156116ab57600080fd5b823567ffffffffffffffff808211156116c357600080fd5b818501915085601f8301126116d757600080fd5b8135818111156116e657600080fd5b8660208260071b85010111156116fb57600080fd5b60209290920196919550909350505050565b60006020828403121561171f57600080fd5b8135610beb8161163a565b60008060006060848603121561173f57600080fd5b833561174a8161163a565b9250602084013561175a8161163a565b929592945050506040919091013590565b6000806040838503121561177e57600080fd5b82356117898161163a565b946020939093013593505050565b600080600080608085870312156117ad57600080fd5b84356117b88161163a565b9350602085013592506040850135915060608501356117d68161163a565b939692955090935050565b600080604083850312156117f457600080fd5b82516117ff8161163a565b6020939093015192949293505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156118515761185161180f565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8035801515811461189557600080fd5b919050565b6000602082840312156118ac57600080fd5b610beb82611885565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036118e6576118e661180f565b5060010190565b6020808252818101839052600090604080840186845b878110156119635761191482611885565b15158352848201356119258161163a565b73ffffffffffffffffffffffffffffffffffffffff168386015281840135848401526060808301359084015260809283019290910190600101611903565b5090979650505050505050565b60006020828403121561198257600080fd5b5051919050565b82815260006020604081840152835180604085015260005b818110156119bd578581018301518582016060015282016119a1565b818111156119cf576000606083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201606001949350505050565b600082821015611a1657611a1661180f565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615611a5357611a5361180f565b50029056fea2646970667358221220b98a0b32548b79dd24a2b527e7737da6a9bb665234f4625d6f5271d172029a3264736f6c634300080d00330000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0
Deployed Bytecode
0x60806040526004361061010e5760003560e01c80635b94db27116100a557806384f289cc116100745780638da5cb5b116100595780638da5cb5b146103685780639b4cd94014610393578063f2b7c5cd146103b357600080fd5b806384f289cc14610335578063864f6a7a1461035557600080fd5b80635b94db27146102825780636ccae054146102a2578063755e1e24146102c25780638367080f1461031557600080fd5b80632fef4a8b116100e15780632fef4a8b146101ec5780633bd1adec146102195780634603f9f41461022e57806349eec51e1461024e57600080fd5b806310c56ed914610113578063186669e81461013557806320f99c0a146101805780632bc9c08a146101cc575b600080fd5b34801561011f57600080fd5b5061013361012e36600461156b565b6103d3565b005b34801561014157600080fd5b5061016d61015036600461165f565b600260209081526000928352604080842090915290825290205481565b6040519081526020015b60405180910390f35b34801561018c57600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610177565b3480156101d857600080fd5b506101336101e7366004611698565b6105e2565b3480156101f857600080fd5b5061016d61020736600461170d565b60036020526000908152604090205481565b34801561022557600080fd5b50610133610922565b34801561023a57600080fd5b5061016d61024936600461170d565b61097e565b34801561025a57600080fd5b506101a77f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca081565b34801561028e57600080fd5b5061013361029d36600461170d565b6109b2565b3480156102ae57600080fd5b506101336102bd36600461172a565b610a72565b3480156102ce57600080fd5b506102e26102dd36600461170d565b610ad3565b60405161017791908151815260208083015190820152604080830151908201526060918201519181019190915260800190565b34801561032157600080fd5b5061016d61033036600461176b565b610b59565b34801561034157600080fd5b5061016d61035036600461170d565b610bf2565b610133610363366004611797565b610c20565b34801561037457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101a7565b34801561039f57600080fd5b506102e26103ae36600461170d565b610e4b565b3480156103bf57600080fd5b506101336103ce36600461165f565b610ed1565b33600090815260056020526040812060020154900361041e576040517fb1efb84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808280602001905181019061043591906117e1565b33600090815260056020526040812092945090925090819061045890849061107b565b909250905080156105435733600090815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff88168452909152812080548392906104a390849061183e565b909155505033600090815260036020526040812080548392906104c790849061183e565b909155505033600081815260026020908152604080832073ffffffffffffffffffffffffffffffffffffffff891680855290835292819020548151948552918401929092528282018490526060830152517ffef75efa635291b302f2c3dfcc6ec92874e699d186cee8bd68c36c86ad35232c9181900360800190a15b61058473ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca01685846110cf565b6040805133815273ffffffffffffffffffffffffffffffffffffffff861660208201529081018390527fece684e11f49f06d351439e63189ad1703238b8040d90cf994901ca2b3da8d44906060015b60405180910390a15050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610633576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b818110156108e45782828281811061065057610650611856565b610666926020608090920201908101915061189a565b156107cc576106dd60006004600086868681811061068657610686611856565b905060800201602001602081019061069e919061170d565b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002061107b565b50508282828181106106f1576106f1611856565b905060800201604001356004600085858581811061071157610711611856565b9050608002016020016020810190610729919061170d565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002015582828281811061076557610765611856565b905060800201606001356004600085858581811061078557610785611856565b905060800201602001602081019061079d919061170d565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020600101556108d2565b6107e760006005600086868681811061068657610686611856565b50508282828181106107fb576107fb611856565b905060800201604001356005600085858581811061081b5761081b611856565b9050608002016020016020810190610833919061170d565b73ffffffffffffffffffffffffffffffffffffffff16815260208101919091526040016000206002015582828281811061086f5761086f611856565b905060800201606001356005600085858581811061088f5761088f611856565b90506080020160200160208101906108a7919061170d565b73ffffffffffffffffffffffffffffffffffffffff1681526020810191909152604001600020600101555b806108dc816118b5565b915050610636565b507f19863caed14ed012a54f927e56250018b7ac3c04fd197e0edf23369f75a6ccd482826040516109169291906118ed565b60405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610973576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61097c336111a9565b565b73ffffffffffffffffffffffffffffffffffffffff811660009081526004602052604081206109ac90611221565b92915050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610a03576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b60005473ffffffffffffffffffffffffffffffffffffffff163314610ac3576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ace838383611288565b505050565b610afe6040518060800160405280600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260046020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b6040517f89c1cf9a0000000000000000000000000000000000000000000000000000000081526004810182905260009073ffffffffffffffffffffffffffffffffffffffff8416906389c1cf9a90602401602060405180830381865afa158015610bc7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610beb9190611970565b9392505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526005602052604081206109ac90611221565b82600003610c5a576040517f1f2a200500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166000908152600460205260408120600201549003610cbb576040517fb1efb84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff81166000908152600460205260409020610ceb908490611378565b610d2d73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0163330866113d5565b8073ffffffffffffffffffffffffffffffffffffffff16631cbdf12b34848787604051602001610d7f92919073ffffffffffffffffffffffffffffffffffffffff929092168252602082015260400190565b6040516020818303038152906040526040518463ffffffff1660e01b8152600401610dab929190611989565b6000604051808303818588803b158015610dc457600080fd5b505af1158015610dd8573d6000803e3d6000fd5b50506040805173ffffffffffffffffffffffffffffffffffffffff8087168252336020830152891691810191909152606081018790527f9474e087d8a0e83962ac44e292b4aba027426203ea66adfb1dd9f65795ff599a93506080019150610e3d9050565b60405180910390a150505050565b610e766040518060800160405280600081526020016000815260200160008152602001600081525090565b5073ffffffffffffffffffffffffffffffffffffffff16600090815260056020908152604091829020825160808101845281548152600182015492810192909252600281015492820192909252600390910154606082015290565b73ffffffffffffffffffffffffffffffffffffffff81166000908152600560205260408120600201549003610f32576040517fb1efb84a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff8082166000818152600260209081526040808320948716835293815283822054928252600590529182209091908190610f8190849061107b565b73ffffffffffffffffffffffffffffffffffffffff8087166000818152600260209081526040808320948c168352938152838220859055918152600390915290812080549395509193508492610fd8908490611a04565b9091555061101f905073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca01686846110cf565b6040805173ffffffffffffffffffffffffffffffffffffffff808716825287166020820152908101839052606081018290527fc77b48bb4899bc689b5733d1b7b8bfc0f70e7660295ad5f94841902db16ddae2906080016105d3565b600080600061108984611221565b42855590508481106110b05761109f8582611a04565b6003850155849250600091506110c7565b600060038501559150816110c48186611a04565b91505b509250929050565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806111a3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c4544000000000000000000000000000000000060448201526064015b60405180910390fd5b50505050565b6000805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b805460009081906112329042611a04565b905060008360010154826112469190611a1b565b9050836002015484600301548261125d919061183e565b111561126f5783600201549250611281565b600384015461127e908261183e565b92505b5050919050565b73ffffffffffffffffffffffffffffffffffffffff82166112d5576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff84160161131c57610ace82826114c7565b8273ffffffffffffffffffffffffffffffffffffffff163b60000361136d576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610ace8383836110cf565b600061138382611221565b90508281106113a3574282556113998382611a04565b6003830155505050565b6040517f47ebad2000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff841660248201528260448201526020600060648360008a5af13d15601f3d11600160005114161716915050806114c0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c4544000000000000000000000000604482015260640161119a565b5050505050565b600080600080600085875af1905080610ace576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c454400000000000000000000000000604482015260640161119a565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60006020828403121561157d57600080fd5b813567ffffffffffffffff8082111561159557600080fd5b818401915084601f8301126115a957600080fd5b8135818111156115bb576115bb61153c565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019083821181831017156116015761160161153c565b8160405282815287602084870101111561161a57600080fd5b826020860160208301376000928101602001929092525095945050505050565b73ffffffffffffffffffffffffffffffffffffffff8116811461165c57600080fd5b50565b6000806040838503121561167257600080fd5b823561167d8161163a565b9150602083013561168d8161163a565b809150509250929050565b600080602083850312156116ab57600080fd5b823567ffffffffffffffff808211156116c357600080fd5b818501915085601f8301126116d757600080fd5b8135818111156116e657600080fd5b8660208260071b85010111156116fb57600080fd5b60209290920196919550909350505050565b60006020828403121561171f57600080fd5b8135610beb8161163a565b60008060006060848603121561173f57600080fd5b833561174a8161163a565b9250602084013561175a8161163a565b929592945050506040919091013590565b6000806040838503121561177e57600080fd5b82356117898161163a565b946020939093013593505050565b600080600080608085870312156117ad57600080fd5b84356117b88161163a565b9350602085013592506040850135915060608501356117d68161163a565b939692955090935050565b600080604083850312156117f457600080fd5b82516117ff8161163a565b6020939093015192949293505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082198211156118515761185161180f565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b8035801515811461189557600080fd5b919050565b6000602082840312156118ac57600080fd5b610beb82611885565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036118e6576118e661180f565b5060010190565b6020808252818101839052600090604080840186845b878110156119635761191482611885565b15158352848201356119258161163a565b73ffffffffffffffffffffffffffffffffffffffff168386015281840135848401526060808301359084015260809283019290910190600101611903565b5090979650505050505050565b60006020828403121561198257600080fd5b5051919050565b82815260006020604081840152835180604085015260005b818110156119bd578581018301518582016060015282016119a1565b818111156119cf576000606083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01692909201606001949350505050565b600082821015611a1657611a1661180f565b500390565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615611a5357611a5361180f565b50029056fea2646970667358221220b98a0b32548b79dd24a2b527e7737da6a9bb665234f4625d6f5271d172029a3264736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0
-----Decoded View---------------
Arg [0] : token_ (address): 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 0000000000000000000000007f39c581f595b53c5cb19bd0b3f8da6c935e2ca0
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 100.00% | $4,367.63 | 0.2538 | $1,108.65 |
Loading...
Loading
[ Download: CSV Export ]
[ 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.