Source Code
Latest 1 from a total of 1 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Send Token | 17841140 | 314 days ago | IN | 0.001 FRAX | 0.0000001 |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LeafRestrictedTokenBridge
Compiler Version
v0.8.27+commit.40a35a09
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.19 <0.9.0;
import {EnumerableSet} from "@openzeppelin5/contracts/utils/structs/EnumerableSet.sol";
import {TypeCasts} from "@hyperlane/core/contracts/libs/TypeCasts.sol";
import {SafeERC20} from "@openzeppelin5/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin5/contracts/token/ERC20/IERC20.sol";
import {IHLHandler} from "../interfaces/bridge/hyperlane/IHLHandler.sol";
import {ILeafVoter} from "../interfaces/voter/ILeafVoter.sol";
import {IReward} from "../interfaces/rewards/IReward.sol";
import {IXERC20} from "../interfaces/xerc20/IXERC20.sol";
import {IVoter} from "../interfaces/external/IVoter.sol";
import {ILeafRestrictedTokenBridge} from "../interfaces/bridge/ILeafRestrictedTokenBridge.sol";
import {LeafTokenBridge, BaseTokenBridge, ITokenBridge} from "./LeafTokenBridge.sol";
import {Commands} from "../libraries/Commands.sol";
/// @title Velodrome Superchain Leaf Restricted Token Bridge
/// @notice Token Bridge for Restricted XERC20 tokens on leaf chains
contract LeafRestrictedTokenBridge is LeafTokenBridge, ILeafRestrictedTokenBridge {
using EnumerableSet for EnumerableSet.UintSet;
using Commands for bytes;
using SafeERC20 for IERC20;
/// @inheritdoc ILeafRestrictedTokenBridge
uint256 public constant BASE_CHAIN_ID = 8453;
/// @inheritdoc ILeafRestrictedTokenBridge
address public immutable voter;
constructor(address _owner, address _xerc20, address _mailbox, address _ism, address _voter)
LeafTokenBridge(_owner, _xerc20, _mailbox, _ism)
{
voter = _voter;
}
/// @inheritdoc IHLHandler
function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external payable override {
if (msg.sender != mailbox) revert NotMailbox();
if (_sender != TypeCasts.addressToBytes32(address(this))) revert NotBridge();
if (!_chainids.contains({value: _origin})) revert NotRegistered();
(address recipient, uint256 amount) = _message.recipientAndAmount();
address incentiveReward = block.chainid == BASE_CHAIN_ID
? IVoter(voter).gaugeToBribe({_gauge: recipient})
: ILeafVoter(voter).gaugeToIncentive({_gauge: recipient});
if (incentiveReward != address(0)) {
IXERC20(xerc20).mint({_user: address(this), _amount: amount});
IERC20(xerc20).safeIncreaseAllowance({spender: incentiveReward, value: amount});
IReward(incentiveReward).notifyRewardAmount({token: xerc20, amount: amount});
} else {
// should only be reachable on chain id 8453
IXERC20(xerc20).mint({_user: recipient, _amount: amount});
}
emit ReceivedMessage({_origin: _origin, _sender: _sender, _value: msg.value, _message: string(_message)});
}
/// @inheritdoc ITokenBridge
function GAS_LIMIT() public pure override(BaseTokenBridge, ITokenBridge) returns (uint256) {
return 190_000;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
library TypeCasts {
// alignment preserving cast
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
// alignment preserving cast
function bytes32ToAddress(bytes32 _buf) internal pure returns (address) {
return address(uint160(uint256(_buf)));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IMessageRecipient} from "@hyperlane/core/contracts/interfaces/IMessageRecipient.sol";
interface IHLHandler is IMessageRecipient {
error NotMailbox();
error NotRoot();
event ReceivedMessage(uint32 indexed _origin, bytes32 indexed _sender, uint256 _value, string _message);
/// @notice Callback function used by the mailbox contract to handle incoming messages
/// @param _origin The domain from which the message originates
/// @param _sender The address of the sender of the message
/// @param _message The message payload
function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external payable override;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ILeafVoter {
error NotAGauge();
error ZeroAddress();
error NotAuthorized();
error GaugeAlreadyKilled();
error GaugeAlreadyRevived();
event GaugeCreated(
address indexed poolFactory,
address indexed votingRewardsFactory,
address indexed gaugeFactory,
address pool,
address incentiveVotingReward,
address feeVotingReward,
address gauge
);
event GaugeKilled(address indexed gauge);
event GaugeRevived(address indexed gauge);
event WhitelistToken(address indexed token, bool indexed _bool);
/// @notice Address of bridge contract used to forward x-chain messages
function bridge() external view returns (address);
/// @dev Pool => Gauge
function gauges(address _pool) external view returns (address);
/// @dev Gauge => Pool
function poolForGauge(address _gauge) external view returns (address);
/// @dev Gauge => Fees Voting Reward
function gaugeToFees(address _gauge) external view returns (address);
/// @dev Gauge => Incentives Voting Reward
function gaugeToIncentive(address _gauge) external view returns (address);
/// @notice Check if a given address is a gauge
/// @param _gauge The address to be checked
/// @return Whether the address is a gauge or not
function isGauge(address _gauge) external view returns (bool);
/// @notice Check if a given gauge is alive
/// @param _gauge The address of the gauge to be checked
/// @return Whether the gauge is alive or not
function isAlive(address _gauge) external view returns (bool);
/// @notice Returns the number of times a token has been whitelisted
/// @param _token Address of token to view whitelist count
/// @return Number of times token has been whitelisted
function whitelistTokenCount(address _token) external view returns (uint256);
/// @notice Get all Whitelisted Tokens approved by the Voter
/// @return Array of Whitelisted Token addresses
function whitelistedTokens() external view returns (address[] memory);
/// @notice Paginated view of all Whitelisted Tokens
/// @dev Should not assume the last Token returned is at index matching given `_end`,
/// because if `_end` exceeds `length`, implementation defaults to `length`
/// @param _start Index of first Token to be fetched
/// @param _end End index for pagination
/// @return _tokens Array of whitelisted tokens
function whitelistedTokens(uint256 _start, uint256 _end) external view returns (address[] memory _tokens);
/// @notice Check if a given token is whitelisted
/// @param _token The address of the token to be checked
/// @return Whether the token is whitelisted or not
function isWhitelistedToken(address _token) external view returns (bool);
/// @notice Get the length of the whitelistedTokens array
function whitelistedTokensLength() external view returns (uint256);
/// @notice Create a new gauge
/// @dev Only callable by Message Bridge
/// @param _poolFactory .
/// @param _pool .
/// @param _votingRewardsFactory .
/// @param _gaugeFactory .
function createGauge(address _poolFactory, address _pool, address _votingRewardsFactory, address _gaugeFactory)
external
returns (address _gauge);
/// @notice Kills a gauge. The gauge will not receive any new emissions and cannot be deposited into.
/// Can still withdraw from gauge.
/// @dev Only callable by Message Bridge
/// Throws if gauge already killed.
/// @param _gauge .
function killGauge(address _gauge) external;
/// @notice Revives a killed gauge. Gauge will be able to receive emissions and deposits again.
/// @dev Only callable by Message Bridge
/// Throws if gauge is not killed.
/// @param _gauge .
function reviveGauge(address _gauge) external;
/// @notice Claim emissions from gauges.
/// @param _gauges Array of gauges to collect emissions from.
function claimRewards(address[] memory _gauges) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IReward {
error InvalidReward();
error NotAuthorized();
error NotGauge();
error NotEscrowToken();
error NotSingleToken();
error NotVotingEscrow();
error NotWhitelisted();
error ZeroAmount();
event Deposit(uint256 indexed _tokenId, uint256 _amount);
event Withdraw(uint256 indexed _tokenId, uint256 _amount);
event NotifyReward(address indexed _sender, address indexed _reward, uint256 indexed _epoch, uint256 _amount);
event ClaimRewards(address indexed _sender, address indexed _reward, uint256 _amount);
/// @notice A checkpoint for marking balance
struct Checkpoint {
uint256 timestamp;
uint256 balanceOf;
}
/// @notice A checkpoint for marking supply
struct SupplyCheckpoint {
uint256 timestamp;
uint256 supply;
}
/// @notice Epoch duration constant (7 days)
function DURATION() external view returns (uint256);
/// @notice Address of LeafVoter.sol
function voter() external view returns (address);
/// @dev Address which has permission to externally call _deposit() & _withdraw()
function authorized() external view returns (address);
/// @notice Total amount currently deposited via _deposit()
function totalSupply() external view returns (uint256);
/// @notice Current amount deposited by tokenId
function balanceOf(uint256 tokenId) external view returns (uint256);
/// @notice Amount of tokens to reward depositors for a given epoch
/// @param token Address of token to reward
/// @param epochStart Startime of rewards epoch
/// @return Amount of token
function tokenRewardsPerEpoch(address token, uint256 epochStart) external view returns (uint256);
/// @notice Most recent timestamp a veNFT has claimed their rewards
/// @param token Address of token rewarded
/// @param tokenId veNFT unique identifier
/// @return Timestamp
function lastEarn(address token, uint256 tokenId) external view returns (uint256);
/// @notice List of reward tokens
/// @param _index Index of reward token
/// @return Address of reward token
function rewards(uint256 _index) external view returns (address);
/// @notice True if a token is or has been an active reward token, else false
function isReward(address token) external view returns (bool);
/// @notice The number of checkpoints for each tokenId deposited
function numCheckpoints(uint256 tokenId) external view returns (uint256);
/// @notice The total number of checkpoints
function supplyNumCheckpoints() external view returns (uint256);
/// @notice Deposit an amount into the rewards contract to earn future rewards associated to a veNFT
/// @dev Internal notation used as only callable internally by `authorized.module()`.
/// @param amount Vote weight to deposit
/// @param tokenId Token ID of weight to deposit
/// @param timestamp Timestamp of deposit
function _deposit(uint256 amount, uint256 tokenId, uint256 timestamp) external;
/// @notice Withdraw an amount from the rewards contract associated to a veNFT
/// @dev Internal notation used as only callable internally by `authorized.module()`.
/// @param amount Vote weight to withdraw
/// @param tokenId Token ID of weight to withdraw
/// @param timestamp Timestamp of withdraw
function _withdraw(uint256 amount, uint256 tokenId, uint256 timestamp) external;
/// @notice Claim the rewards earned by a veNFT staker
/// @param _recipient Address of reward recipient
/// @param _tokenId Unique identifier of the veNFT
/// @param _tokens Array of tokens to claim rewards of
function getReward(address _recipient, uint256 _tokenId, address[] memory _tokens) external;
/// @notice Add rewards for stakers to earn
/// @param token Address of token to reward
/// @param amount Amount of token to transfer to rewards
function notifyRewardAmount(address token, uint256 amount) external;
/// @notice Determine the prior balance for an account as of a block number
/// @dev Block number must be a finalized block or else this function will revert to prevent misinformation.
/// @param tokenId The token of the NFT to check
/// @param timestamp The timestamp to get the balance at
/// @return The balance the account had as of the given block
function getPriorBalanceIndex(uint256 tokenId, uint256 timestamp) external view returns (uint256);
/// @notice Determine the prior index of supply staked by of a timestamp
/// @dev Timestamp must be <= current timestamp
/// @param timestamp The timestamp to get the index at
/// @return Index of supply checkpoint
function getPriorSupplyIndex(uint256 timestamp) external view returns (uint256);
/// @notice Get number of rewards tokens
function rewardsListLength() external view returns (uint256);
/// @notice Calculate how much in rewards are earned for a specific token and veNFT
/// @param token Address of token to fetch rewards of
/// @param tokenId Unique identifier of the veNFT
/// @return Amount of token earned in rewards
function earned(address token, uint256 tokenId) external view returns (uint256);
function checkpoints(uint256 tokenId, uint256 index) external view returns (uint256 timestamp, uint256 balanceOf);
function supplyCheckpoints(uint256 index) external view returns (uint256 timestamp, uint256 supply);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../../xerc20/MintLimits.sol";
import {RateLimitMidPoint} from "../../libraries/rateLimits/RateLimitMidpointCommonLibrary.sol";
interface IXERC20 {
/// @notice Emits when a limit is set
/// @param _bridge The address of the bridge we are setting the limit to
/// @param _bufferCap The updated buffer cap for the bridge
event BridgeLimitsSet(address indexed _bridge, uint256 _bufferCap);
/// @notice The address of the lockbox contract
function lockbox() external view returns (address);
/// @notice Maps bridge address to bridge rate limits
/// @param _bridge The bridge we are viewing the limits of
/// @return _rateLimit The limits of the bridge
function rateLimits(address _bridge) external view returns (RateLimitMidPoint memory _rateLimit);
/// @notice Returns the max limit of a bridge
/// @param _bridge The bridge we are viewing the limits of
/// @return _limit The limit the bridge has
function mintingMaxLimitOf(address _bridge) external view returns (uint256 _limit);
/// @notice Returns the max limit of a bridge
/// @param _bridge the bridge we are viewing the limits of
/// @return _limit The limit the bridge has
function burningMaxLimitOf(address _bridge) external view returns (uint256 _limit);
/// @notice Returns the current limit of a bridge
/// @param _bridge The bridge we are viewing the limits of
/// @return _limit The limit the bridge has
function mintingCurrentLimitOf(address _bridge) external view returns (uint256 _limit);
/// @notice Returns the current limit of a bridge
/// @param _bridge the bridge we are viewing the limits of
/// @return _limit The limit the bridge has
function burningCurrentLimitOf(address _bridge) external view returns (uint256 _limit);
/// @notice Mints tokens for a user
/// @dev Can only be called by a bridge
/// @param _user The address of the user who needs tokens minted
/// @param _amount The amount of tokens being minted
function mint(address _user, uint256 _amount) external;
/// @notice Burns tokens for a user
/// @dev Can only be called by a bridge
/// @param _user The address of the user who needs tokens burned
/// @param _amount The amount of tokens being burned
function burn(address _user, uint256 _amount) external;
/// @notice Conform to the xERC20 setLimits interface
/// @dev Can only be called if the bridge already has a buffer cap
/// @param _bridge The bridge we are setting the limits of
/// @param _newBufferCap The new buffer cap, uint112 max for unlimited
function setBufferCap(address _bridge, uint256 _newBufferCap) external;
/// @notice Sets rate limit per second for a bridge
/// @dev Can only be called if the bridge already has a buffer cap
/// @param _bridge The bridge we are setting the limits of
/// @param _newRateLimitPerSecond The new rate limit per second
function setRateLimitPerSecond(address _bridge, uint128 _newRateLimitPerSecond) external;
/// @notice Adds a new bridge to the currently active bridges
/// @param _newBridge The bridge to add
function addBridge(MintLimits.RateLimitMidPointInfo memory _newBridge) external;
/// @notice Removes a bridge from the currently active bridges
/// deleting its buffer stored, buffer cap, mid point and last
/// buffer used time
/// @param _bridge The bridge to remove
function removeBridge(address _bridge) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IFactoryRegistry} from "src/interfaces/external/IFactoryRegistry.sol";
interface IVoter {
error AlreadyVotedOrDeposited();
error DistributeWindow();
error GaugeAlreadyKilled();
error GaugeAlreadyRevived();
error NotEmergencyCouncil();
error ZeroAddress();
event GaugeKilled(address indexed gauge);
event GaugeRevived(address indexed gauge);
/// @dev Gauge => Amount claimable
function claimable(address gauge) external view returns (uint256);
/// @notice Address of Minter.sol
function minter() external view returns (address);
/// @notice Set new emergency council.
/// @dev Throws if not called by emergency council.
/// @param _emergencyCouncil .
function setEmergencyCouncil(address _emergencyCouncil) external;
function vote(uint256 _tokenId, address[] calldata _poolVote, uint256[] calldata _weights) external;
function gauges(address _pool) external view returns (address);
function isGauge(address _gauge) external view returns (bool);
function gaugeToFees(address _gauge) external view returns (address);
function gaugeToBribe(address _gauge) external view returns (address);
function poolForGauge(address _gauge) external view returns (address);
/// @dev Nft => Timestamp of last vote (ensures single vote per epoch)
function lastVoted(uint256 tokenId) external view returns (uint256);
function createGauge(address _poolFactory, address _pool) external returns (address);
function factoryRegistry() external view returns (address);
/// @dev Utility to distribute to gauges of pools in array.
/// @param _gauges Array of gauges to distribute to.
function distribute(address[] memory _gauges) external;
function isAlive(address _gauge) external view returns (bool);
function killGauge(address _gauge) external;
/// @notice Revives a killed gauge. Gauge will can receive emissions and deposits again.
/// @dev Throws if not called by emergency council.
/// Throws if gauge is not killed.
/// @param _gauge .
function reviveGauge(address _gauge) external;
function isWhitelistedToken(address _token) external view returns (bool);
function emergencyCouncil() external view returns (address);
function ve() external view returns (address);
/// @notice Claim emissions from gauges.
/// @param _gauges Array of gauges to collect emissions from.
// function claimRewards(address[] memory _gauges) external;
/// @notice Claim fees for a given NFT.
/// @dev Utility to help batch fee claims.
/// @param _fees Array of FeesVotingReward contracts to collect from.
/// @param _tokens Array of tokens that are used as fees.
/// @param _tokenId Id of veNFT that you wish to claim fees for.
function claimFees(address[] memory _fees, address[][] memory _tokens, uint256 _tokenId) external;
/// @notice Called by users to update voting balances in voting rewards contracts.
/// @param _tokenId Id of veNFT whose balance you wish to update.
function poke(uint256 _tokenId) external;
/// @notice Called by users to reset voting state. Required if you wish to make changes to
/// veNFT state (e.g. merge, split, deposit into managed etc).
/// Cannot reset in the same epoch that you voted in.
/// Can vote or deposit into a managed NFT again after reset.
/// @param _tokenId Id of veNFT you are reseting.
function reset(uint256 _tokenId) external;
/// @notice Standard OZ IGovernor using ve for vote weights.
function governor() external view returns (address);
/// @notice Whitelist (or unwhitelist) token for use in incentives.
/// @dev Throws if not called by governor.
/// @param _token .
/// @param _bool .
function whitelistToken(address _token, bool _bool) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ITokenBridge} from "./ITokenBridge.sol";
interface ILeafRestrictedTokenBridge is ITokenBridge {
/// @notice Chain id for Base network
/// @return The Base chain id (8453)
function BASE_CHAIN_ID() external view returns (uint256);
/// @notice Address of the voter contract
/// @return The address of the voter contract
function voter() external view returns (address);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.19 <0.9.0;
import {EnumerableSet} from "@openzeppelin5/contracts/utils/structs/EnumerableSet.sol";
import {IPostDispatchHook} from "@hyperlane/core/contracts/interfaces/hooks/IPostDispatchHook.sol";
import {TypeCasts} from "@hyperlane/core/contracts/libs/TypeCasts.sol";
import {Mailbox} from "@hyperlane/core/contracts/Mailbox.sol";
import {IHLHandler} from "../interfaces/bridge/hyperlane/IHLHandler.sol";
import {ITokenBridge} from "../interfaces/bridge/ITokenBridge.sol";
import {IXERC20} from "../interfaces/xerc20/IXERC20.sol";
import {BaseTokenBridge} from "./BaseTokenBridge.sol";
import {Commands} from "../libraries/Commands.sol";
/*
██╗ ██╗███████╗██╗ ██████╗ ██████╗ ██████╗ ██████╗ ███╗ ███╗███████╗
██║ ██║██╔════╝██║ ██╔═══██╗██╔══██╗██╔══██╗██╔═══██╗████╗ ████║██╔════╝
██║ ██║█████╗ ██║ ██║ ██║██║ ██║██████╔╝██║ ██║██╔████╔██║█████╗
╚██╗ ██╔╝██╔══╝ ██║ ██║ ██║██║ ██║██╔══██╗██║ ██║██║╚██╔╝██║██╔══╝
╚████╔╝ ███████╗███████╗╚██████╔╝██████╔╝██║ ██║╚██████╔╝██║ ╚═╝ ██║███████╗
╚═══╝ ╚══════╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
███████╗██╗ ██╗██████╗ ███████╗██████╗ ██████╗██╗ ██╗ █████╗ ██╗███╗ ██╗
██╔════╝██║ ██║██╔══██╗██╔════╝██╔══██╗██╔════╝██║ ██║██╔══██╗██║████╗ ██║
███████╗██║ ██║██████╔╝█████╗ ██████╔╝██║ ███████║███████║██║██╔██╗ ██║
╚════██║██║ ██║██╔═══╝ ██╔══╝ ██╔══██╗██║ ██╔══██║██╔══██║██║██║╚██╗██║
███████║╚██████╔╝██║ ███████╗██║ ██║╚██████╗██║ ██║██║ ██║██║██║ ╚████║
╚══════╝ ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝
██╗ ███████╗ █████╗ ███████╗████████╗ ██████╗ ██╗ ██╗███████╗███╗ ██╗██████╗ ██████╗ ██╗██████╗ ██████╗ ███████╗
██║ ██╔════╝██╔══██╗██╔════╝╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗ ██║██╔══██╗██╔══██╗██║██╔══██╗██╔════╝ ██╔════╝
██║ █████╗ ███████║█████╗ ██║ ██║ ██║█████╔╝ █████╗ ██╔██╗ ██║██████╔╝██████╔╝██║██║ ██║██║ ███╗█████╗
██║ ██╔══╝ ██╔══██║██╔══╝ ██║ ██║ ██║██╔═██╗ ██╔══╝ ██║╚██╗██║██╔══██╗██╔══██╗██║██║ ██║██║ ██║██╔══╝
███████╗███████╗██║ ██║██║ ██║ ╚██████╔╝██║ ██╗███████╗██║ ╚████║██████╔╝██║ ██║██║██████╔╝╚██████╔╝███████╗
╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═╝╚═╝╚═════╝ ╚═════╝ ╚══════╝
*/
/// @title Velodrome Superchain Leaf Token Bridge
/// @notice General Purpose Leaf Token Bridge
contract LeafTokenBridge is BaseTokenBridge {
using EnumerableSet for EnumerableSet.UintSet;
using Commands for bytes;
constructor(address _owner, address _xerc20, address _mailbox, address _ism)
BaseTokenBridge(_owner, _xerc20, _mailbox, _ism)
{}
/// @inheritdoc ITokenBridge
function sendToken(address _recipient, uint256 _amount, uint256 _chainid) external payable virtual override {
bytes memory message = abi.encodePacked(_recipient, _amount);
_send({_amount: _amount, _recipient: _recipient, _chainid: _chainid, _message: message});
}
/// @inheritdoc IHLHandler
function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external payable virtual override {
if (msg.sender != mailbox) revert NotMailbox();
if (_sender != TypeCasts.addressToBytes32(address(this))) revert NotBridge();
if (!_chainids.contains({value: _origin})) revert NotRegistered();
(address recipient, uint256 amount) = _message.recipientAndAmount();
IXERC20(xerc20).mint({_user: recipient, _amount: amount});
emit ReceivedMessage({_origin: _origin, _sender: _sender, _value: msg.value, _message: string(_message)});
}
function _send(uint256 _amount, address _recipient, uint256 _chainid, bytes memory _message) internal {
if (_amount == 0) revert ZeroAmount();
if (_recipient == address(0)) revert ZeroAddress();
if (!_chainids.contains({value: _chainid})) revert NotRegistered();
address _hook = hook;
bytes memory metadata = _generateGasMetadata({_hook: _hook, _value: msg.value, _message: _message});
uint32 domain = uint32(_chainid);
uint256 fee = Mailbox(mailbox).quoteDispatch({
destinationDomain: domain,
recipientAddress: TypeCasts.addressToBytes32(address(this)),
messageBody: _message,
metadata: metadata,
hook: IPostDispatchHook(_hook)
});
if (fee > msg.value) revert InsufficientBalance();
IXERC20(xerc20).burn({_user: msg.sender, _amount: _amount});
Mailbox(mailbox).dispatch{value: fee}({
destinationDomain: domain,
recipientAddress: TypeCasts.addressToBytes32(address(this)),
messageBody: _message,
metadata: metadata,
hook: IPostDispatchHook(_hook)
});
uint256 leftover = msg.value - fee;
if (leftover > 0) payable(msg.sender).transfer(leftover);
emit SentMessage({
_destination: domain,
_recipient: TypeCasts.addressToBytes32(address(this)),
_value: fee,
_message: string(_message),
_metadata: string(metadata)
});
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.19 <0.9.0;
/// @notice Commands for x-chain interactions
/// @dev Existing commands cannot be modified but new commands can be added
library Commands {
uint256 public constant NOTIFY = 0x00;
uint256 public constant NOTIFY_WITHOUT_CLAIM = 0x01;
uint256 public constant GET_INCENTIVES = 0x02;
uint256 public constant GET_FEES = 0x03;
uint256 public constant DEPOSIT = 0x04;
uint256 public constant WITHDRAW = 0x05;
uint256 public constant CREATE_GAUGE = 0x06;
uint256 public constant KILL_GAUGE = 0x07;
uint256 public constant REVIVE_GAUGE = 0x08;
uint256 private constant COMMAND_OFFSET = 0;
uint256 private constant ADDRESS_OFFSET = 1;
/// @dev Second and Third offset are used in messages with multiple consecutive addresses
uint256 private constant SECOND_OFFSET = ADDRESS_OFFSET + 20;
uint256 private constant THIRD_OFFSET = SECOND_OFFSET + 20;
// Offsets for Create Gauge Command
uint256 private constant TOKEN0_OFFSET = THIRD_OFFSET + 20;
uint256 private constant TOKEN1_OFFSET = TOKEN0_OFFSET + 20;
uint256 private constant POOL_PARAM_OFFSET = TOKEN1_OFFSET + 20;
// Offsets for Reward Claims
uint256 private constant LENGTH_OFFSET = THIRD_OFFSET + 32;
uint256 private constant TOKENS_OFFSET = LENGTH_OFFSET + 1;
// Offset for Deposit/Withdraw
uint256 private constant TOKEN_ID_OFFSET = ADDRESS_OFFSET + 20 + 32;
uint256 private constant TIMESTAMP_OFFSET = TOKEN_ID_OFFSET + 32;
// Offset for Send Token
uint256 private constant AMOUNT_OFFSET = COMMAND_OFFSET + 20;
uint256 private constant TOKEN_ID_WITHOUT_COMMAND_OFFSET = AMOUNT_OFFSET + 32;
/// @notice Returns the command encoded in the message
/// @dev Assumes message is encoded as (command, ...)
/// @param _message The message to be decoded
function command(bytes calldata _message) internal pure returns (uint256) {
return uint256(uint8(bytes1(_message[COMMAND_OFFSET:COMMAND_OFFSET + 1])));
}
/// @notice Returns the address encoded in the message
/// @dev Assumes message is encoded as (command, address, ...)
/// @param _message The message to be decoded
function toAddress(bytes calldata _message) internal pure returns (address) {
return address(bytes20(_message[ADDRESS_OFFSET:ADDRESS_OFFSET + 20]));
}
/// @notice Returns the message without the encoded command
/// @dev Assumes message is encoded as (command, message)
/// @param _message The message to be decoded
function messageWithoutCommand(bytes calldata _message) internal pure returns (bytes calldata) {
return bytes(_message[COMMAND_OFFSET + 1:]);
}
/// @notice Returns the amount encoded in the message
/// @dev Assumes message is encoded as (command, amount, ...)
/// @param _message The message to be decoded
function amount(bytes calldata _message) internal pure returns (uint256) {
return uint256(bytes32(_message[SECOND_OFFSET:SECOND_OFFSET + 32]));
}
/// @notice Returns the amount, tokenId and timestamp encoded in the message
/// @dev Assumes message is encoded as (command, amount, tokenId, timestamp, ...)
/// @param _message The message to be decoded
function voteParams(bytes calldata _message) internal pure returns (uint256, uint256, uint256) {
return (
uint256(bytes32(_message[SECOND_OFFSET:SECOND_OFFSET + 32])),
uint256(bytes32(_message[TOKEN_ID_OFFSET:TIMESTAMP_OFFSET])),
uint256(uint40(bytes5(_message[TIMESTAMP_OFFSET:TIMESTAMP_OFFSET + 5])))
);
}
/// @notice Returns the parameters necessary for gauge creation, encoded in the message
/// @dev Assumes message is encoded as (command, address, address, address, address, uint24)
/// @param _message The message to be decoded
function createGaugeParams(bytes calldata _message)
internal
pure
returns (address, address, address, address, address, uint24)
{
return (
address(bytes20(_message[ADDRESS_OFFSET:ADDRESS_OFFSET + 20])),
address(bytes20(_message[SECOND_OFFSET:SECOND_OFFSET + 20])),
address(bytes20(_message[THIRD_OFFSET:THIRD_OFFSET + 20])),
address(bytes20(_message[TOKEN0_OFFSET:TOKEN0_OFFSET + 20])),
address(bytes20(_message[TOKEN1_OFFSET:TOKEN1_OFFSET + 20])),
uint24(bytes3(_message[POOL_PARAM_OFFSET:POOL_PARAM_OFFSET + 3]))
);
}
/// @notice Returns the owner encoded in the message
/// @dev Assumes message is encoded as (command, address, owner, ...)
/// @param _message The message to be decoded
function owner(bytes calldata _message) internal pure returns (address) {
return address(bytes20(_message[SECOND_OFFSET:SECOND_OFFSET + 20]));
}
/// @notice Returns the tokenId encoded in a reward claiming message
/// @dev Assumes message is encoded as (command, address, tokenId, ...)
/// @param _message The message to be decoded
function tokenId(bytes calldata _message) internal pure returns (uint256) {
return uint256(bytes32(_message[THIRD_OFFSET:THIRD_OFFSET + 32]));
}
/// @notice Returns the token addresses encoded in the message
/// @dev Assumes message has length and token addresses encoded
/// @param _message The message to be decoded
function tokens(bytes calldata _message) internal pure returns (address[] memory _tokens) {
uint256 length = uint8(bytes1(_message[LENGTH_OFFSET:LENGTH_OFFSET + 1]));
_tokens = new address[](length);
for (uint256 i = 0; i < length; i++) {
_tokens[i] =
address(uint160(uint256(bytes32(_message[TOKENS_OFFSET + (i * 32):TOKENS_OFFSET + ((i + 1) * 32)]))));
}
}
// Token Bridge
// Send Token - (address, uint256)
uint256 public constant SEND_TOKEN_LENGTH = 52;
// Send Token and Lock - (address, uint256, uint256)
uint256 public constant SEND_TOKEN_AND_LOCK_LENGTH = 84;
/// @notice Returns the recipient and amount encoded in the message
/// @dev Assumes no command is encoded and message is encoded as (address, amount)
/// @param _message The message to be decoded
function recipientAndAmount(bytes calldata _message) internal pure returns (address, uint256) {
return (
address(bytes20(_message[COMMAND_OFFSET:COMMAND_OFFSET + 20])),
uint256(bytes32(_message[AMOUNT_OFFSET:AMOUNT_OFFSET + 32]))
);
}
/// @notice Returns the recipient, amount and tokenId encoded in the message
/// @dev Assumes no command is encoded and message is encoded as (address, amount, tokenId)
/// @param _message The message to be decoded
function sendTokenAndLockParams(bytes calldata _message) internal pure returns (address, uint256, uint256) {
return (
address(bytes20(_message[COMMAND_OFFSET:COMMAND_OFFSET + 20])),
uint256(bytes32(_message[AMOUNT_OFFSET:AMOUNT_OFFSET + 32])),
uint256(bytes32(_message[TOKEN_ID_WITHOUT_COMMAND_OFFSET:TOKEN_ID_WITHOUT_COMMAND_OFFSET + 32]))
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
interface IMessageRecipient {
function handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) external payable;
}// SPDX-License-Identifier: BSD-3.0
pragma solidity >=0.8.19 <0.9.0;
import {
RateLimitMidPoint,
RateLimitMidpointCommonLibrary
} from "../libraries/rateLimits/RateLimitMidpointCommonLibrary.sol";
import {RateLimitedMidpointLibrary} from "../libraries/rateLimits/RateLimitedMidpointLibrary.sol";
/// @dev Modified lightly from Zelt at commit 30b2ba0, with the following changes:
/// - Updated the Solidity compiler version used;
/// - Refactored the `_rateLimits` mapping to be internal;
/// - Removed internal `_addLimits(...)` & `_removeLimits(...)` helpers.
/// Can refer to: (https://github.com/solidity-labs-io/zelt/blob/30b2ba0352422471c03b233d55feddfbdba198a3/src/impl/MintLimits.sol)
abstract contract MintLimits {
using RateLimitMidpointCommonLibrary for RateLimitMidPoint;
using RateLimitedMidpointLibrary for RateLimitMidPoint;
/// @notice struct for initializing rate limit
struct RateLimitMidPointInfo {
/// @notice the buffer cap for this bridge
uint112 bufferCap;
/// @notice the rate limit per second for this bridge
uint128 rateLimitPerSecond;
/// @notice the bridge address
address bridge;
}
/// @notice rate limit for each bridge contract
mapping(address bridge => RateLimitMidPoint bridgeRateLimit) internal _rateLimits;
/// @notice emitted when a rate limit is added or removed
/// @param bridge the bridge address
/// @param bufferCap the new buffer cap for this bridge
/// @param rateLimitPerSecond the new rate limit per second for this bridge
event ConfigurationChanged(address indexed bridge, uint112 bufferCap, uint128 rateLimitPerSecond);
//// ------------------------------------------------------------
//// ------------------------------------------------------------
//// -------------------- View Functions ------------------------
//// ------------------------------------------------------------
//// ------------------------------------------------------------
/// @notice the amount of action used before hitting limit
/// @dev replenishes at rateLimitPerSecond per second up to bufferCap
function buffer(address from) public view returns (uint256) {
return _rateLimits[from].buffer();
}
/// @notice the cap of the buffer for this address
/// @param from address to get buffer cap for
function bufferCap(address from) public view returns (uint256) {
return _rateLimits[from].bufferCap;
}
/// @notice the amount the buffer replenishes towards the midpoint per second
/// @param from address to get rate limit for
function rateLimitPerSecond(address from) public view returns (uint256) {
return _rateLimits[from].rateLimitPerSecond;
}
//// ------------------------------------------------------------
//// ------------------------------------------------------------
//// -------------- Internal Helper Functions -------------------
//// ------------------------------------------------------------
//// ------------------------------------------------------------
//// ----------- Depleting and Replenishing Buffer --------------
/// @notice the method that enforces the rate limit.
/// Decreases buffer by "amount".
/// If buffer is <= amount, revert
/// @param amount to decrease buffer by
function _depleteBuffer(address from, uint256 amount) internal {
require(amount != 0, "MintLimits: deplete amount cannot be 0");
_rateLimits[from].depleteBuffer(amount);
}
/// @notice function to replenish buffer
/// @param from address to set rate limit for
/// @param amount to increase buffer by if under buffer cap
function _replenishBuffer(address from, uint256 amount) internal {
require(amount != 0, "MintLimits: replenish amount cannot be 0");
_rateLimits[from].replenishBuffer(amount);
}
//// -------------- Modifying Existing Limits -------------------
/// @notice function to set rate limit per second
/// @dev updates the current buffer and last buffer used time first,
/// then sets the new rate limit per second
/// @param from address to set rate limit for
/// @param newRateLimitPerSecond new rate limit per second
function _setRateLimitPerSecond(address from, uint128 newRateLimitPerSecond) internal {
require(newRateLimitPerSecond <= maxRateLimitPerSecond(), "MintLimits: rateLimitPerSecond too high");
require(_rateLimits[from].bufferCap != 0, "MintLimits: non-existent rate limit");
_rateLimits[from].setRateLimitPerSecond(newRateLimitPerSecond);
emit ConfigurationChanged(from, _rateLimits[from].bufferCap, newRateLimitPerSecond);
}
/// @notice function to set buffer cap
/// @dev updates the current buffer and last buffer used time first,
/// then sets the new buffer cap
/// @param from address to set the buffer cap for
/// @param newBufferCap new buffer cap
function _setBufferCap(address from, uint112 newBufferCap) internal {
require(newBufferCap != 0, "MintLimits: bufferCap cannot be 0");
require(_rateLimits[from].bufferCap != 0, "MintLimits: non-existent rate limit");
require(newBufferCap > minBufferCap(), "MintLimits: buffer cap below min");
_rateLimits[from].setBufferCap(newBufferCap);
emit ConfigurationChanged(from, newBufferCap, _rateLimits[from].rateLimitPerSecond);
}
//// -------------- Adding Limits -------------------
/// @notice add an individual rate limit
/// @param rateLimit cap on buffer size for this rate limited instance
function _addLimit(RateLimitMidPointInfo memory rateLimit) internal {
require(rateLimit.rateLimitPerSecond <= maxRateLimitPerSecond(), "MintLimits: rateLimitPerSecond too high");
require(rateLimit.bridge != address(0), "MintLimits: invalid bridge address");
require(_rateLimits[rateLimit.bridge].bufferCap == 0, "MintLimits: rate limit already exists");
require(rateLimit.bufferCap > minBufferCap(), "MintLimits: buffer cap below min");
_rateLimits[rateLimit.bridge] = RateLimitMidPoint({
bufferCap: rateLimit.bufferCap,
lastBufferUsedTime: uint32(block.timestamp),
bufferStored: uint112(rateLimit.bufferCap / 2),
midPoint: uint112(rateLimit.bufferCap / 2),
rateLimitPerSecond: rateLimit.rateLimitPerSecond
});
emit ConfigurationChanged(rateLimit.bridge, rateLimit.bufferCap, rateLimit.rateLimitPerSecond);
}
//// -------------- Removing Limits -------------------
/// @notice remove a bridge from the rate limit mapping, deleting all data
/// @param bridge the bridge address to remove
function _removeLimit(address bridge) internal {
require(_rateLimits[bridge].bufferCap != 0, "MintLimits: cannot remove non-existent rate limit");
delete _rateLimits[bridge];
emit ConfigurationChanged(bridge, 0, 0);
}
//// ------------------------------------------------------------
//// ------------------------------------------------------------
//// ---------------------- Virtual Function --------------------
//// ------------------------------------------------------------
//// ------------------------------------------------------------
/// @notice the maximum rate limit per second allowed in any bridge
/// must be overridden by child contract
function maxRateLimitPerSecond() public pure virtual returns (uint128);
/// @notice the minimum buffer cap, non inclusive
/// must be overridden by child contract
function minBufferCap() public pure virtual returns (uint112);
}// SPDX-License-Identifier: BSD-3.0
pragma solidity >=0.8.19 <0.9.0;
import {Math} from "@openzeppelin5/contracts/utils/math/Math.sol";
/// @notice two rate storage slots per rate limit
struct RateLimitMidPoint {
//// -------------------------------------------- ////
//// ------------------ SLOT 0 ------------------ ////
//// -------------------------------------------- ////
/// @notice the rate per second for this contract
uint128 rateLimitPerSecond;
/// @notice the cap of the buffer that can be used at once
uint112 bufferCap;
//// -------------------------------------------- ////
//// ------------------ SLOT 1 ------------------ ////
//// -------------------------------------------- ////
/// @notice the last time the buffer was used by the contract
uint32 lastBufferUsedTime;
/// @notice the buffer at the timestamp of lastBufferUsedTime
uint112 bufferStored;
/// @notice the mid point of the buffer
uint112 midPoint;
}
/// @title abstract contract for putting a rate limit on how fast a contract
/// can perform an action e.g. Minting
/// @author Elliot Friedman
/// @dev Modified lightly from Zelt at commit 30b2ba0 to update the Solidity Compiler version used
/// Can refer to: (https://github.com/solidity-labs-io/zelt/blob/30b2ba0352422471c03b233d55feddfbdba198a3/src/lib/RateLimitMidpointCommonLibrary.sol)
library RateLimitMidpointCommonLibrary {
/// @notice event emitted when buffer cap is updated
event BufferCapUpdate(uint256 oldBufferCap, uint256 newBufferCap);
/// @notice event emitted when rate limit per second is updated
event RateLimitPerSecondUpdate(uint256 oldRateLimitPerSecond, uint256 newRateLimitPerSecond);
/// @notice the amount of action available before hitting the rate limit
/// @dev replenishes at rateLimitPerSecond per second back to midPoint
/// @param limit pointer to the rate limit object
function buffer(RateLimitMidPoint storage limit) public view returns (uint256) {
uint256 elapsed;
unchecked {
elapsed = uint32(block.timestamp) - limit.lastBufferUsedTime;
}
uint256 accrued = uint256(limit.rateLimitPerSecond) * elapsed;
if (limit.bufferStored < limit.midPoint) {
return Math.min(uint256(limit.bufferStored) + accrued, uint256(limit.midPoint));
} else if (limit.bufferStored > limit.midPoint) {
/// past midpoint so subtract accrued off bufferStored back down to midpoint
/// second part of if statement will not be evaluated if first part is true
if (accrued > limit.bufferStored || limit.bufferStored - accrued < limit.midPoint) {
/// if accrued is more than buffer stored, subtracting will underflow,
/// and we are at the midpoint, so return that
return limit.midPoint;
} else {
return limit.bufferStored - accrued;
}
} else {
/// no change
return limit.bufferStored;
}
}
/// @notice syncs the buffer to the current time
/// @dev should be called before any action that
/// updates buffer cap or rate limit per second
/// @param limit pointer to the rate limit object
function sync(RateLimitMidPoint storage limit) internal {
uint112 newBuffer = uint112(buffer(limit));
uint32 blockTimestamp = uint32(block.timestamp);
limit.lastBufferUsedTime = blockTimestamp;
limit.bufferStored = newBuffer;
}
/// @notice set the rate limit per second
/// @param limit pointer to the rate limit object
/// @param newRateLimitPerSecond the new rate limit per second
function setRateLimitPerSecond(RateLimitMidPoint storage limit, uint128 newRateLimitPerSecond) internal {
sync(limit);
uint256 oldRateLimitPerSecond = limit.rateLimitPerSecond;
limit.rateLimitPerSecond = newRateLimitPerSecond;
emit RateLimitPerSecondUpdate(oldRateLimitPerSecond, newRateLimitPerSecond);
}
/// @notice set the buffer cap, but first sync to accrue all rate limits accrued
/// @param limit pointer to the rate limit object
/// @param newBufferCap the new buffer cap to set
function setBufferCap(RateLimitMidPoint storage limit, uint112 newBufferCap) internal {
sync(limit);
uint256 oldBufferCap = limit.bufferCap;
limit.bufferCap = newBufferCap;
limit.midPoint = uint112(newBufferCap / 2);
/// if buffer stored is gt buffer cap, then we need set buffer stored to buffer cap
if (limit.bufferStored > newBufferCap) {
limit.bufferStored = newBufferCap;
}
emit BufferCapUpdate(oldBufferCap, newBufferCap);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IFactoryRegistry {
error FallbackFactory();
error InvalidFactoriesToPoolFactory();
error PathAlreadyApproved();
error PathNotApproved();
error SameAddress();
error ZeroAddress();
event Approve(address indexed poolFactory, address indexed votingRewardsFactory, address indexed gaugeFactory);
event Unapprove(address indexed poolFactory, address indexed votingRewardsFactory, address indexed gaugeFactory);
event SetManagedRewardsFactory(address indexed _newRewardsFactory);
/// @notice Approve a set of factories used in Velodrome Protocol.
/// Router.sol is able to swap any poolFactories currently approved.
/// Cannot approve address(0) factories.
/// Cannot approve path that is already approved.
/// Each poolFactory has one unique set and maintains state. In the case a poolFactory is unapproved
/// and then re-approved, the same set of factories must be used. In other words, you cannot overwrite
/// the factories tied to a poolFactory address.
/// VotingRewardsFactories and GaugeFactories may use the same address across multiple poolFactories.
/// @dev Callable by onlyOwner
/// @param poolFactory .
/// @param votingRewardsFactory .
/// @param gaugeFactory .
function approve(address poolFactory, address votingRewardsFactory, address gaugeFactory) external;
/// @notice Get the factories correlated to a poolFactory.
/// Once set, this can never be modified.
/// Returns the correlated factories even after an approved poolFactory is unapproved.
function factoriesToPoolFactory(address poolFactory)
external
view
returns (address votingRewardsFactory, address gaugeFactory);
/// @notice Check if a PoolFactory is approved within the factory registry. Router uses this method to
/// ensure a pool swapped from is approved.
/// @param poolFactory .
/// @return True if PoolFactory is approved, else false
function isPoolFactoryApproved(address poolFactory) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IInterchainSecurityModule} from "@hyperlane/core/contracts/interfaces/IInterchainSecurityModule.sol";
interface ITokenBridge {
error NotBridge();
error ZeroAmount();
error ZeroAddress();
error InsufficientBalance();
event HookSet(address indexed _newHook);
event SentMessage(
uint32 indexed _destination, bytes32 indexed _recipient, uint256 _value, string _message, string _metadata
);
/// @notice Max gas limit for token bridging transactions
/// @dev Can set a different gas limit by using a custom hook
function GAS_LIMIT() external view returns (uint256);
/// @notice Returns the address of the xERC20 token that is bridged by this contract
function xerc20() external view returns (address);
/// @notice Returns the address of the mailbox contract that is used to bridge by this contract
function mailbox() external view returns (address);
/// @notice Returns the address of the hook contract used after dispatching a message
/// @dev If set to zero address, default hook will be used instead
function hook() external view returns (address);
/// @notice Returns the address of the security module contract used by the bridge
function securityModule() external view returns (IInterchainSecurityModule);
/// @notice Sets the address of the hook contract that will be used in bridging
/// @dev Can use default hook by setting to zero address
/// @param _hook The address of the new hook contract
function setHook(address _hook) external;
/// @notice Burns xERC20 tokens from the sender and triggers a x-chain transfer
/// @dev If bridging from/to Root, ERC20 tokens are wrapped into xERC20 for bridging and unwrapped back when received.
/// @param _recipient The address of the recipient on the destination chain
/// @param _amount The amount of xERC20 tokens to send
/// @param _chainid The chain id of the destination chain
function sendToken(address _recipient, uint256 _amount, uint256 _chainid) external payable;
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
interface IPostDispatchHook {
enum Types {
UNUSED,
ROUTING,
AGGREGATION,
MERKLE_TREE,
INTERCHAIN_GAS_PAYMASTER,
FALLBACK_ROUTING,
ID_AUTH_ISM,
PAUSABLE,
PROTOCOL_FEE,
LAYER_ZERO_V1,
RATE_LIMITED,
ARB_L2_TO_L1
}
/**
* @notice Returns an enum that represents the type of hook
*/
function hookType() external view returns (uint8);
/**
* @notice Returns whether the hook supports metadata
* @param metadata metadata
* @return Whether the hook supports metadata
*/
function supportsMetadata(
bytes calldata metadata
) external view returns (bool);
/**
* @notice Post action after a message is dispatched via the Mailbox
* @param metadata The metadata required for the hook
* @param message The message passed from the Mailbox.dispatch() call
*/
function postDispatch(
bytes calldata metadata,
bytes calldata message
) external payable;
/**
* @notice Compute the payment required by the postDispatch call
* @param metadata The metadata required for the hook
* @param message The message passed from the Mailbox.dispatch() call
* @return Quoted payment for the postDispatch call
*/
function quoteDispatch(
bytes calldata metadata,
bytes calldata message
) external view returns (uint256);
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {Versioned} from "./upgrade/Versioned.sol";
import {Indexed} from "./libs/Indexed.sol";
import {Message} from "./libs/Message.sol";
import {TypeCasts} from "./libs/TypeCasts.sol";
import {IInterchainSecurityModule, ISpecifiesInterchainSecurityModule} from "./interfaces/IInterchainSecurityModule.sol";
import {IPostDispatchHook} from "./interfaces/hooks/IPostDispatchHook.sol";
import {IMessageRecipient} from "./interfaces/IMessageRecipient.sol";
import {IMailbox} from "./interfaces/IMailbox.sol";
// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
contract Mailbox is IMailbox, Indexed, Versioned, OwnableUpgradeable {
// ============ Libraries ============
using Message for bytes;
using TypeCasts for bytes32;
using TypeCasts for address;
// ============ Constants ============
// Domain of chain on which the contract is deployed
uint32 public immutable localDomain;
// ============ Public Storage ============
// A monotonically increasing nonce for outbound unique message IDs.
uint32 public nonce;
// The latest dispatched message ID used for auth in post-dispatch hooks.
bytes32 public latestDispatchedId;
// The default ISM, used if the recipient fails to specify one.
IInterchainSecurityModule public defaultIsm;
// The default post dispatch hook, used for post processing of opting-in dispatches.
IPostDispatchHook public defaultHook;
// The required post dispatch hook, used for post processing of ALL dispatches.
IPostDispatchHook public requiredHook;
// Mapping of message ID to delivery context that processed the message.
struct Delivery {
address processor;
uint48 blockNumber;
}
mapping(bytes32 => Delivery) internal deliveries;
// ============ Events ============
/**
* @notice Emitted when the default ISM is updated
* @param module The new default ISM
*/
event DefaultIsmSet(address indexed module);
/**
* @notice Emitted when the default hook is updated
* @param hook The new default hook
*/
event DefaultHookSet(address indexed hook);
/**
* @notice Emitted when the required hook is updated
* @param hook The new required hook
*/
event RequiredHookSet(address indexed hook);
// ============ Constructor ============
constructor(uint32 _localDomain) {
localDomain = _localDomain;
}
// ============ Initializers ============
function initialize(
address _owner,
address _defaultIsm,
address _defaultHook,
address _requiredHook
) external initializer {
__Ownable_init();
setDefaultIsm(_defaultIsm);
setDefaultHook(_defaultHook);
setRequiredHook(_requiredHook);
transferOwnership(_owner);
}
// ============ External Functions ============
/**
* @notice Dispatches a message to the destination domain & recipient
* using the default hook and empty metadata.
* @param _destinationDomain Domain of destination chain
* @param _recipientAddress Address of recipient on destination chain as bytes32
* @param _messageBody Raw bytes content of message body
* @return The message ID inserted into the Mailbox's merkle tree
*/
function dispatch(
uint32 _destinationDomain,
bytes32 _recipientAddress,
bytes calldata _messageBody
) external payable override returns (bytes32) {
return
dispatch(
_destinationDomain,
_recipientAddress,
_messageBody,
_messageBody[0:0],
defaultHook
);
}
/**
* @notice Dispatches a message to the destination domain & recipient.
* @param destinationDomain Domain of destination chain
* @param recipientAddress Address of recipient on destination chain as bytes32
* @param messageBody Raw bytes content of message body
* @param hookMetadata Metadata used by the post dispatch hook
* @return The message ID inserted into the Mailbox's merkle tree
*/
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata hookMetadata
) external payable override returns (bytes32) {
return
dispatch(
destinationDomain,
recipientAddress,
messageBody,
hookMetadata,
defaultHook
);
}
/**
* @notice Computes quote for dipatching a message to the destination domain & recipient
* using the default hook and empty metadata.
* @param destinationDomain Domain of destination chain
* @param recipientAddress Address of recipient on destination chain as bytes32
* @param messageBody Raw bytes content of message body
* @return fee The payment required to dispatch the message
*/
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody
) external view returns (uint256 fee) {
return
quoteDispatch(
destinationDomain,
recipientAddress,
messageBody,
messageBody[0:0],
defaultHook
);
}
/**
* @notice Computes quote for dispatching a message to the destination domain & recipient.
* @param destinationDomain Domain of destination chain
* @param recipientAddress Address of recipient on destination chain as bytes32
* @param messageBody Raw bytes content of message body
* @param defaultHookMetadata Metadata used by the default post dispatch hook
* @return fee The payment required to dispatch the message
*/
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata defaultHookMetadata
) external view returns (uint256 fee) {
return
quoteDispatch(
destinationDomain,
recipientAddress,
messageBody,
defaultHookMetadata,
defaultHook
);
}
/**
* @notice Attempts to deliver `_message` to its recipient. Verifies
* `_message` via the recipient's ISM using the provided `_metadata`.
* @param _metadata Metadata used by the ISM to verify `_message`.
* @param _message Formatted Hyperlane message (refer to Message.sol).
*/
function process(
bytes calldata _metadata,
bytes calldata _message
) external payable override {
/// CHECKS ///
// Check that the message was intended for this mailbox.
require(_message.version() == VERSION, "Mailbox: bad version");
require(
_message.destination() == localDomain,
"Mailbox: unexpected destination"
);
// Check that the message hasn't already been delivered.
bytes32 _id = _message.id();
require(delivered(_id) == false, "Mailbox: already delivered");
// Get the recipient's ISM.
address recipient = _message.recipientAddress();
IInterchainSecurityModule ism = recipientIsm(recipient);
/// EFFECTS ///
deliveries[_id] = Delivery({
processor: msg.sender,
blockNumber: uint48(block.number)
});
emit Process(_message.origin(), _message.sender(), recipient);
emit ProcessId(_id);
/// INTERACTIONS ///
// Verify the message via the interchain security module.
require(
ism.verify(_metadata, _message),
"Mailbox: ISM verification failed"
);
// Deliver the message to the recipient.
IMessageRecipient(recipient).handle{value: msg.value}(
_message.origin(),
_message.sender(),
_message.body()
);
}
/**
* @notice Returns the account that processed the message.
* @param _id The message ID to check.
* @return The account that processed the message.
*/
function processor(bytes32 _id) external view returns (address) {
return deliveries[_id].processor;
}
/**
* @notice Returns the account that processed the message.
* @param _id The message ID to check.
* @return The number of the block that the message was processed at.
*/
function processedAt(bytes32 _id) external view returns (uint48) {
return deliveries[_id].blockNumber;
}
// ============ Public Functions ============
/**
* @notice Dispatches a message to the destination domain & recipient.
* @param destinationDomain Domain of destination chain
* @param recipientAddress Address of recipient on destination chain as bytes32
* @param messageBody Raw bytes content of message body
* @param metadata Metadata used by the post dispatch hook
* @param hook Custom hook to use instead of the default
* @return The message ID inserted into the Mailbox's merkle tree
*/
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata metadata,
IPostDispatchHook hook
) public payable virtual returns (bytes32) {
if (address(hook) == address(0)) {
hook = defaultHook;
}
/// CHECKS ///
// Format the message into packed bytes.
bytes memory message = _buildMessage(
destinationDomain,
recipientAddress,
messageBody
);
bytes32 id = message.id();
/// EFFECTS ///
latestDispatchedId = id;
nonce += 1;
emit Dispatch(msg.sender, destinationDomain, recipientAddress, message);
emit DispatchId(id);
/// INTERACTIONS ///
uint256 requiredValue = requiredHook.quoteDispatch(metadata, message);
// if underpaying, defer to required hook's reverting behavior
if (msg.value < requiredValue) {
requiredValue = msg.value;
}
requiredHook.postDispatch{value: requiredValue}(metadata, message);
hook.postDispatch{value: msg.value - requiredValue}(metadata, message);
return id;
}
/**
* @notice Computes quote for dispatching a message to the destination domain & recipient.
* @param destinationDomain Domain of destination chain
* @param recipientAddress Address of recipient on destination chain as bytes32
* @param messageBody Raw bytes content of message body
* @param metadata Metadata used by the post dispatch hook
* @param hook Custom hook to use instead of the default
* @return fee The payment required to dispatch the message
*/
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata metadata,
IPostDispatchHook hook
) public view returns (uint256 fee) {
if (address(hook) == address(0)) {
hook = defaultHook;
}
bytes memory message = _buildMessage(
destinationDomain,
recipientAddress,
messageBody
);
return
requiredHook.quoteDispatch(metadata, message) +
hook.quoteDispatch(metadata, message);
}
/**
* @notice Returns true if the message has been processed.
* @param _id The message ID to check.
* @return True if the message has been delivered.
*/
function delivered(bytes32 _id) public view override returns (bool) {
return deliveries[_id].blockNumber > 0;
}
/**
* @notice Sets the default ISM for the Mailbox.
* @param _module The new default ISM. Must be a contract.
*/
function setDefaultIsm(address _module) public onlyOwner {
require(
Address.isContract(_module),
"Mailbox: default ISM not contract"
);
defaultIsm = IInterchainSecurityModule(_module);
emit DefaultIsmSet(_module);
}
/**
* @notice Sets the default post dispatch hook for the Mailbox.
* @param _hook The new default post dispatch hook. Must be a contract.
*/
function setDefaultHook(address _hook) public onlyOwner {
require(
Address.isContract(_hook),
"Mailbox: default hook not contract"
);
defaultHook = IPostDispatchHook(_hook);
emit DefaultHookSet(_hook);
}
/**
* @notice Sets the required post dispatch hook for the Mailbox.
* @param _hook The new default post dispatch hook. Must be a contract.
*/
function setRequiredHook(address _hook) public onlyOwner {
require(
Address.isContract(_hook),
"Mailbox: required hook not contract"
);
requiredHook = IPostDispatchHook(_hook);
emit RequiredHookSet(_hook);
}
/**
* @notice Returns the ISM to use for the recipient, defaulting to the
* default ISM if none is specified.
* @param _recipient The message recipient whose ISM should be returned.
* @return The ISM to use for `_recipient`.
*/
function recipientIsm(
address _recipient
) public view returns (IInterchainSecurityModule) {
// use low-level staticcall in case of revert or empty return data
(bool success, bytes memory returnData) = _recipient.staticcall(
abi.encodeCall(
ISpecifiesInterchainSecurityModule.interchainSecurityModule,
()
)
);
// check if call was successful and returned data
if (success && returnData.length != 0) {
// check if returnData is a valid address
address ism = abi.decode(returnData, (address));
// check if the ISM is a contract
if (ism != address(0)) {
return IInterchainSecurityModule(ism);
}
}
// Use the default if a valid one is not specified by the recipient.
return defaultIsm;
}
// ============ Internal Functions ============
function _buildMessage(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody
) internal view returns (bytes memory) {
return
Message.formatMessage(
VERSION,
nonce,
localDomain,
msg.sender.addressToBytes32(),
destinationDomain,
recipientAddress,
messageBody
);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.19 <0.9.0;
import {IInterchainSecurityModule} from "@hyperlane/core/contracts/interfaces/IInterchainSecurityModule.sol";
import {StandardHookMetadata} from "@hyperlane/core/contracts/hooks/libs/StandardHookMetadata.sol";
import {ISpecifiesInterchainSecurityModule} from "../interfaces/external/ISpecifiesInterchainSecurityModule.sol";
import {IHookGasEstimator} from "../interfaces/root/bridge/hyperlane/IHookGasEstimator.sol";
import {IHLHandler} from "../interfaces/bridge/hyperlane/IHLHandler.sol";
import {ITokenBridge} from "../interfaces/bridge/ITokenBridge.sol";
import {ChainRegistry} from "./ChainRegistry.sol";
/// @title Velodrome Superchain Base Token Bridge
/// @notice Base Token Bridge contract to be extended in Root & Leaf implementations
abstract contract BaseTokenBridge is ITokenBridge, IHLHandler, ISpecifiesInterchainSecurityModule, ChainRegistry {
/// @inheritdoc ITokenBridge
address public immutable xerc20;
/// @inheritdoc ITokenBridge
address public immutable mailbox;
/// @inheritdoc ITokenBridge
address public hook;
/// @inheritdoc ITokenBridge
IInterchainSecurityModule public securityModule;
constructor(address _owner, address _xerc20, address _mailbox, address _ism) ChainRegistry(_owner) {
xerc20 = _xerc20;
mailbox = _mailbox;
securityModule = IInterchainSecurityModule(_ism);
emit InterchainSecurityModuleSet({_new: _ism});
}
/// @inheritdoc ISpecifiesInterchainSecurityModule
function interchainSecurityModule() external view returns (IInterchainSecurityModule) {
return securityModule;
}
/// @inheritdoc ISpecifiesInterchainSecurityModule
function setInterchainSecurityModule(address _ism) external onlyOwner {
securityModule = IInterchainSecurityModule(_ism);
emit InterchainSecurityModuleSet({_new: _ism});
}
/// @inheritdoc ITokenBridge
function setHook(address _hook) external onlyOwner {
hook = _hook;
emit HookSet({_newHook: _hook});
}
/// @inheritdoc ITokenBridge
function sendToken(address _recipient, uint256 _amount, uint256 _chainid) external payable virtual;
/// @inheritdoc IHLHandler
function handle(uint32 _origin, bytes32 _sender, bytes calldata _message) external payable virtual;
function _generateGasMetadata(address _hook, uint256 _value, bytes memory _message)
internal
view
virtual
returns (bytes memory)
{
/// @dev If custom hook is set, it should be used to estimate gas
uint256 gasLimit = _hook == address(0) ? GAS_LIMIT() : IHookGasEstimator(_hook).estimateSendTokenGas();
return StandardHookMetadata.formatMetadata({
_msgValue: _value,
_gasLimit: gasLimit,
_refundAddress: msg.sender,
_customMetadata: ""
});
}
/// @inheritdoc ITokenBridge
function GAS_LIMIT() public pure virtual returns (uint256) {
return 200_000;
}
}// SPDX-License-Identifier: BSD-3.0
pragma solidity >=0.8.19 <0.9.0;
import {Math} from "@openzeppelin5/contracts/utils/math/Math.sol";
import {RateLimitMidPoint, RateLimitMidpointCommonLibrary} from "./RateLimitMidpointCommonLibrary.sol";
/// @title library for putting a rate limit on how fast a contract
/// can perform an action e.g. Minting and Burning with a midpoint
/// @author Elliot Friedman
/// @dev Modified lightly from Zelt at commit 30b2ba0 to update the Solidity Compiler version used
/// Can refer to: (https://github.com/solidity-labs-io/zelt/blob/30b2ba0352422471c03b233d55feddfbdba198a3/src/lib/RateLimitedMidpointLibrary.sol)
library RateLimitedMidpointLibrary {
using RateLimitMidpointCommonLibrary for RateLimitMidPoint;
/// @notice event emitted when buffer gets eaten into
event BufferUsed(uint256 amountUsed, uint256 bufferRemaining);
/// @notice event emitted when buffer gets replenished
event BufferReplenished(uint256 amountReplenished, uint256 bufferRemaining);
/// @notice the method that enforces the rate limit.
/// Decreases buffer by "amount".
/// If buffer is <= amount, revert
/// @param limit pointer to the rate limit object
/// @param amount to decrease buffer by
function depleteBuffer(RateLimitMidPoint storage limit, uint256 amount) internal {
/// SLOAD 2x
uint256 newBuffer = limit.buffer();
require(amount <= newBuffer, "RateLimited: rate limit hit");
uint32 blockTimestamp = uint32(block.timestamp);
uint112 newBufferStored = uint112(newBuffer - amount);
/// gas optimization to only use a single SSTORE
limit.lastBufferUsedTime = blockTimestamp;
limit.bufferStored = newBufferStored;
emit BufferUsed(amount, newBufferStored);
}
/// @notice function to replenish buffer
/// @param amount to increase buffer by if under buffer cap
/// @param limit pointer to the rate limit object
function replenishBuffer(RateLimitMidPoint storage limit, uint256 amount) internal {
/// SLOAD 2x
uint256 buffer = limit.buffer();
/// warm SLOAD
uint256 _bufferCap = limit.bufferCap;
uint256 newBuffer = buffer + amount;
require(newBuffer <= _bufferCap, "RateLimited: buffer cap overflow");
uint32 blockTimestamp = uint32(block.timestamp);
/// ensure that bufferStored cannot be gt buffer cap
uint112 newBufferStored = uint112(newBuffer);
/// gas optimization to only use a single SSTORE
limit.lastBufferUsedTime = blockTimestamp;
limit.bufferStored = newBufferStored;
emit BufferReplenished(amount, newBufferStored);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
interface IInterchainSecurityModule {
enum Types {
UNUSED,
ROUTING,
AGGREGATION,
LEGACY_MULTISIG,
MERKLE_ROOT_MULTISIG,
MESSAGE_ID_MULTISIG,
NULL, // used with relayer carrying no metadata
CCIP_READ,
ARB_L2_TO_L1
}
/**
* @notice Returns an enum that represents the type of security model
* encoded by this ISM.
* @dev Relayers infer how to fetch and format metadata.
*/
function moduleType() external view returns (uint8);
/**
* @notice Defines a security model responsible for verifying interchain
* messages based on the provided metadata.
* @param _metadata Off-chain metadata provided by a relayer, specific to
* the security model encoded by the module (e.g. validator signatures)
* @param _message Hyperlane encoded interchain message
* @return True if the message was verified
*/
function verify(
bytes calldata _metadata,
bytes calldata _message
) external returns (bool);
}
interface ISpecifiesInterchainSecurityModule {
function interchainSecurityModule()
external
view
returns (IInterchainSecurityModule);
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
/**
* @title Versioned
* @notice Version getter for contracts
**/
contract Versioned {
uint8 public constant VERSION = 3;
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
contract Indexed {
uint256 public immutable deployedBlock;
constructor() {
deployedBlock = block.number;
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {TypeCasts} from "./TypeCasts.sol";
/**
* @title Hyperlane Message Library
* @notice Library for formatted messages used by Mailbox
**/
library Message {
using TypeCasts for bytes32;
uint256 private constant VERSION_OFFSET = 0;
uint256 private constant NONCE_OFFSET = 1;
uint256 private constant ORIGIN_OFFSET = 5;
uint256 private constant SENDER_OFFSET = 9;
uint256 private constant DESTINATION_OFFSET = 41;
uint256 private constant RECIPIENT_OFFSET = 45;
uint256 private constant BODY_OFFSET = 77;
/**
* @notice Returns formatted (packed) Hyperlane message with provided fields
* @dev This function should only be used in memory message construction.
* @param _version The version of the origin and destination Mailboxes
* @param _nonce A nonce to uniquely identify the message on its origin chain
* @param _originDomain Domain of origin chain
* @param _sender Address of sender as bytes32
* @param _destinationDomain Domain of destination chain
* @param _recipient Address of recipient on destination chain as bytes32
* @param _messageBody Raw bytes of message body
* @return Formatted message
*/
function formatMessage(
uint8 _version,
uint32 _nonce,
uint32 _originDomain,
bytes32 _sender,
uint32 _destinationDomain,
bytes32 _recipient,
bytes calldata _messageBody
) internal pure returns (bytes memory) {
return
abi.encodePacked(
_version,
_nonce,
_originDomain,
_sender,
_destinationDomain,
_recipient,
_messageBody
);
}
/**
* @notice Returns the message ID.
* @param _message ABI encoded Hyperlane message.
* @return ID of `_message`
*/
function id(bytes memory _message) internal pure returns (bytes32) {
return keccak256(_message);
}
/**
* @notice Returns the message version.
* @param _message ABI encoded Hyperlane message.
* @return Version of `_message`
*/
function version(bytes calldata _message) internal pure returns (uint8) {
return uint8(bytes1(_message[VERSION_OFFSET:NONCE_OFFSET]));
}
/**
* @notice Returns the message nonce.
* @param _message ABI encoded Hyperlane message.
* @return Nonce of `_message`
*/
function nonce(bytes calldata _message) internal pure returns (uint32) {
return uint32(bytes4(_message[NONCE_OFFSET:ORIGIN_OFFSET]));
}
/**
* @notice Returns the message origin domain.
* @param _message ABI encoded Hyperlane message.
* @return Origin domain of `_message`
*/
function origin(bytes calldata _message) internal pure returns (uint32) {
return uint32(bytes4(_message[ORIGIN_OFFSET:SENDER_OFFSET]));
}
/**
* @notice Returns the message sender as bytes32.
* @param _message ABI encoded Hyperlane message.
* @return Sender of `_message` as bytes32
*/
function sender(bytes calldata _message) internal pure returns (bytes32) {
return bytes32(_message[SENDER_OFFSET:DESTINATION_OFFSET]);
}
/**
* @notice Returns the message sender as address.
* @param _message ABI encoded Hyperlane message.
* @return Sender of `_message` as address
*/
function senderAddress(
bytes calldata _message
) internal pure returns (address) {
return sender(_message).bytes32ToAddress();
}
/**
* @notice Returns the message destination domain.
* @param _message ABI encoded Hyperlane message.
* @return Destination domain of `_message`
*/
function destination(
bytes calldata _message
) internal pure returns (uint32) {
return uint32(bytes4(_message[DESTINATION_OFFSET:RECIPIENT_OFFSET]));
}
/**
* @notice Returns the message recipient as bytes32.
* @param _message ABI encoded Hyperlane message.
* @return Recipient of `_message` as bytes32
*/
function recipient(
bytes calldata _message
) internal pure returns (bytes32) {
return bytes32(_message[RECIPIENT_OFFSET:BODY_OFFSET]);
}
/**
* @notice Returns the message recipient as address.
* @param _message ABI encoded Hyperlane message.
* @return Recipient of `_message` as address
*/
function recipientAddress(
bytes calldata _message
) internal pure returns (address) {
return recipient(_message).bytes32ToAddress();
}
/**
* @notice Returns the message body.
* @param _message ABI encoded Hyperlane message.
* @return Body of `_message`
*/
function body(
bytes calldata _message
) internal pure returns (bytes calldata) {
return bytes(_message[BODY_OFFSET:]);
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IInterchainSecurityModule} from "./IInterchainSecurityModule.sol";
import {IPostDispatchHook} from "./hooks/IPostDispatchHook.sol";
interface IMailbox {
// ============ Events ============
/**
* @notice Emitted when a new message is dispatched via Hyperlane
* @param sender The address that dispatched the message
* @param destination The destination domain of the message
* @param recipient The message recipient address on `destination`
* @param message Raw bytes of message
*/
event Dispatch(
address indexed sender,
uint32 indexed destination,
bytes32 indexed recipient,
bytes message
);
/**
* @notice Emitted when a new message is dispatched via Hyperlane
* @param messageId The unique message identifier
*/
event DispatchId(bytes32 indexed messageId);
/**
* @notice Emitted when a Hyperlane message is processed
* @param messageId The unique message identifier
*/
event ProcessId(bytes32 indexed messageId);
/**
* @notice Emitted when a Hyperlane message is delivered
* @param origin The origin domain of the message
* @param sender The message sender address on `origin`
* @param recipient The address that handled the message
*/
event Process(
uint32 indexed origin,
bytes32 indexed sender,
address indexed recipient
);
function localDomain() external view returns (uint32);
function delivered(bytes32 messageId) external view returns (bool);
function defaultIsm() external view returns (IInterchainSecurityModule);
function defaultHook() external view returns (IPostDispatchHook);
function requiredHook() external view returns (IPostDispatchHook);
function latestDispatchedId() external view returns (bytes32);
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody
) external payable returns (bytes32 messageId);
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody
) external view returns (uint256 fee);
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata body,
bytes calldata defaultHookMetadata
) external payable returns (bytes32 messageId);
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata defaultHookMetadata
) external view returns (uint256 fee);
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata body,
bytes calldata customHookMetadata,
IPostDispatchHook customHook
) external payable returns (bytes32 messageId);
function quoteDispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata customHookMetadata,
IPostDispatchHook customHook
) external view returns (uint256 fee);
function process(
bytes calldata metadata,
bytes calldata message
) external payable;
function recipientIsm(
address recipient
) external view returns (IInterchainSecurityModule module);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal onlyInitializing {
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal onlyInitializing {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
/**
* Format of metadata:
*
* [0:2] variant
* [2:34] msg.value
* [34:66] Gas limit for message (IGP)
* [66:86] Refund address for message (IGP)
* [86:] Custom metadata
*/
library StandardHookMetadata {
struct Metadata {
uint16 variant;
uint256 msgValue;
uint256 gasLimit;
address refundAddress;
}
uint8 private constant VARIANT_OFFSET = 0;
uint8 private constant MSG_VALUE_OFFSET = 2;
uint8 private constant GAS_LIMIT_OFFSET = 34;
uint8 private constant REFUND_ADDRESS_OFFSET = 66;
uint256 private constant MIN_METADATA_LENGTH = 86;
uint16 public constant VARIANT = 1;
/**
* @notice Returns the variant of the metadata.
* @param _metadata ABI encoded standard hook metadata.
* @return variant of the metadata as uint8.
*/
function variant(bytes calldata _metadata) internal pure returns (uint16) {
if (_metadata.length < VARIANT_OFFSET + 2) return 0;
return uint16(bytes2(_metadata[VARIANT_OFFSET:VARIANT_OFFSET + 2]));
}
/**
* @notice Returns the specified value for the message.
* @param _metadata ABI encoded standard hook metadata.
* @param _default Default fallback value.
* @return Value for the message as uint256.
*/
function msgValue(
bytes calldata _metadata,
uint256 _default
) internal pure returns (uint256) {
if (_metadata.length < MSG_VALUE_OFFSET + 32) return _default;
return
uint256(bytes32(_metadata[MSG_VALUE_OFFSET:MSG_VALUE_OFFSET + 32]));
}
/**
* @notice Returns the specified gas limit for the message.
* @param _metadata ABI encoded standard hook metadata.
* @param _default Default fallback gas limit.
* @return Gas limit for the message as uint256.
*/
function gasLimit(
bytes calldata _metadata,
uint256 _default
) internal pure returns (uint256) {
if (_metadata.length < GAS_LIMIT_OFFSET + 32) return _default;
return
uint256(bytes32(_metadata[GAS_LIMIT_OFFSET:GAS_LIMIT_OFFSET + 32]));
}
/**
* @notice Returns the specified refund address for the message.
* @param _metadata ABI encoded standard hook metadata.
* @param _default Default fallback refund address.
* @return Refund address for the message as address.
*/
function refundAddress(
bytes calldata _metadata,
address _default
) internal pure returns (address) {
if (_metadata.length < REFUND_ADDRESS_OFFSET + 20) return _default;
return
address(
bytes20(
_metadata[REFUND_ADDRESS_OFFSET:REFUND_ADDRESS_OFFSET + 20]
)
);
}
/**
* @notice Returns any custom metadata.
* @param _metadata ABI encoded standard hook metadata.
* @return Custom metadata.
*/
function getCustomMetadata(
bytes calldata _metadata
) internal pure returns (bytes calldata) {
if (_metadata.length < MIN_METADATA_LENGTH) return _metadata[0:0];
return _metadata[MIN_METADATA_LENGTH:];
}
/**
* @notice Formats the specified gas limit and refund address into standard hook metadata.
* @param _msgValue msg.value for the message.
* @param _gasLimit Gas limit for the message.
* @param _refundAddress Refund address for the message.
* @param _customMetadata Additional metadata to include in the standard hook metadata.
* @return ABI encoded standard hook metadata.
*/
function formatMetadata(
uint256 _msgValue,
uint256 _gasLimit,
address _refundAddress,
bytes memory _customMetadata
) internal pure returns (bytes memory) {
return
abi.encodePacked(
VARIANT,
_msgValue,
_gasLimit,
_refundAddress,
_customMetadata
);
}
/**
* @notice Formats the specified gas limit and refund address into standard hook metadata.
* @param _msgValue msg.value for the message.
* @return ABI encoded standard hook metadata.
*/
function overrideMsgValue(
uint256 _msgValue
) internal view returns (bytes memory) {
return formatMetadata(_msgValue, uint256(0), msg.sender, "");
}
/**
* @notice Formats the specified gas limit and refund address into standard hook metadata.
* @param _gasLimit Gas limit for the message.
* @return ABI encoded standard hook metadata.
*/
function overrideGasLimit(
uint256 _gasLimit
) internal view returns (bytes memory) {
return formatMetadata(uint256(0), _gasLimit, msg.sender, "");
}
/**
* @notice Formats the specified refund address into standard hook metadata.
* @param _refundAddress Refund address for the message.
* @return ABI encoded standard hook metadata.
*/
function overrideRefundAddress(
address _refundAddress
) internal pure returns (bytes memory) {
return formatMetadata(uint256(0), uint256(0), _refundAddress, "");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IInterchainSecurityModule} from "@hyperlane/core/contracts/interfaces/IInterchainSecurityModule.sol";
interface ISpecifiesInterchainSecurityModule {
event InterchainSecurityModuleSet(address indexed _new);
// @notice The currently set InterchainSecurityModule.
function interchainSecurityModule() external view returns (IInterchainSecurityModule);
// @notice Sets the new InterchainSecurityModule.
/// @dev Throws if not called by owner.
/// @param _ism .
function setInterchainSecurityModule(address _ism) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IHookGasEstimator {
/// @notice Returns the estimated gas limit for token bridging
function estimateSendTokenGas() external view returns (uint256);
/// @notice Returns the estimated gas limit for token bridging with locking
function estimateSendTokenAndLockGas() external view returns (uint256);
/// @notice Returns the estimated gas limit for a given command
/// @param _command The identifier of the command to estimate gas for
function estimateGas(uint256 _command) external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.19 <0.9.0;
import {Ownable} from "@openzeppelin5/contracts/access/Ownable.sol";
import {EnumerableSet} from "@openzeppelin5/contracts/utils/structs/EnumerableSet.sol";
import {IChainRegistry} from "../interfaces/bridge/IChainRegistry.sol";
/// @title Chain Registry
/// @notice Contains logic for managing registered chains from which messages can be sent to or received from
abstract contract ChainRegistry is IChainRegistry, Ownable {
using EnumerableSet for EnumerableSet.UintSet;
/// @dev Stores list of trusted chains
EnumerableSet.UintSet internal _chainids;
constructor(address _owner) Ownable(_owner) {}
/// @inheritdoc IChainRegistry
function registerChain(uint256 _chainid) external onlyOwner {
if (_chainid == block.chainid) revert InvalidChain();
if (_chainids.contains({value: _chainid})) revert AlreadyRegistered();
_chainids.add({value: _chainid});
emit ChainRegistered({_chainid: _chainid});
}
/// @inheritdoc IChainRegistry
function deregisterChain(uint256 _chainid) external onlyOwner {
if (!_chainids.contains({value: _chainid})) revert NotRegistered();
_chainids.remove({value: _chainid});
emit ChainDeregistered({_chainid: _chainid});
}
/// @inheritdoc IChainRegistry
function chainids() external view returns (uint256[] memory) {
return _chainids.values();
}
/// @inheritdoc IChainRegistry
function contains(uint256 _chainid) external view returns (bool) {
return _chainids.contains({value: _chainid});
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IChainRegistry {
error AlreadyRegistered();
error NotRegistered();
error InvalidChain();
event ChainRegistered(uint256 indexed _chainid);
event ChainDeregistered(uint256 indexed _chainid);
/// @notice Returns set of all registered chain ids
/// @return An array of all registered chain ids
function chainids() external view returns (uint256[] memory);
/// @notice Checks if a chain is registered
/// @param _chainid The chain id to check
/// @return True if the chain is registered, false otherwise
function contains(uint256 _chainid) external view returns (bool);
/// @notice Registers a new chain
/// @dev Only callable by the owner, allows messages to the registered chain
/// @param _chainid The chain id to register
function registerChain(uint256 _chainid) external;
/// @notice Deregisters a chain
/// @dev Only callable by the owner, disallows messages to the deregistered chain
/// @param _chainid The chain id to deregister
function deregisterChain(uint256 _chainid) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}{
"remappings": [
"@openzeppelin5/contracts/=lib/openzeppelin-contracts/contracts/",
"ds-test/=lib/openzeppelin-contracts/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/src/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"createX/=lib/createX/src/",
"@nomad-xyz/=lib/ExcessivelySafeCall/",
"@hyperlane/=node_modules/@hyperlane-xyz/",
"@openzeppelin/contracts/=node_modules/@openzeppelin/contracts/",
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"ExcessivelySafeCall/=lib/ExcessivelySafeCall/src/",
"openzeppelin/=lib/createX/lib/openzeppelin-contracts/contracts/",
"solady/=lib/createX/lib/solady/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "cancun",
"viaIR": false,
"libraries": {
"src/libraries/rateLimits/RateLimitMidpointCommonLibrary.sol": {
"RateLimitMidpointCommonLibrary": "0x50f6F01b1804eAf2e53830A5a6890b70FE45937E"
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_xerc20","type":"address"},{"internalType":"address","name":"_mailbox","type":"address"},{"internalType":"address","name":"_ism","type":"address"},{"internalType":"address","name":"_voter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AlreadyRegistered","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidChain","type":"error"},{"inputs":[],"name":"NotBridge","type":"error"},{"inputs":[],"name":"NotMailbox","type":"error"},{"inputs":[],"name":"NotRegistered","type":"error"},{"inputs":[],"name":"NotRoot","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_chainid","type":"uint256"}],"name":"ChainDeregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_chainid","type":"uint256"}],"name":"ChainRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_newHook","type":"address"}],"name":"HookSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_new","type":"address"}],"name":"InterchainSecurityModuleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"_origin","type":"uint32"},{"indexed":true,"internalType":"bytes32","name":"_sender","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"string","name":"_message","type":"string"}],"name":"ReceivedMessage","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"_destination","type":"uint32"},{"indexed":true,"internalType":"bytes32","name":"_recipient","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"},{"indexed":false,"internalType":"string","name":"_message","type":"string"},{"indexed":false,"internalType":"string","name":"_metadata","type":"string"}],"name":"SentMessage","type":"event"},{"inputs":[],"name":"BASE_CHAIN_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAS_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"chainids","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainid","type":"uint256"}],"name":"contains","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainid","type":"uint256"}],"name":"deregisterChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_origin","type":"uint32"},{"internalType":"bytes32","name":"_sender","type":"bytes32"},{"internalType":"bytes","name":"_message","type":"bytes"}],"name":"handle","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"hook","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interchainSecurityModule","outputs":[{"internalType":"contract IInterchainSecurityModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mailbox","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_chainid","type":"uint256"}],"name":"registerChain","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"securityModule","outputs":[{"internalType":"contract IInterchainSecurityModule","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_chainid","type":"uint256"}],"name":"sendToken","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"_hook","type":"address"}],"name":"setHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_ism","type":"address"}],"name":"setInterchainSecurityModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"voter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"xerc20","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60e060405234801561000f575f5ffd5b5060405161198038038061198083398101604081905261002e9161014c565b848484848383838383806001600160a01b03811661006557604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b61006e816100e2565b50506001600160a01b0383811660805282811660a052600480546001600160a01b03191691831691821790556040517ffec811ed4e60aebdaf7a79cad8a97196bf56e35362f039705598226d30c9d248905f90a25050506001600160a01b0390951660c052506101ad975050505050505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b0381168114610147575f5ffd5b919050565b5f5f5f5f5f60a08688031215610160575f5ffd5b61016986610131565b945061017760208701610131565b935061018560408701610131565b925061019360608701610131565b91506101a160808701610131565b90509295509295909350565b60805160a05160c05161176361021d5f395f81816101c001528181610511015261059e01525f81816102970152818161044901528181610b8e0152610cc901525f818161037a01528181610634015281816106a0015281816106e0015281816107710152610c5e01526117635ff3fe608060405260043610610110575f3560e01c80638da5cb5b1161009d578063efc21e3f11610062578063efc21e3f146102f5578063f10d8c971461030a578063f2fde38b14610329578063f772795914610348578063ffcbd8d014610369575f5ffd5b80638da5cb5b1461023b578063c34052e014610257578063d5438eae14610286578063de523cf3146102b9578063e68e00bf146102d6575f5ffd5b806346c96aac116100e357806346c96aac146101af57806356d5d475146101e2578063587faab6146101f5578063715018a6146102085780637f5a7c7b1461021c575f5ffd5b8063091d2788146101145780630e72cc0614610138578063380ef3fe146101595780633dfd387314610190575b5f5ffd5b34801561011f575f5ffd5b506202e6305b6040519081526020015b60405180910390f35b348015610143575f5ffd5b50610157610152366004611383565b61039c565b005b348015610164575f5ffd5b50600454610178906001600160a01b031681565b6040516001600160a01b03909116815260200161012f565b34801561019b575f5ffd5b506101576101aa366004611383565b6103ed565b3480156101ba575f5ffd5b506101787f000000000000000000000000000000000000000000000000000000000000000081565b6101576101f036600461139e565b61043e565b61015761020336600461142b565b610815565b348015610213575f5ffd5b5061015761085b565b348015610227575f5ffd5b50600354610178906001600160a01b031681565b348015610246575f5ffd5b505f546001600160a01b0316610178565b348015610262575f5ffd5b5061027661027136600461145d565b61086e565b604051901515815260200161012f565b348015610291575f5ffd5b506101787f000000000000000000000000000000000000000000000000000000000000000081565b3480156102c4575f5ffd5b506004546001600160a01b0316610178565b3480156102e1575f5ffd5b506101576102f036600461145d565b610880565b348015610300575f5ffd5b5061012561210581565b348015610315575f5ffd5b5061015761032436600461145d565b6108e9565b348015610334575f5ffd5b50610157610343366004611383565b610973565b348015610353575f5ffd5b5061035c6109b5565b60405161012f9190611474565b348015610374575f5ffd5b506101787f000000000000000000000000000000000000000000000000000000000000000081565b6103a46109c6565b600480546001600160a01b0319166001600160a01b0383169081179091556040517ffec811ed4e60aebdaf7a79cad8a97196bf56e35362f039705598226d30c9d248905f90a250565b6103f56109c6565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4eab7b127c764308788622363ad3e9532de3dfba7845bd4f84c125a22544255a905f90a250565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146104875760405163079ddc6d60e31b815260040160405180910390fd5b3083146104a757604051637fea9dc560e01b815260040160405180910390fd5b6104bb600163ffffffff808716906109f216565b6104d85760405163aba4733960e01b815260040160405180910390fd5b5f5f6104e48484610a0c565b915091505f612105461461057f57604051631918e7f760e11b81526001600160a01b0384811660048301527f00000000000000000000000000000000000000000000000000000000000000001690633231cfee90602401602060405180830381865afa158015610556573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061057a91906114b6565b610607565b60405163929c8dcd60e01b81526001600160a01b0384811660048301527f0000000000000000000000000000000000000000000000000000000000000000169063929c8dcd90602401602060405180830381865afa1580156105e3573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061060791906114b6565b90506001600160a01b0381161561074b576040516340c10f1960e01b8152306004820152602481018390527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906340c10f19906044015f604051808303815f87803b15801561067d575f5ffd5b505af115801561068f573d5f5f3e3d5ffd5b506106c99250506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690508284610a7a565b60405163b66503cf60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301526024820184905282169063b66503cf906044015f604051808303815f87803b158015610730575f5ffd5b505af1158015610742573d5f5f3e3d5ffd5b505050506107c9565b6040516340c10f1960e01b81526001600160a01b038481166004830152602482018490527f000000000000000000000000000000000000000000000000000000000000000016906340c10f19906044015f604051808303815f87803b1580156107b2575f5ffd5b505af11580156107c4573d5f5f3e3d5ffd5b505050505b858763ffffffff167fecdc36fa3681f5d9c559dcbc399417db6f0ac0d81a78529685a1150265971d55348888604051610804939291906114d1565b60405180910390a350505050505050565b6040516001600160601b0319606085901b166020820152603481018390525f90605401604051602081830303815290604052905061085583858484610b01565b50505050565b6108636109c6565b61086c5f610de1565b565b5f61087a6001836109f2565b92915050565b6108886109c6565b6108936001826109f2565b6108b05760405163aba4733960e01b815260040160405180910390fd5b6108bb600182610e30565b5060405181907f4732a5e8ed8dad61e5185f5b8d342146071e8029d9055b85b5ff24078cee3d86905f90a250565b6108f16109c6565b4681036109115760405163057f3fa760e51b815260040160405180910390fd5b61091c6001826109f2565b1561093a57604051630ea075bf60e21b815260040160405180910390fd5b610945600182610e3b565b5060405181907f51f61861fc6e796ffd63c9c30190bb4a24477325376d9e326d507a75097f89e7905f90a250565b61097b6109c6565b6001600160a01b0381166109a957604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6109b281610de1565b50565b60606109c16001610e46565b905090565b5f546001600160a01b0316331461086c5760405163118cdaa760e01b81523360048201526024016109a0565b5f81815260018301602052604081205415155b9392505050565b5f80838184610a1c82601461151a565b92610a299392919061152d565b610a3291611554565b60601c8484610a425f601461151a565b90610a4e5f601461151a565b610a5990602061151a565b92610a669392919061152d565b610a6f9161158a565b909590945092505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa158015610ac7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aeb91906115a7565b90506108558484610afc858561151a565b610e52565b835f03610b2157604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038316610b485760405163d92e233d60e01b815260040160405180910390fd5b610b536001836109f2565b610b705760405163aba4733960e01b815260040160405180910390fd5b6003546001600160a01b03165f610b88823485610f05565b9050835f7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166381d2ea9583308887896040518663ffffffff1660e01b8152600401610be09594939291906115ec565b602060405180830381865afa158015610bfb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c1f91906115a7565b905034811115610c4257604051631e9acf1760e31b815260040160405180910390fd5b604051632770a7eb60e21b8152336004820152602481018990527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690639dc29fac906044015f604051808303815f87803b158015610ca7575f5ffd5b505af1158015610cb9573d5f5f3e3d5ffd5b50506040516242e0f760e61b81527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692506310b83dc091508390610d1290869030908b908a908c906004016115ec565b60206040518083038185885af1158015610d2e573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610d5391906115a7565b505f610d5f823461163d565b90508015610d9357604051339082156108fc029083905f818181858888f19350505050158015610d91573d5f5f3e3d5ffd5b505b308363ffffffff167fe8ed70f129c378298b9277a92cb6f0f821d501da841fb5a9f313c645b1da14e3848988604051610dce93929190611650565b60405180910390a3505050505050505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f610a058383610fa6565b5f610a058383611090565b60605f610a05836110dc565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610ea38482611135565b61085557604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610efb9085906111d2565b61085584826111d2565b60605f6001600160a01b03851615610f7c57846001600160a01b03166330fb74ab6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f53573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f7791906115a7565b610f81565b6202e6305b9050610f9d84823360405180602001604052805f815250611238565b95945050505050565b5f8181526001830160205260408120548015611080575f610fc860018361163d565b85549091505f90610fdb9060019061163d565b905080821461103a575f865f018281548110610ff957610ff961167a565b905f5260205f200154905080875f0184815481106110195761101961167a565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061104b5761104b61168e565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f90556001935050505061087a565b5f91505061087a565b5092915050565b5f8181526001830160205260408120546110d557508154600181810184555f84815260208082209093018490558454848252828601909352604090209190915561087a565b505f61087a565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561112957602002820191905f5260205f20905b815481526020019060010190808311611115575b50505050509050919050565b5f5f5f846001600160a01b03168460405161115091906116b9565b5f604051808303815f865af19150503d805f8114611189576040519150601f19603f3d011682016040523d82523d5f602084013e61118e565b606091505b50915091508180156111b85750805115806111b85750808060200190518101906111b891906116c4565b8015610f9d5750505050506001600160a01b03163b151590565b5f6111e66001600160a01b0384168361126d565b905080515f1415801561120a57508080602001905181019061120891906116c4565b155b1561123357604051635274afe760e01b81526001600160a01b03841660048201526024016109a0565b505050565b60606001858585856040516020016112549594939291906116e3565b6040516020818303038152906040529050949350505050565b6060610a0583835f845f5f856001600160a01b0316848660405161129191906116b9565b5f6040518083038185875af1925050503d805f81146112cb576040519150601f19603f3d011682016040523d82523d5f602084013e6112d0565b606091505b50915091506112e08683836112ea565b9695505050505050565b6060826112ff576112fa82611346565b610a05565b815115801561131657506001600160a01b0384163b155b1561133f57604051639996b31560e01b81526001600160a01b03851660048201526024016109a0565b5080610a05565b8051156113565780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b03811681146109b2575f5ffd5b5f60208284031215611393575f5ffd5b8135610a058161136f565b5f5f5f5f606085870312156113b1575f5ffd5b843563ffffffff811681146113c4575f5ffd5b935060208501359250604085013567ffffffffffffffff8111156113e6575f5ffd5b8501601f810187136113f6575f5ffd5b803567ffffffffffffffff81111561140c575f5ffd5b87602082840101111561141d575f5ffd5b949793965060200194505050565b5f5f5f6060848603121561143d575f5ffd5b83356114488161136f565b95602085013595506040909401359392505050565b5f6020828403121561146d575f5ffd5b5035919050565b602080825282518282018190525f918401906040840190835b818110156114ab57835183526020938401939092019160010161148d565b509095945050505050565b5f602082840312156114c6575f5ffd5b8151610a058161136f565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561087a5761087a611506565b5f5f8585111561153b575f5ffd5b83861115611547575f5ffd5b5050820193919092039150565b80356001600160601b03198116906014841015611089576001600160601b031960149490940360031b84901b1690921692915050565b8035602083101561087a575f19602084900360031b1b1692915050565b5f602082840312156115b7575f5ffd5b5051919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b63ffffffff8616815284602082015260a060408201525f61161060a08301866115be565b828103606084015261162281866115be565b91505060018060a01b03831660808301529695505050505050565b8181038181111561087a5761087a611506565b838152606060208201525f61166860608301856115be565b82810360408401526112e081856115be565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52603160045260245ffd5b5f81518060208401855e5f93019283525090919050565b5f610a0582846116a2565b5f602082840312156116d4575f5ffd5b81518015158114610a05575f5ffd5b61ffff60f01b8660f01b1681528460028201528360228201526bffffffffffffffffffffffff198360601b1660428201525f61172260568301846116a2565b97965050505050505056fea26469706673582212202eac49e721f6fda77d21268fd56b5767a9133fcb375a8416ce86956d0e42f5a764736f6c634300081b0033000000000000000000000000607eba808ef2685fac3da68ab96de961fa8f3312000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123
Deployed Bytecode
0x608060405260043610610110575f3560e01c80638da5cb5b1161009d578063efc21e3f11610062578063efc21e3f146102f5578063f10d8c971461030a578063f2fde38b14610329578063f772795914610348578063ffcbd8d014610369575f5ffd5b80638da5cb5b1461023b578063c34052e014610257578063d5438eae14610286578063de523cf3146102b9578063e68e00bf146102d6575f5ffd5b806346c96aac116100e357806346c96aac146101af57806356d5d475146101e2578063587faab6146101f5578063715018a6146102085780637f5a7c7b1461021c575f5ffd5b8063091d2788146101145780630e72cc0614610138578063380ef3fe146101595780633dfd387314610190575b5f5ffd5b34801561011f575f5ffd5b506202e6305b6040519081526020015b60405180910390f35b348015610143575f5ffd5b50610157610152366004611383565b61039c565b005b348015610164575f5ffd5b50600454610178906001600160a01b031681565b6040516001600160a01b03909116815260200161012f565b34801561019b575f5ffd5b506101576101aa366004611383565b6103ed565b3480156101ba575f5ffd5b506101787f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a112381565b6101576101f036600461139e565b61043e565b61015761020336600461142b565b610815565b348015610213575f5ffd5b5061015761085b565b348015610227575f5ffd5b50600354610178906001600160a01b031681565b348015610246575f5ffd5b505f546001600160a01b0316610178565b348015610262575f5ffd5b5061027661027136600461145d565b61086e565b604051901515815260200161012f565b348015610291575f5ffd5b506101787f0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e381565b3480156102c4575f5ffd5b506004546001600160a01b0316610178565b3480156102e1575f5ffd5b506101576102f036600461145d565b610880565b348015610300575f5ffd5b5061012561210581565b348015610315575f5ffd5b5061015761032436600461145d565b6108e9565b348015610334575f5ffd5b50610157610343366004611383565b610973565b348015610353575f5ffd5b5061035c6109b5565b60405161012f9190611474565b348015610374575f5ffd5b506101787f000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b81565b6103a46109c6565b600480546001600160a01b0319166001600160a01b0383169081179091556040517ffec811ed4e60aebdaf7a79cad8a97196bf56e35362f039705598226d30c9d248905f90a250565b6103f56109c6565b600380546001600160a01b0319166001600160a01b0383169081179091556040517f4eab7b127c764308788622363ad3e9532de3dfba7845bd4f84c125a22544255a905f90a250565b336001600160a01b037f0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e316146104875760405163079ddc6d60e31b815260040160405180910390fd5b3083146104a757604051637fea9dc560e01b815260040160405180910390fd5b6104bb600163ffffffff808716906109f216565b6104d85760405163aba4733960e01b815260040160405180910390fd5b5f5f6104e48484610a0c565b915091505f612105461461057f57604051631918e7f760e11b81526001600160a01b0384811660048301527f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a11231690633231cfee90602401602060405180830381865afa158015610556573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061057a91906114b6565b610607565b60405163929c8dcd60e01b81526001600160a01b0384811660048301527f00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123169063929c8dcd90602401602060405180830381865afa1580156105e3573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061060791906114b6565b90506001600160a01b0381161561074b576040516340c10f1960e01b8152306004820152602481018390527f000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b6001600160a01b0316906340c10f19906044015f604051808303815f87803b15801561067d575f5ffd5b505af115801561068f573d5f5f3e3d5ffd5b506106c99250506001600160a01b037f000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b1690508284610a7a565b60405163b66503cf60e01b81526001600160a01b037f000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b811660048301526024820184905282169063b66503cf906044015f604051808303815f87803b158015610730575f5ffd5b505af1158015610742573d5f5f3e3d5ffd5b505050506107c9565b6040516340c10f1960e01b81526001600160a01b038481166004830152602482018490527f000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b16906340c10f19906044015f604051808303815f87803b1580156107b2575f5ffd5b505af11580156107c4573d5f5f3e3d5ffd5b505050505b858763ffffffff167fecdc36fa3681f5d9c559dcbc399417db6f0ac0d81a78529685a1150265971d55348888604051610804939291906114d1565b60405180910390a350505050505050565b6040516001600160601b0319606085901b166020820152603481018390525f90605401604051602081830303815290604052905061085583858484610b01565b50505050565b6108636109c6565b61086c5f610de1565b565b5f61087a6001836109f2565b92915050565b6108886109c6565b6108936001826109f2565b6108b05760405163aba4733960e01b815260040160405180910390fd5b6108bb600182610e30565b5060405181907f4732a5e8ed8dad61e5185f5b8d342146071e8029d9055b85b5ff24078cee3d86905f90a250565b6108f16109c6565b4681036109115760405163057f3fa760e51b815260040160405180910390fd5b61091c6001826109f2565b1561093a57604051630ea075bf60e21b815260040160405180910390fd5b610945600182610e3b565b5060405181907f51f61861fc6e796ffd63c9c30190bb4a24477325376d9e326d507a75097f89e7905f90a250565b61097b6109c6565b6001600160a01b0381166109a957604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6109b281610de1565b50565b60606109c16001610e46565b905090565b5f546001600160a01b0316331461086c5760405163118cdaa760e01b81523360048201526024016109a0565b5f81815260018301602052604081205415155b9392505050565b5f80838184610a1c82601461151a565b92610a299392919061152d565b610a3291611554565b60601c8484610a425f601461151a565b90610a4e5f601461151a565b610a5990602061151a565b92610a669392919061152d565b610a6f9161158a565b909590945092505050565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa158015610ac7573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610aeb91906115a7565b90506108558484610afc858561151a565b610e52565b835f03610b2157604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038316610b485760405163d92e233d60e01b815260040160405180910390fd5b610b536001836109f2565b610b705760405163aba4733960e01b815260040160405180910390fd5b6003546001600160a01b03165f610b88823485610f05565b9050835f7f0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e36001600160a01b03166381d2ea9583308887896040518663ffffffff1660e01b8152600401610be09594939291906115ec565b602060405180830381865afa158015610bfb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c1f91906115a7565b905034811115610c4257604051631e9acf1760e31b815260040160405180910390fd5b604051632770a7eb60e21b8152336004820152602481018990527f000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b6001600160a01b031690639dc29fac906044015f604051808303815f87803b158015610ca7575f5ffd5b505af1158015610cb9573d5f5f3e3d5ffd5b50506040516242e0f760e61b81527f0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e36001600160a01b031692506310b83dc091508390610d1290869030908b908a908c906004016115ec565b60206040518083038185885af1158015610d2e573d5f5f3e3d5ffd5b50505050506040513d601f19601f82011682018060405250810190610d5391906115a7565b505f610d5f823461163d565b90508015610d9357604051339082156108fc029083905f818181858888f19350505050158015610d91573d5f5f3e3d5ffd5b505b308363ffffffff167fe8ed70f129c378298b9277a92cb6f0f821d501da841fb5a9f313c645b1da14e3848988604051610dce93929190611650565b60405180910390a3505050505050505050565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f610a058383610fa6565b5f610a058383611090565b60605f610a05836110dc565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610ea38482611135565b61085557604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610efb9085906111d2565b61085584826111d2565b60605f6001600160a01b03851615610f7c57846001600160a01b03166330fb74ab6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610f53573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f7791906115a7565b610f81565b6202e6305b9050610f9d84823360405180602001604052805f815250611238565b95945050505050565b5f8181526001830160205260408120548015611080575f610fc860018361163d565b85549091505f90610fdb9060019061163d565b905080821461103a575f865f018281548110610ff957610ff961167a565b905f5260205f200154905080875f0184815481106110195761101961167a565b5f918252602080832090910192909255918252600188019052604090208390555b855486908061104b5761104b61168e565b600190038181905f5260205f20015f90559055856001015f8681526020019081526020015f205f90556001935050505061087a565b5f91505061087a565b5092915050565b5f8181526001830160205260408120546110d557508154600181810184555f84815260208082209093018490558454848252828601909352604090209190915561087a565b505f61087a565b6060815f0180548060200260200160405190810160405280929190818152602001828054801561112957602002820191905f5260205f20905b815481526020019060010190808311611115575b50505050509050919050565b5f5f5f846001600160a01b03168460405161115091906116b9565b5f604051808303815f865af19150503d805f8114611189576040519150601f19603f3d011682016040523d82523d5f602084013e61118e565b606091505b50915091508180156111b85750805115806111b85750808060200190518101906111b891906116c4565b8015610f9d5750505050506001600160a01b03163b151590565b5f6111e66001600160a01b0384168361126d565b905080515f1415801561120a57508080602001905181019061120891906116c4565b155b1561123357604051635274afe760e01b81526001600160a01b03841660048201526024016109a0565b505050565b60606001858585856040516020016112549594939291906116e3565b6040516020818303038152906040529050949350505050565b6060610a0583835f845f5f856001600160a01b0316848660405161129191906116b9565b5f6040518083038185875af1925050503d805f81146112cb576040519150601f19603f3d011682016040523d82523d5f602084013e6112d0565b606091505b50915091506112e08683836112ea565b9695505050505050565b6060826112ff576112fa82611346565b610a05565b815115801561131657506001600160a01b0384163b155b1561133f57604051639996b31560e01b81526001600160a01b03851660048201526024016109a0565b5080610a05565b8051156113565780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b03811681146109b2575f5ffd5b5f60208284031215611393575f5ffd5b8135610a058161136f565b5f5f5f5f606085870312156113b1575f5ffd5b843563ffffffff811681146113c4575f5ffd5b935060208501359250604085013567ffffffffffffffff8111156113e6575f5ffd5b8501601f810187136113f6575f5ffd5b803567ffffffffffffffff81111561140c575f5ffd5b87602082840101111561141d575f5ffd5b949793965060200194505050565b5f5f5f6060848603121561143d575f5ffd5b83356114488161136f565b95602085013595506040909401359392505050565b5f6020828403121561146d575f5ffd5b5035919050565b602080825282518282018190525f918401906040840190835b818110156114ab57835183526020938401939092019160010161148d565b509095945050505050565b5f602082840312156114c6575f5ffd5b8151610a058161136f565b83815260406020820152816040820152818360608301375f818301606090810191909152601f909201601f1916010192915050565b634e487b7160e01b5f52601160045260245ffd5b8082018082111561087a5761087a611506565b5f5f8585111561153b575f5ffd5b83861115611547575f5ffd5b5050820193919092039150565b80356001600160601b03198116906014841015611089576001600160601b031960149490940360031b84901b1690921692915050565b8035602083101561087a575f19602084900360031b1b1692915050565b5f602082840312156115b7575f5ffd5b5051919050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b63ffffffff8616815284602082015260a060408201525f61161060a08301866115be565b828103606084015261162281866115be565b91505060018060a01b03831660808301529695505050505050565b8181038181111561087a5761087a611506565b838152606060208201525f61166860608301856115be565b82810360408401526112e081856115be565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52603160045260245ffd5b5f81518060208401855e5f93019283525090919050565b5f610a0582846116a2565b5f602082840312156116d4575f5ffd5b81518015158114610a05575f5ffd5b61ffff60f01b8660f01b1681528460028201528360228201526bffffffffffffffffffffffff198360601b1660428201525f61172260568301846116a2565b97965050505050505056fea26469706673582212202eac49e721f6fda77d21268fd56b5767a9133fcb375a8416ce86956d0e42f5a764736f6c634300081b0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000607eba808ef2685fac3da68ab96de961fa8f3312000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e3000000000000000000000000000000000000000000000000000000000000000000000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123
-----Decoded View---------------
Arg [0] : _owner (address): 0x607EbA808EF2685fAc3da68aB96De961fa8F3312
Arg [1] : _xerc20 (address): 0xafcc6AE807187A31E84138F3860D4CE27973e01b
Arg [2] : _mailbox (address): 0x2f9DB5616fa3fAd1aB06cB2C906830BA63d135e3
Arg [3] : _ism (address): 0x0000000000000000000000000000000000000000
Arg [4] : _voter (address): 0x97cDBCe21B6fd0585d29E539B1B99dAd328a1123
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000607eba808ef2685fac3da68ab96de961fa8f3312
Arg [1] : 000000000000000000000000afcc6ae807187a31e84138f3860d4ce27973e01b
Arg [2] : 0000000000000000000000002f9db5616fa3fad1ab06cb2c906830ba63d135e3
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 00000000000000000000000097cdbce21b6fd0585d29e539b1b99dad328a1123
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in FRAX
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.