Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
20371579 | 39 hrs ago | 0.00065858 ETH | ||||
20371579 | 39 hrs ago | 0.00065858 ETH | ||||
20353822 | 4 days ago | 0.00124134 ETH | ||||
20353822 | 4 days ago | 0.00124134 ETH | ||||
20286564 | 13 days ago | 0.00124134 ETH | ||||
20286564 | 13 days ago | 0.00124134 ETH | ||||
20192242 | 26 days ago | 0.00124134 ETH | ||||
20192242 | 26 days ago | 0.00124134 ETH | ||||
20159589 | 31 days ago | 0.00036028 ETH | ||||
20159589 | 31 days ago | 0.00036028 ETH | ||||
20092329 | 40 days ago | 0.00107618 ETH | ||||
20092329 | 40 days ago | 0.00107618 ETH | ||||
20092088 | 40 days ago | 0.00107618 ETH | ||||
20092088 | 40 days ago | 0.00107618 ETH | ||||
20089357 | 41 days ago | 0.00095512 ETH | ||||
20089357 | 41 days ago | 0.00095512 ETH | ||||
20070705 | 43 days ago | 0.00143311 ETH | ||||
20070705 | 43 days ago | 0.00143311 ETH | ||||
20070558 | 43 days ago | 0.00143311 ETH | ||||
20070558 | 43 days ago | 0.00143311 ETH | ||||
20013417 | 51 days ago | 0.00130423 ETH | ||||
20013417 | 51 days ago | 0.00130423 ETH | ||||
19872420 | 71 days ago | 0.00175926 ETH | ||||
19872420 | 71 days ago | 0.00175926 ETH | ||||
19872290 | 71 days ago | 0.00175926 ETH |
Loading...
Loading
Contract Name:
ConnectorPlug
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 "../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_); } }
// 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 {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/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_); } }
pragma solidity 0.8.13; import "lib/solmate/src/utils/ReentrancyGuard.sol"; import "../libraries/ExcessivelySafeCall.sol"; /** * @title Execute * @notice It enables payload execution and contains relevant storages. * @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge * to support any type of message bridge. */ contract Execute is ReentrancyGuard { using ExcessivelySafeCall for address; /** * @notice this struct stores relevant details for a pending payload execution * @param receiver address of receiver where payload executes. * @param siblingChainSlug the unique identifier of the source chain. * @param payload payload to be executed * @param isAmountPending if amount to be bridged is pending */ struct PendingExecutionDetails { address receiver; uint32 siblingChainSlug; bytes payload; bool isAmountPending; } uint16 private constant MAX_COPY_BYTES = 150; // messageId => PendingExecutionDetails mapping(bytes32 => PendingExecutionDetails) public pendingExecutions; //////////////////////////////////////////////////////// ////////////////////// ERRORS ////////////////////////// //////////////////////////////////////////////////////// error InvalidExecutionRetry(); error PendingAmount(); error CannotExecuteOnBridgeContracts(); /** * @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 = _execute(details.receiver, details.payload); if (success) _clearPayload(msgId_); } /** * @notice this function is used to execute a payload at receiver * @dev receiver address cannot be bridge address or this contract address. * @param target_ address of target. * @param payload_ payload to be executed at target. */ function _execute( address target_, bytes memory payload_ ) internal returns (bool success) { (success, ) = target_.excessivelySafeCall( gasleft(), MAX_COPY_BYTES, payload_ ); } /** * @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_, uint32 siblingChainSlug_, bool isAmountPending_, address receiver_, bytes memory payload_ ) internal { pendingExecutions[msgId_].receiver = receiver_; pendingExecutions[msgId_].siblingChainSlug = siblingChainSlug_; pendingExecutions[msgId_].payload = payload_; 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_].siblingChainSlug = 0; pendingExecutions[msgId_].payload = bytes(""); pendingExecutions[msgId_].isAmountPending = false; } }
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 {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 "./../IMessageBridge.sol"; import {ISuperTokenOrVault} from "./../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_ ) external view returns (uint256 totalFees) { return socket__.getMinFees( msgGasLimit_, 96, 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 { ( , 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 {AccessControl} from "../common/AccessControl.sol"; import {Gauge} from "../common/Gauge.sol"; import {RescueFundsLib} from "../libraries/RescueFundsLib.sol"; import "./Execute.sol"; import "./IMessageBridge.sol"; import "./ISuperTokenOrVault.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, Gauge, ISuperTokenOrVault, AccessControl, Execute { struct UpdateLimitParams { bool isMint; uint32 siblingChainSlug; uint256 maxLimit; uint256 ratePerSecond; } bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); 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 ZeroAmount(); error NotMessageBridge(); //////////////////////////////////////////////////////// ////////////////////// 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 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 (_sendingLimitParams[siblingChainSlug_].maxLimit == 0) revert SiblingNotSupported(); if (sendingAmount_ == 0) revert ZeroAmount(); _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); if ( pendingAmount == 0 && pendingExecutions[identifier_].receiver != address(0) ) { // execute pendingExecutions[identifier_].isAmountPending = false; bool success = _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, siblingChainSlug_, true, receiver, execPayload ); emit TokensPending( siblingChainSlug_, receiver, pendingAmount, pendingMints[siblingChainSlug_][receiver][identifier], identifier ); } else if (execPayload.length > 0) { // execute bool success = _execute(receiver, execPayload); if (!success) _cachePayload( identifier, siblingChainSlug_, false, 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_]; } /** * @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/utils/SafeTransferLib.sol"; import {AccessControl} from "../common/AccessControl.sol"; import {Gauge} from "../common/Gauge.sol"; import {RescueFundsLib} from "../libraries/RescueFundsLib.sol"; import "./Execute.sol"; import {ISuperTokenOrVault} from "./ISuperTokenOrVault.sol"; import {IMessageBridge} from "./IMessageBridge.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 Gauge, ISuperTokenOrVault, AccessControl, Execute { using SafeTransferLib for ERC20; struct UpdateLimitParams { bool isLock; uint32 siblingChainSlug; uint256 maxLimit; uint256 ratePerSecond; } bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE"); 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 ZeroAmount(); error NotMessageBridge(); //////////////////////////////////////////////////////// ////////////////////// 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 ); // emitted when pending tokens are transferred to the receiver event PendingTokensTransferred( uint32 siblingChainSlug, address receiver, uint256 unlockedAmount, uint256 pendingAmount ); // emitted when transfer reaches limit and token transfer is added to pending queue event TokensPending( uint32 siblingChainSlug, address receiver, uint256 pendingAmount, uint256 totalPendingAmount ); // emitted when pending tokens are unlocked as limits are replenished event TokensUnlocked( uint32 siblingChainSlug, address receiver, uint256 unlockedAmount ); /** * @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_) { 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 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 (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_); bridge__.outbound{value: msg.value}( siblingChainSlug_, msgGasLimit_, abi.encode(receiver_, amount_, messageId, payload_), options_ ); emit TokensDeposited(siblingChainSlug_, msg.sender, receiver_, amount_); } /** * @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); if ( pendingAmount == 0 && pendingExecutions[identifier_].receiver != address(0) ) { // execute pendingExecutions[identifier_].isAmountPending = false; bool success = _execute( receiver_, pendingExecutions[identifier_].payload ); if (success) _clearPayload(identifier_); } emit PendingTokensTransferred( siblingChainSlug_, receiver_, consumedAmount, pendingAmount ); } /** * @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__)) revert CannotExecuteOnBridgeContracts(); (uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit( unlockAmount, _unlockLimitParams[siblingChainSlug_] ); token__.safeTransfer(receiver, consumedAmount); if (pendingAmount > 0) { // add instead of overwrite to handle case where already pending amount is left pendingUnlocks[siblingChainSlug_][receiver][ identifier ] += pendingAmount; siblingPendingUnlocks[siblingChainSlug_] += pendingAmount; // cache payload if (execPayload.length > 0) _cachePayload( identifier, siblingChainSlug_, true, receiver, execPayload ); emit TokensPending( siblingChainSlug_, receiver, pendingAmount, pendingUnlocks[siblingChainSlug_][receiver][identifier] ); } else if (execPayload.length > 0) { // execute bool success = _execute(receiver, execPayload); if (!success) _cachePayload( identifier, siblingChainSlug_, false, receiver, execPayload ); } emit TokensUnlocked(siblingChainSlug_, receiver, consumedAmount); } 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_]; } /** * @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_); } }
// 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":"hub_","type":"address"},{"internalType":"address","name":"socket_","type":"address"},{"internalType":"uint32","name":"siblingChainSlug_","type":"uint32"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"NotHub","type":"error"},{"inputs":[],"name":"NotSocket","type":"error"},{"inputs":[],"name":"OnlyNominee","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[],"name":"ConnectorPlugDisconnected","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"},{"inputs":[],"name":"claimOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"siblingPlug_","type":"address"},{"internalType":"address","name":"switchboard_","type":"address"}],"name":"connect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disconnect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"msgGasLimit_","type":"uint256"}],"name":"getMinFees","outputs":[{"internalType":"uint256","name":"totalFees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hub__","outputs":[{"internalType":"contract IHub","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"bytes","name":"payload_","type":"bytes"}],"name":"inbound","outputs":[],"stateMutability":"payable","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":[{"internalType":"uint256","name":"msgGasLimit_","type":"uint256"},{"internalType":"bytes","name":"payload_","type":"bytes"}],"name":"outbound","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","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":"siblingChainSlug","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"socket__","outputs":[{"internalType":"contract ISocket","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60e060405234801561001057600080fd5b50604051620013b7380380620013b7833981016040819052610031916100cc565b3361003b8161005d565b506001600160a01b03928316608052911660a05263ffffffff1660c05261011c565b600080546001600160a01b0383166001600160a01b0319918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b80516001600160a01b03811681146100c757600080fd5b919050565b6000806000606084860312156100e157600080fd5b6100ea846100b0565b92506100f8602085016100b0565b9150604084015163ffffffff8116811461011157600080fd5b809150509250925092565b60805160a05160c051611210620001a760003960008181610251015281816103bf015281816104b60152818161072e015281816109800152610a5b0152600081816102ad01528181610390015281816105090152818161075c015281816107fa015281816109c10152610ab40152600081816101c4015281816102fc015261088e01526112106000f3fe6080604052600436106100d25760003560e01c806377e031d91161007f578063bf965e5211610059578063bf965e521461023f578063c41f1f6c14610288578063c6a261d21461029b578063d9374bff146102cf57600080fd5b806377e031d9146101b257806389c1cf9a146101e65780638da5cb5b1461021457600080fd5b80633bd1adec116100b05780633bd1adec1461015d5780635b94db27146101725780636ccae0541461019257600080fd5b80631cbdf12b146100d757806320f99c0a146100ec578063295058ef1461013d575b600080fd5b6100ea6100e5366004610e26565b6102e4565b005b3480156100f857600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561014957600080fd5b506100ea610158366004610f24565b610438565b34801561016957600080fd5b506100ea610569565b34801561017e57600080fd5b506100ea61018d366004610f5d565b6105c5565b34801561019e57600080fd5b506100ea6101ad366004610f81565b610685565b3480156101be57600080fd5b506101137f000000000000000000000000000000000000000000000000000000000000000081565b3480156101f257600080fd5b50610206610201366004610fc2565b6106e1565b604051908152602001610134565b34801561022057600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16610113565b34801561024b57600080fd5b506102737f000000000000000000000000000000000000000000000000000000000000000081565b60405163ffffffff9091168152602001610134565b6100ea610296366004610fdb565b6107e2565b3480156102a757600080fd5b506101137f000000000000000000000000000000000000000000000000000000000000000081565b3480156102db57600080fd5b506100ea6108fc565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610353576040517f9de9741800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f3386774f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690633386774f9034906103f0907f000000000000000000000000000000000000000000000000000000000000000090879060009081908990600401611069565b60206040518083038185885af115801561040e573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061043391906110ff565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610489576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f3b1be67800000000000000000000000000000000000000000000000000000000815263ffffffff7f000000000000000000000000000000000000000000000000000000000000000016600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301528281166044830181905260648301527f00000000000000000000000000000000000000000000000000000000000000001690633b1be67890608401600060405180830381600087803b15801561054d57600080fd5b505af1158015610561573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146105ba576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105c333610b40565b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610616576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b60005473ffffffffffffffffffffffffffffffffffffffff1633146106d6576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610433838383610bb8565b604080517f9ae3f05a0000000000000000000000000000000000000000000000000000000081526004810183905260248101919091526000604482018190526064820181905263ffffffff7f00000000000000000000000000000000000000000000000000000000000000001660848301523060a4830152907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690639ae3f05a9060c401602060405180830381865afa1580156107b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107dc91906110ff565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610851576040517fc59f8f7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f10c56ed900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906310c56ed9906108c59085908590600401611118565b600060405180830381600087803b1580156108df57600080fd5b505af11580156108f3573d6000803e3d6000fd5b50505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461094d576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f6f38f87000000000000000000000000000000000000000000000000000000000815230600482015263ffffffff7f0000000000000000000000000000000000000000000000000000000000000000166024820152600090819073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001690636f38f8709060440160a060405180830381865afa158015610a08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2c9190611165565b50506040517f3b1be67800000000000000000000000000000000000000000000000000000000815263ffffffff7f00000000000000000000000000000000000000000000000000000000000000001660048201526000602482015273ffffffffffffffffffffffffffffffffffffffff808416604483015280831660648301529295509093507f00000000000000000000000000000000000000000000000000000000000000009091169150633b1be67890608401600060405180830381600087803b158015610afb57600080fd5b505af1158015610b0f573d6000803e3d6000fd5b50506040517fc2af098c82dba3c4b00be8bda596d62d13b98a87b42626fefa67e0bb0e198fdd925060009150a15050565b6000805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b73ffffffffffffffffffffffffffffffffffffffff8216610c05576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff841601610c4c576104338282610ca8565b8273ffffffffffffffffffffffffffffffffffffffff163b600003610c9d576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610433838383610d22565b600080600080600085875af1905080610433576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c45440000000000000000000000000060448201526064015b60405180910390fd5b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610df1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610d19565b50505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060408385031215610e3957600080fd5b82359150602083013567ffffffffffffffff80821115610e5857600080fd5b818501915085601f830112610e6c57600080fd5b813581811115610e7e57610e7e610df7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610ec457610ec4610df7565b81604052828152886020848701011115610edd57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b73ffffffffffffffffffffffffffffffffffffffff81168114610f2157600080fd5b50565b60008060408385031215610f3757600080fd5b8235610f4281610eff565b91506020830135610f5281610eff565b809150509250929050565b600060208284031215610f6f57600080fd5b8135610f7a81610eff565b9392505050565b600080600060608486031215610f9657600080fd5b8335610fa181610eff565b92506020840135610fb181610eff565b929592945050506040919091013590565b600060208284031215610fd457600080fd5b5035919050565b600080600060408486031215610ff057600080fd5b833563ffffffff8116811461100457600080fd5b9250602084013567ffffffffffffffff8082111561102157600080fd5b818601915086601f83011261103557600080fd5b81358181111561104457600080fd5b87602082850101111561105657600080fd5b6020830194508093505050509250925092565b63ffffffff8616815260006020868184015285604084015284606084015260a0608084015283518060a085015260005b818110156110b55785810183015185820160c001528201611099565b818111156110c757600060c083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160c001979650505050505050565b60006020828403121561111157600080fd5b5051919050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b600080600080600060a0868803121561117d57600080fd5b855161118881610eff565b602087015190955061119981610eff565b60408701519094506111aa81610eff565b60608701519093506111bb81610eff565b60808701519092506111cc81610eff565b80915050929550929590935056fea264697066735822122006a1fb966bfff403f9bba366f0146a90e289b79d0f8d1f08e7f3baf7f80025ca64736f6c634300080d0033000000000000000000000000b39df6bbb1cf2b609dee43f109cafeff1a7ccbea000000000000000000000000943ac2775928318653e91d350574436a1b9b16f90000000000000000000000000000000000000000000000000000000000001239
Deployed Bytecode
0x6080604052600436106100d25760003560e01c806377e031d91161007f578063bf965e5211610059578063bf965e521461023f578063c41f1f6c14610288578063c6a261d21461029b578063d9374bff146102cf57600080fd5b806377e031d9146101b257806389c1cf9a146101e65780638da5cb5b1461021457600080fd5b80633bd1adec116100b05780633bd1adec1461015d5780635b94db27146101725780636ccae0541461019257600080fd5b80631cbdf12b146100d757806320f99c0a146100ec578063295058ef1461013d575b600080fd5b6100ea6100e5366004610e26565b6102e4565b005b3480156100f857600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff165b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b34801561014957600080fd5b506100ea610158366004610f24565b610438565b34801561016957600080fd5b506100ea610569565b34801561017e57600080fd5b506100ea61018d366004610f5d565b6105c5565b34801561019e57600080fd5b506100ea6101ad366004610f81565b610685565b3480156101be57600080fd5b506101137f000000000000000000000000b39df6bbb1cf2b609dee43f109cafeff1a7ccbea81565b3480156101f257600080fd5b50610206610201366004610fc2565b6106e1565b604051908152602001610134565b34801561022057600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff16610113565b34801561024b57600080fd5b506102737f000000000000000000000000000000000000000000000000000000000000123981565b60405163ffffffff9091168152602001610134565b6100ea610296366004610fdb565b6107e2565b3480156102a757600080fd5b506101137f000000000000000000000000943ac2775928318653e91d350574436a1b9b16f981565b3480156102db57600080fd5b506100ea6108fc565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b39df6bbb1cf2b609dee43f109cafeff1a7ccbea1614610353576040517f9de9741800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f3386774f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000943ac2775928318653e91d350574436a1b9b16f91690633386774f9034906103f0907f000000000000000000000000000000000000000000000000000000000000123990879060009081908990600401611069565b60206040518083038185885af115801561040e573d6000803e3d6000fd5b50505050506040513d601f19601f8201168201806040525081019061043391906110ff565b505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314610489576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f3b1be67800000000000000000000000000000000000000000000000000000000815263ffffffff7f000000000000000000000000000000000000000000000000000000000000123916600482015273ffffffffffffffffffffffffffffffffffffffff83811660248301528281166044830181905260648301527f000000000000000000000000943ac2775928318653e91d350574436a1b9b16f91690633b1be67890608401600060405180830381600087803b15801561054d57600080fd5b505af1158015610561573d6000803e3d6000fd5b505050505050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146105ba576040517f7c91ccdd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6105c333610b40565b565b60005473ffffffffffffffffffffffffffffffffffffffff163314610616576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040517f906a1c6bd7e3091ea86693dd029a831c19049ce77f1dce2ce0bab1cacbabce2290600090a250565b60005473ffffffffffffffffffffffffffffffffffffffff1633146106d6576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610433838383610bb8565b604080517f9ae3f05a0000000000000000000000000000000000000000000000000000000081526004810183905260248101919091526000604482018190526064820181905263ffffffff7f00000000000000000000000000000000000000000000000000000000000012391660848301523060a4830152907f000000000000000000000000943ac2775928318653e91d350574436a1b9b16f973ffffffffffffffffffffffffffffffffffffffff1690639ae3f05a9060c401602060405180830381865afa1580156107b8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107dc91906110ff565b92915050565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000943ac2775928318653e91d350574436a1b9b16f91614610851576040517fc59f8f7c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f10c56ed900000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000b39df6bbb1cf2b609dee43f109cafeff1a7ccbea16906310c56ed9906108c59085908590600401611118565b600060405180830381600087803b1580156108df57600080fd5b505af11580156108f3573d6000803e3d6000fd5b50505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff16331461094d576040517f5fc483c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f6f38f87000000000000000000000000000000000000000000000000000000000815230600482015263ffffffff7f0000000000000000000000000000000000000000000000000000000000001239166024820152600090819073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000943ac2775928318653e91d350574436a1b9b16f91690636f38f8709060440160a060405180830381865afa158015610a08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a2c9190611165565b50506040517f3b1be67800000000000000000000000000000000000000000000000000000000815263ffffffff7f00000000000000000000000000000000000000000000000000000000000012391660048201526000602482015273ffffffffffffffffffffffffffffffffffffffff808416604483015280831660648301529295509093507f000000000000000000000000943ac2775928318653e91d350574436a1b9b16f99091169150633b1be67890608401600060405180830381600087803b158015610afb57600080fd5b505af1158015610b0f573d6000803e3d6000fd5b50506040517fc2af098c82dba3c4b00be8bda596d62d13b98a87b42626fefa67e0bb0e198fdd925060009150a15050565b6000805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff0000000000000000000000000000000000000000918216811783556001805490921690915560405190917ffbe19c9b601f5ee90b44c7390f3fa2319eba01762d34ee372aeafd59b25c7f8791a250565b73ffffffffffffffffffffffffffffffffffffffff8216610c05576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7fffffffffffffffffffffffff111111111111111111111111111111111111111273ffffffffffffffffffffffffffffffffffffffff841601610c4c576104338282610ca8565b8273ffffffffffffffffffffffffffffffffffffffff163b600003610c9d576040517f1eb00b0600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610433838383610d22565b600080600080600085875af1905080610433576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f4554485f5452414e534645525f4641494c45440000000000000000000000000060448201526064015b60405180910390fd5b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84166004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080610df1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401610d19565b50505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060408385031215610e3957600080fd5b82359150602083013567ffffffffffffffff80821115610e5857600080fd5b818501915085601f830112610e6c57600080fd5b813581811115610e7e57610e7e610df7565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715610ec457610ec4610df7565b81604052828152886020848701011115610edd57600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b73ffffffffffffffffffffffffffffffffffffffff81168114610f2157600080fd5b50565b60008060408385031215610f3757600080fd5b8235610f4281610eff565b91506020830135610f5281610eff565b809150509250929050565b600060208284031215610f6f57600080fd5b8135610f7a81610eff565b9392505050565b600080600060608486031215610f9657600080fd5b8335610fa181610eff565b92506020840135610fb181610eff565b929592945050506040919091013590565b600060208284031215610fd457600080fd5b5035919050565b600080600060408486031215610ff057600080fd5b833563ffffffff8116811461100457600080fd5b9250602084013567ffffffffffffffff8082111561102157600080fd5b818601915086601f83011261103557600080fd5b81358181111561104457600080fd5b87602082850101111561105657600080fd5b6020830194508093505050509250925092565b63ffffffff8616815260006020868184015285604084015284606084015260a0608084015283518060a085015260005b818110156110b55785810183015185820160c001528201611099565b818111156110c757600060c083870101525b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160c001979650505050505050565b60006020828403121561111157600080fd5b5051919050565b60208152816020820152818360408301376000818301604090810191909152601f9092017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0160101919050565b600080600080600060a0868803121561117d57600080fd5b855161118881610eff565b602087015190955061119981610eff565b60408701519094506111aa81610eff565b60608701519093506111bb81610eff565b60808701519092506111cc81610eff565b80915050929550929590935056fea264697066735822122006a1fb966bfff403f9bba366f0146a90e289b79d0f8d1f08e7f3baf7f80025ca64736f6c634300080d0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000b39df6bbb1cf2b609dee43f109cafeff1a7ccbea000000000000000000000000943ac2775928318653e91d350574436a1b9b16f90000000000000000000000000000000000000000000000000000000000001239
-----Decoded View---------------
Arg [0] : hub_ (address): 0xB39DF6BBB1Cf2B609DeE43F109caFEFF1A7CCBEa
Arg [1] : socket_ (address): 0x943AC2775928318653e91d350574436A1b9b16f9
Arg [2] : siblingChainSlug_ (uint32): 4665
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000b39df6bbb1cf2b609dee43f109cafeff1a7ccbea
Arg [1] : 000000000000000000000000943ac2775928318653e91d350574436a1b9b16f9
Arg [2] : 0000000000000000000000000000000000000000000000000000000000001239
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.