019d5666-71ff-7060-88f6-c0d2b403a889
Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?
| ID | Type | Status | Attempts | Heartbeat |
|---|---|---|---|---|
| 42834 | artifact.fetch | complete | 1 / 3 | 1 week ago |
| 42835 | proxy.resolve | complete | 1 / 3 | 1 week ago |
| 42836 | tool.cast_disasm | complete | 1 / 3 | 1 week ago |
| 42837 | analysis.bundle | complete | 1 / 3 | 1 week ago |
| 42838 | capability.graph | complete | 1 / 3 | 1 week ago |
| 42839 | detector.run | complete | 1 / 3 | 1 week ago |
| 42840 | validation.fork | complete | 1 / 3 | 1 week ago |
| 42949 | report.consolidate | complete | 1 / 6 | 1 week ago |
{
"status": "not_proxy"
}
No slither job recorded yet.
{"no_findings":false,"summary":"Found 3 issues: multiple constructor-deployed contracts can be hijacked through permissionless initializers, mailbox-authenticated hooks can be triggered directly by anyone for the current latest message, and recipient-specific routing quotes do not match execution.","findings":[{"severity":"high","category":"auth","title":"Permissionless initializers let the first caller seize control and mint supply","exploit_scenario":"Several production contracts are deployed with constructors but defer critical setup to unrestricted `initializer` functions. If deployment and initialization are not atomic, an attacker can front-run the intended initializer call, become owner, install malicious hooks/ISMs/bridge endpoints, or mint the full initial token/NFT supply to themselves. For example, hijacking `Mailbox.initialize` gives the attacker control over `defaultIsm/defaultHook/requiredHook`, while hijacking `HypERC20.initialize` or `HypERC721.initialize` mints the initial supply to the attacker.","recommendation":"Do not expose public initialization for constructor-deployed instances. Move critical setup into the constructor or gate initialization to the constructor-established owner with a separate one-time guard. If these are meant to be proxy implementations, disable initializers on the implementation and initialize atomically via proxy constructor calldata.","evidence":"`initialize`/`setAuthorizedHook` are externally callable with only the OZ `initializer` modifier, which enforces one-time execution but does not authenticate the caller. Some of these initializers also mint to `msg.sender` or rewrite owner/configuration.","locations":["AUDIT_BUNDLE.txt:9360 (contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol)","AUDIT_BUNDLE.txt:10150 (contracts/hooks/igp/InterchainGasPaymaster.sol)","AUDIT_BUNDLE.txt:12612 (contracts/isms/routing/DomainRoutingIsm.sol)","AUDIT_BUNDLE.txt:12623 (contracts/isms/routing/DomainRoutingIsm.sol)","AUDIT_BUNDLE.txt:13188 (contracts/Mailbox.sol)","AUDIT_BUNDLE.txt:13664 (contracts/middleware/InterchainAccountRouter.sol)","AUDIT_BUNDLE.txt:14255 (contracts/middleware/InterchainQueryRouter.sol)","AUDIT_BUNDLE.txt:14545 (contracts/middleware/liquidity-layer/adapters/CircleBridgeAdapter.sol)","AUDIT_BUNDLE.txt:14862 (contracts/middleware/liquidity-layer/adapters/PortalAdapter.sol)","AUDIT_BUNDLE.txt:15074 (contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol)","AUDIT_BUNDLE.txt:16441 (contracts/token/HypERC20.sol)","AUDIT_BUNDLE.txt:16992 (contracts/token/HypERC721.sol)"]},{"severity":"high","category":"auth","title":"Anyone can call post-dispatch hooks directly for the current latest message","exploit_scenario":"After any legitimate dispatch, the full message bytes are public in the `Dispatch` event and `latestDispatchedId` stays set until the next dispatch. Because `AbstractPostDispatchHook.postDispatch` is permissionless, an attacker can call `MerkleTreeHook.postDispatch` directly and insert the same message ID into the Merkle tree again, corrupting checkpoint state. The same pattern also lets an attacker directly trigger message-id authorization hooks (`ERC5164Hook`/`OPStackHook`) for the current latest message, even if that message did not actually opt into those hooks through mailbox dispatch.","recommendation":"Require `msg.sender == address(mailbox)` for hooks whose security depends on mailbox state, or override `postDispatch` in those hooks to enforce mailbox-only access. Add single-use guards per message ID where duplicate execution would be unsafe.","evidence":"`AbstractPostDispatchHook.postDispatch` has no caller check. `MerkleTreeHook` and `AbstractMessageIdAuthHook` only verify that the supplied message matches `mailbox.latestDispatchedId()`, which any external caller can satisfy by replaying the just-emitted message bytes.","locations":["AUDIT_BUNDLE.txt:9226 (contracts/hooks/libs/AbstractPostDispatchHook.sol)","AUDIT_BUNDLE.txt:9489 (contracts/hooks/libs/AbstractMessageIdAuthHook.sol)","AUDIT_BUNDLE.txt:10753 (contracts/hooks/MerkleTreeHook.sol)"]},{"severity":"low","category":"other","title":"DestinationRecipientRoutingHook quotes the wrong hook for recipient-specific routes","exploit_scenario":"If the owner configures a `customHooks[destination][recipient]`, dispatch execution will route to that custom hook, but fee quoting still uses the destination-level hook from `DomainRoutingHook`. Users relying on `quoteDispatch` can underpay and have dispatches revert, making recipient-specific routes unusable unless callers manually overpay.","recommendation":"Override `_quoteDispatch` in `DestinationRecipientRoutingHook` so it resolves the same hook that `_postDispatch` will actually call, including recipient-specific overrides.","evidence":"`DestinationRecipientRoutingHook._postDispatch` consults `customHooks[destination][recipient]`, but inherited `DomainRoutingHook._quoteDispatch` only calls `_getConfiguredHook(message)`, which is destination-only and never checks the recipient override map.","locations":["AUDIT_BUNDLE.txt:11186 (contracts/hooks/routing/DomainRoutingHook.sol)","AUDIT_BUNDLE.txt:11238 (contracts/hooks/routing/DestinationRecipientRoutingHook.sol)"]}]}
Adapter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract PortalAdapter is ILiquidityLayerAdapter, Router {
/// @notice The Portal TokenBridge contract.
IPortalTokenBridge public portalTokenBridge;
/// @notice The LiquidityLayerRouter contract.
address public liquidityLayerRouter;
/// @notice Hyperlane domain => Wormhole domain.
mapping(uint32 => uint16) public hyperlaneDomainToWormholeDomain;
/// @notice transferId => token address
mapping(bytes32 => address) public portalTransfersProcessed;
// We could technically use Portal's sequence number here but it doesn't
// get passed through, so we would have to parse the VAA twice
// 224 bits should be large enough and allows us to pack into a single slot
// with a Hyperlane domain
uint224 public nonce = 0;
constructor(address _mailbox) Router(_mailbox) {}
/**
* @notice Emits the nonce of the Portal message when a token is bridged.
* @param nonce The nonce of the Portal message.
* @param portalSequence The sequence of the Portal message.
* @param destination The hyperlane domain of the destination
*/
event BridgedToken(
uint256 nonce,
uint64 portalSequence,
uint32 destination
);
/**
* @notice Emitted when the Hyperlane domain to Wormhole domain mapping is updated.
* @param hyperlaneDomain The Hyperlane domain.
* @param wormholeDomain The Wormhole domain.
*/
event DomainAdded(uint32 indexed hyperlaneDomain, uint32 wormholeDomain);
modifier onlyLiquidityLayerRouter() {
require(msg.sender == liquidityLayerRouter, "!liquidityLayerRouter");
_;
}
/**
* @param _owner The new owner.
* @param _portalTokenBridge The Portal TokenBridge contract.
* @param _liquidityLayerRouter The LiquidityLayerRouter contract.
*/
function initialize(
address _owner,
address _portalTokenBridge,
address _liquidityLayerRouter
) public initializer {
// Transfer ownership of the contract to deployer
_transferOwnership(_owner);
portalTokenBridge = IPortalTokenBridge(_portalTokenBridge);
liquidityLayerRouter = _liquidityLayerRouter;
}
/**
* Sends tokens as requested by the router
* @param _destinationDomain The hyperlane domain of the destination
* @param _token The token address
* @param _amount The amount of tokens to send
*/
function sendTokens(
uint32 _destinationDomain,
bytes32, // _recipientAddress, unused
address _token,
uint256 _amount
) external onlyLiquidityLayerRouter returns (bytes memory) {
nonce = nonce + 1;
uint16 _wormholeDomain = hyperlaneDomainToWormholeDomain[
_destinationDomain
];
bytes32 _remoteRouter = _mustHaveRemoteRouter(_destinationDomain);
// Approve the token to Portal. We assume that the LiquidityLayerRouter
// has already transferred the token to this contract.
require(
IERC20(_token).approve(address(portalTokenBridge), _amount),
"!approval"
);
uint64 _portalSequence = portalTokenBridge.transferTokensWithPayload(
_token,
_amount,
_wormholeDomain,
_remoteRouter,
// Nonce for grouping Portal messages in the same tx, not relevant for us
// https://book.wormhole.com/technical/evm/coreLayer.html#emitting-a-vaa
0,
// Portal Payload used in completeTransfer
abi.encode(localDomain, nonce)
);
emit BridgedToken(nonce, _portalSequence, _destinationDomain);
return abi.encode(nonce);
}
/**
* Sends the tokens to the recipient as requested by the router
* @param _originDomain The hyperlane domain of the origin
* @param _recipient The address of the recipient
* @param _amount The amount of tokens to send
* @param _adapterData The adapter data from the origin chain, containing the nonce
*/
function receiveTokens(
uint32 _originDomain, // Hyperlane domain
address _recipient,
uint256 _amount,
bytes calldata _adapterData // The adapter data from the message
) external onlyLiquidityLayerRouter returns (address, uint256) {
// Get the nonce information from the adapterData
uint224 _nonce = abi.decode(_adapterData, (uint224));
address _tokenAddress = portalTransfersProcessed[
transferId(_originDomain, _nonce)
];
require(
_tokenAddress != address(0x0),
"Portal Transfer has not yet been completed"
);
IERC20 _token = IERC20(_tokenAddress);
// Transfer the token out to the recipient
// TODO: use safeTransfer
// Portal doesn't charge any fee, so we can safely transfer out the
// exact amount that was bridged over.
require(_token.transfer(_recipient, _amount), "!transfer out");
return (_tokenAddress, _amount);
}
/**
* Completes the Portal transfer which sends the funds to this adapter.
* The router can call receiveTokens to move those funds to the ultimate recipient.
* @param encodedVm The VAA from the Wormhole Guardians
*/
function completeTransfer(bytes memory encodedVm) public {
bytes memory _tokenBridgeTransferWithPayload = portalTokenBridge
.completeTransferWithPayload(encodedVm);
IPortalTokenBridge.TransferWithPayload
memory _transfer = portalTokenBridge.parseTransferWithPayload(
_tokenBridgeTransferWithPayload
);
(uint32 _originDomain, uint224 _nonce) = abi.decode(
_transfer.payload,
(uint32, uint224)
);
// Logic taken from here https://github.com/wormhole-foundation/wormhole/blob/dev.v2/ethereum/contracts/bridge/Bridge.sol#L503
address tokenAddress = _transfer.tokenChain ==
hyperlaneDomainToWormholeDomain[localDomain]
? TypeCasts.bytes32ToAddress(_transfer.tokenAddress)
: portalTokenBridge.wrappedAsset(
_transfer.tokenChain,
_transfer.tokenAddress
);
portalTransfersProcessed[
transferId(_originDomain, _nonce)
] = tokenAddress;
}
// This contract is only a Router to be aware of remote router addresses,
// and doesn't actually send/handle Hyperlane messages directly
function _handle(
uint32, // origin
bytes32, // sender
bytes calldata // message
) internal pure override {
revert("No messages expected");
}
function addDomain(
uint32 _hyperlaneDomain,
uint16 _wormholeDomain
) external onlyOwner {
hyperlaneDomainToWormholeDomain[_hyperlaneDomain] = _wormholeDomain;
emit DomainAdded(_hyperlaneDomain, _wormholeDomain);
}
/**
* The key that is used to track fulfilled Portal transfers
* @param _hyperlaneDomain The hyperlane of the origin
* @param _nonce The nonce of the adapter on the origin
*/
function transferId(
uint32 _hyperlaneDomain,
uint224 _nonce
) public pure returns (bytes32) {
return bytes32(abi.encodePacked(_hyperlaneDomain, _nonce));
}
}
// File contracts/interfaces/ILiquidityLayerMessageRecipient.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
interface ILiquidityLayerMessageRecipient {
function handleWithTokens(
uint32 _origin,
bytes32 _sender,
bytes calldata _message,
address _token,
uint256 _amount
) external;
}
// File contracts/interfaces/ILiquidityLayerRouter.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.6.11;
interface ILiquidityLayerRouter {
function dispatchWithTokens(
uint32 _destinationDomain,
bytes32 _recipientAddress,
address _token,
uint256 _amount,
string calldata _bridge,
bytes calldata _messageBody
) external returns (bytes32);
}
// File contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract LiquidityLayerRouter is Router, ILiquidityLayerRouter {
using SafeERC20 for IERC20;
// Token bridge => adapter address
mapping(string => address) public liquidityLayerAdapters;
event LiquidityLayerAdapterSet(string indexed bridge, address adapter);
constructor(address _mailbox) Router(_mailbox) {}
/**
* @notice Initializes the Router contract with Hyperlane core contracts and the address of the interchain security module.
* @param _interchainGasPaymaster The address of the interchain gas paymaster contract.
* @param _interchainSecurityModule The address of the interchain security module contract.
* @param _owner The address with owner privileges.
*/
function initialize(
address _interchainGasPaymaster,
address _interchainSecurityModule,
address _owner
) external initializer {
_MailboxClient_initialize(
_interchainGasPaymaster,
_interchainSecurityModule,
_owner
);
}
function dispatchWithTokens(
uint32 _destinationDomain,
bytes32 _recipientAddress,
address _token,
uint256 _amount,
string calldata _bridge,
bytes calldata _messageBody
) external returns (bytes32) {
ILiquidityLayerAdapter _adapter = _getAdapter(_bridge);
// Transfer the tokens to the adapter
IERC20(_token).safeTransferFrom(msg.sender, address(_adapter), _amount);
// Reverts if the bridge was unsuccessful.
// Gets adapter-specific data that is encoded into the message
// ultimately sent via Hyperlane.
bytes memory _adapterData = _adapter.sendTokens(
_destinationDomain,
_recipientAddress,
_token,
_amount
);
// The user's message "wrapped" with metadata required by this middleware
bytes memory _messageWithMetadata = abi.encode(
TypeCasts.addressToBytes32(msg.sender),
_recipientAddress, // The "user" recipient
_amount, // The amount of the tokens sent over the bridge
_bridge, // The destination token bridge ID
_adapterData, // The adapter-specific data
_messageBody // The "user" message
);
// Dispatch the _messageWithMetadata to the destination's LiquidityLayerRouter.
return _dispatch(_destinationDomain, _messageWithMetadata);
}
// Handles a message from an enrolled remote LiquidityLayerRouter
function _handle(
uint32 _origin,
bytes32, // _sender, unused
bytes calldata _message
) internal override {
// Decode the message with metadata, "unwrapping" the user's message body
(
bytes32 _originalSender,
bytes32 _userRecipientAddress,
uint256 _amount,
string memory _bridge,
bytes memory _adapterData,
bytes memory _userMessageBody
) = abi.decode(
_message,
(bytes32, bytes32, uint256, string, bytes, bytes)
);
ILiquidityLayerMessageRecipient _userRecipient = ILiquidityLayerMessageRecipient(
TypeCasts.bytes32ToAddress(_userRecipientAddress)
);
// Reverts if the adapter hasn't received the bridged tokens yet
(address _token, uint256 _receivedAmount) = _getAdapter(_bridge)
.receiveTokens(
_origin,
address(_userRecipient),
_amount,
_adapterData
);
if (_userMessageBody.length > 0) {
_userRecipient.handleWithTokens(
_origin,
_originalSender,
_userMessageBody,
_token,
_receivedAmount
);
}
}
function setLiquidityLayerAdapter(
string calldata _bridge,
address _adapter
) external onlyOwner {
liquidityLayerAdapters[_bridge] = _adapter;
emit LiquidityLayerAdapterSet(_bridge, _adapter);
}
function _getAdapter(
string memory _bridge
) internal view returns (ILiquidityLayerAdapter _adapter) {
_adapter = ILiquidityLayerAdapter(liquidityLayerAdapters[_bridge]);
// Require the adapter to have been set
require(address(_adapter) != address(0), "No adapter found for bridge");
}
exec
/bin/bash -lc "sed -n '15181,17410p' AUDIT_BUNDLE.txt" in /tmp/codex-3gxliacl
succeeded in 0ms:
}
// File contracts/mock/MockToken.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract MockToken is ERC20Upgradeable {
function mint(address account, uint256 amount) external {
_mint(account, amount);
}
function burn(uint256 _amount) external {
_burn(msg.sender, _amount);
}
}
// File contracts/mock/MockCircleMessageTransmitter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract MockCircleMessageTransmitter is ICircleMessageTransmitter {
mapping(bytes32 => bool) processedNonces;
MockToken token;
constructor(MockToken _token) {
token = _token;
}
function receiveMessage(
bytes memory,
bytes calldata
) external pure returns (bool success) {
success = true;
}
function hashSourceAndNonce(
uint32 _source,
uint64 _nonce
) public pure returns (bytes32) {
return keccak256(abi.encodePacked(_source, _nonce));
}
function process(
bytes32 _nonceId,
address _recipient,
uint256 _amount
) public {
processedNonces[_nonceId] = true;
token.mint(_recipient, _amount);
}
function usedNonces(bytes32 _nonceId) external view returns (bool) {
return processedNonces[_nonceId];
}
}
// File contracts/mock/MockCircleTokenMessenger.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract MockCircleTokenMessenger is ITokenMessenger {
uint64 public nextNonce = 0;
MockToken token;
constructor(MockToken _token) {
token = _token;
}
function depositForBurn(
uint256 _amount,
uint32,
bytes32,
address _burnToken
) external returns (uint64 _nonce) {
nextNonce = nextNonce + 1;
_nonce = nextNonce;
require(address(token) == _burnToken);
token.transferFrom(msg.sender, address(this), _amount);
token.burn(_amount);
}
function depositForBurnWithCaller(
uint256,
uint32,
bytes32,
address,
bytes32
) external returns (uint64 _nonce) {
nextNonce = nextNonce + 1;
_nonce = nextNonce;
}
}
// File contracts/mock/MockERC5164.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity ^0.8.13;
contract MockMessageDispatcher is IMessageDispatcher {
function dispatchMessage(
uint256 toChainId,
address to,
bytes calldata data
) external returns (bytes32) {
bytes32 messageId = keccak256(abi.encodePacked(toChainId, to, data));
// simulate a successful dispatch
emit MessageDispatched(messageId, msg.sender, toChainId, to, data);
return messageId;
}
}
contract MockMessageExecutor {
event MessageIdExecuted(
uint256 indexed fromChainId,
bytes32 indexed messageId
);
}
// File contracts/test/TestIsm.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.6.11;
contract TestIsm is IInterchainSecurityModule {
uint8 public moduleType = uint8(Types.NULL);
bool verifyResult = true;
function setVerify(bool _verify) public {
verifyResult = _verify;
}
function verify(bytes calldata, bytes calldata) public view returns (bool) {
return verifyResult;
}
}
// File contracts/test/TestPostDispatchHook.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
contract TestPostDispatchHook is AbstractPostDispatchHook {
// ============ Public Storage ============
// test fees for quoteDispatch
uint256 public fee = 0;
// ============ External Functions ============
/// @inheritdoc IPostDispatchHook
function hookType() external pure override returns (uint8) {
return uint8(IPostDispatchHook.Types.UNUSED);
}
function supportsMetadata(
bytes calldata
) public pure override returns (bool) {
return true;
}
function setFee(uint256 _fee) external {
fee = _fee;
}
// ============ Internal functions ============
function _postDispatch(
bytes calldata,
/*metadata*/
bytes calldata /*message*/
) internal pure override {
// test - empty
}
function _quoteDispatch(
bytes calldata,
/*metadata*/
bytes calldata /*message*/
) internal view override returns (uint256) {
return fee;
}
}
// File contracts/mock/MockMailbox.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity ^0.8.0;
contract MockMailbox is Mailbox {
using Message for bytes;
uint32 public inboundUnprocessedNonce = 0;
uint32 public inboundProcessedNonce = 0;
mapping(uint32 => MockMailbox) public remoteMailboxes;
mapping(uint256 => bytes) public inboundMessages;
constructor(uint32 _domain) Mailbox(_domain) {
TestIsm ism = new TestIsm();
defaultIsm = ism;
TestPostDispatchHook hook = new TestPostDispatchHook();
defaultHook = hook;
requiredHook = hook;
_transferOwnership(msg.sender);
_disableInitializers();
}
function addRemoteMailbox(uint32 _domain, MockMailbox _mailbox) external {
remoteMailboxes[_domain] = _mailbox;
}
function dispatch(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata messageBody,
bytes calldata metadata,
IPostDispatchHook hook
) public payable override returns (bytes32) {
bytes memory message = _buildMessage(
destinationDomain,
recipientAddress,
messageBody
);
bytes32 id = super.dispatch(
destinationDomain,
recipientAddress,
messageBody,
metadata,
hook
);
MockMailbox _destinationMailbox = remoteMailboxes[destinationDomain];
require(
address(_destinationMailbox) != address(0),
"Missing remote mailbox"
);
_destinationMailbox.addInboundMessage(message);
return id;
}
function addInboundMessage(bytes calldata message) external {
inboundMessages[inboundUnprocessedNonce] = message;
inboundUnprocessedNonce++;
}
function processNextInboundMessage() public {
bytes memory _message = inboundMessages[inboundProcessedNonce];
Mailbox(address(this)).process("", _message);
inboundProcessedNonce++;
}
}
// File contracts/test/TestInterchainGasPaymaster.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
// ============ Internal Imports ============
contract TestInterchainGasPaymaster is InterchainGasPaymaster {
uint256 public constant gasPrice = 10;
constructor() {
initialize(msg.sender, msg.sender);
}
function quoteGasPayment(
uint32,
uint256 gasAmount
) public pure override returns (uint256) {
return gasPrice * gasAmount;
}
}
// File contracts/mock/MockHyperlaneEnvironment.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract MockHyperlaneEnvironment {
uint32 originDomain;
uint32 destinationDomain;
mapping(uint32 => MockMailbox) public mailboxes;
mapping(uint32 => TestInterchainGasPaymaster) public igps;
mapping(uint32 => IInterchainSecurityModule) public isms;
constructor(uint32 _originDomain, uint32 _destinationDomain) {
originDomain = _originDomain;
destinationDomain = _destinationDomain;
MockMailbox originMailbox = new MockMailbox(_originDomain);
MockMailbox destinationMailbox = new MockMailbox(_destinationDomain);
originMailbox.addRemoteMailbox(_destinationDomain, destinationMailbox);
destinationMailbox.addRemoteMailbox(_originDomain, originMailbox);
isms[originDomain] = new TestIsm();
isms[destinationDomain] = new TestIsm();
originMailbox.setDefaultIsm(address(isms[originDomain]));
destinationMailbox.setDefaultIsm(address(isms[destinationDomain]));
igps[originDomain] = new TestInterchainGasPaymaster();
igps[destinationDomain] = new TestInterchainGasPaymaster();
originMailbox.transferOwnership(msg.sender);
destinationMailbox.transferOwnership(msg.sender);
mailboxes[_originDomain] = originMailbox;
mailboxes[_destinationDomain] = destinationMailbox;
}
function processNextPendingMessage() public {
mailboxes[destinationDomain].processNextInboundMessage();
}
function processNextPendingMessageFromDestination() public {
mailboxes[originDomain].processNextInboundMessage();
}
}
// File contracts/mock/MockPortalBridge.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract MockPortalBridge is IPortalTokenBridge {
uint256 nextNonce = 0;
MockToken token;
constructor(MockToken _token) {
token = _token;
}
function transferTokensWithPayload(
address,
uint256 amount,
uint16,
bytes32,
uint32,
bytes memory
) external payable returns (uint64 sequence) {
nextNonce = nextNonce + 1;
token.transferFrom(msg.sender, address(this), amount);
token.burn(amount);
return uint64(nextNonce);
}
function wrappedAsset(uint16, bytes32) external view returns (address) {
return address(token);
}
function isWrappedAsset(address) external pure returns (bool) {
return true;
}
function completeTransferWithPayload(
bytes memory encodedVm
) external returns (bytes memory) {
(uint32 _originDomain, uint224 _nonce, uint256 _amount) = abi.decode(
encodedVm,
(uint32, uint224, uint256)
);
token.mint(msg.sender, _amount);
// Format it so that parseTransferWithPayload returns the desired payload
return
abi.encode(
TypeCasts.addressToBytes32(address(token)),
adapterData(_originDomain, _nonce, address(token))
);
}
function parseTransferWithPayload(
bytes memory encoded
) external pure returns (TransferWithPayload memory transfer) {
(bytes32 tokenAddress, bytes memory payload) = abi.decode(
encoded,
(bytes32, bytes)
);
transfer.payload = payload;
transfer.tokenAddress = tokenAddress;
}
function adapterData(
uint32 _originDomain,
uint224 _nonce,
address _token
) public pure returns (bytes memory) {
return
abi.encode(
_originDomain,
_nonce,
TypeCasts.addressToBytes32(_token)
);
}
function mockPortalVaa(
uint32 _originDomain,
uint224 _nonce,
uint256 _amount
) public pure returns (bytes memory) {
return abi.encode(_originDomain, _nonce, _amount);
}
}
// File contracts/test/ERC20Test.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
contract ERC20Test is ERC20 {
uint8 public immutable _decimals;
constructor(
string memory name,
string memory symbol,
uint256 totalSupply,
uint8 __decimals
) ERC20(name, symbol) {
_decimals = __decimals;
_mint(msg.sender, totalSupply);
}
function decimals() public view override returns (uint8) {
return _decimals;
}
}
// File contracts/test/ERC721Test.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
contract ERC721Test is ERC721Enumerable {
constructor(
string memory name,
string memory symbol,
uint256 _mintAmount
) ERC721(name, symbol) {
for (uint256 i = 0; i < _mintAmount; i++) {
_mint(msg.sender, i);
}
}
function _baseURI() internal pure override returns (string memory) {
return "TEST-BASE-URI";
}
}
// File contracts/test/TestRecipient.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
contract TestRecipient is
Ownable,
IMessageRecipient,
ISpecifiesInterchainSecurityModule
{
IInterchainSecurityModule public interchainSecurityModule;
bytes32 public lastSender;
bytes public lastData;
address public lastCaller;
string public lastCallMessage;
event ReceivedMessage(
uint32 indexed origin,
bytes32 indexed sender,
uint256 indexed value,
string message
);
event ReceivedCall(address indexed caller, uint256 amount, string message);
function handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _data
) external payable virtual override {
emit ReceivedMessage(_origin, _sender, msg.value, string(_data));
lastSender = _sender;
lastData = _data;
}
function fooBar(uint256 amount, string calldata message) external {
emit ReceivedCall(msg.sender, amount, message);
lastCaller = msg.sender;
lastCallMessage = message;
}
function setInterchainSecurityModule(address _ism) external onlyOwner {
interchainSecurityModule = IInterchainSecurityModule(_ism);
}
}
// File contracts/test/LightTestRecipient.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.6.11;
contract LightTestRecipient is TestRecipient {
// solhint-disable-next-line no-empty-blocks
function handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _data
) external payable override {
// do nothing
}
}
// File contracts/test/TestGasRouter.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.6.11;
contract TestGasRouter is GasRouter {
constructor(address _mailbox) GasRouter(_mailbox) {}
function dispatch(uint32 _destination, bytes memory _msg) external payable {
_dispatch(_destination, _msg);
}
function _handle(uint32, bytes32, bytes calldata) internal pure override {}
}
// File contracts/test/TestLiquidityLayerMessageRecipient.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract TestLiquidityLayerMessageRecipient is ILiquidityLayerMessageRecipient {
event HandledWithTokens(
uint32 origin,
bytes32 sender,
bytes message,
address token,
uint256 amount
);
function handleWithTokens(
uint32 _origin,
bytes32 _sender,
bytes calldata _message,
address _token,
uint256 _amount
) external {
emit HandledWithTokens(_origin, _sender, _message, _token, _amount);
}
}
// File contracts/test/TestMailbox.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
contract TestMailbox is Mailbox {
using TypeCasts for bytes32;
constructor(uint32 _localDomain) Mailbox(_localDomain) {
_transferOwnership(msg.sender);
}
function testHandle(
uint32 _origin,
bytes32 _sender,
bytes32 _recipient,
bytes calldata _body
) external {
IMessageRecipient(_recipient.bytes32ToAddress()).handle(
_origin,
_sender,
_body
);
}
function buildOutboundMessage(
uint32 destinationDomain,
bytes32 recipientAddress,
bytes calldata body
) external view returns (bytes memory) {
return _buildMessage(destinationDomain, recipientAddress, body);
}
function buildInboundMessage(
uint32 originDomain,
bytes32 recipientAddress,
bytes32 senderAddress,
bytes calldata body
) external view returns (bytes memory) {
return
Message.formatMessage(
VERSION,
nonce,
originDomain,
senderAddress,
localDomain,
recipientAddress,
body
);
}
function updateLatestDispatchedId(bytes32 _id) external {
latestDispatchedId = _id;
}
}
// File contracts/test/TestMerkle.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
contract TestMerkle {
using MerkleLib for MerkleLib.Tree;
MerkleLib.Tree public tree;
// solhint-disable-next-line no-empty-blocks
constructor() {}
function insert(bytes32 _node) external {
tree.insert(_node);
}
function branchRoot(
bytes32 _leaf,
bytes32[32] calldata _proof,
uint256 _index
) external pure returns (bytes32 _node) {
return MerkleLib.branchRoot(_leaf, _proof, _index);
}
/**
* @notice Returns the number of inserted leaves in the tree
*/
function count() public view returns (uint256) {
return tree.count;
}
function root() public view returns (bytes32) {
return tree.root();
}
}
// File contracts/test/TestMerkleTreeHook.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
contract TestMerkleTreeHook is MerkleTreeHook {
using MerkleLib for MerkleLib.Tree;
constructor(address _mailbox) MerkleTreeHook(_mailbox) {}
function proof() external view returns (bytes32[32] memory) {
bytes32[32] memory _zeroes = MerkleLib.zeroHashes();
uint256 _index = _tree.count - 1;
bytes32[32] memory _proof;
for (uint256 i = 0; i < 32; i++) {
uint256 _ithBit = (_index >> i) & 0x01;
if (_ithBit == 1) {
_proof[i] = _tree.branch[i];
} else {
_proof[i] = _zeroes[i];
}
}
return _proof;
}
function insert(bytes32 _id) external {
_tree.insert(_id);
}
}
// File contracts/test/TestMessage.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.6.11;
contract TestMessage {
using Message for bytes;
function version(
bytes calldata _message
) external pure returns (uint32 _version) {
return _message.version();
}
function nonce(
bytes calldata _message
) external pure returns (uint256 _nonce) {
return _message.nonce();
}
function body(
bytes calldata _message
) external pure returns (bytes calldata _body) {
return _message.body();
}
function origin(
bytes calldata _message
) external pure returns (uint32 _origin) {
return _message.origin();
}
function sender(
bytes calldata _message
) external pure returns (bytes32 _sender) {
return _message.sender();
}
function destination(
bytes calldata _message
) external pure returns (uint32 _destination) {
return _message.destination();
}
function recipient(
bytes calldata _message
) external pure returns (bytes32 _recipient) {
return _message.recipient();
}
function recipientAddress(
bytes calldata _message
) external pure returns (address _recipient) {
return _message.recipientAddress();
}
function id(bytes calldata _message) external pure returns (bytes32) {
return _message.id();
}
}
// File contracts/test/TestQuery.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
contract TestQuery {
InterchainQueryRouter public router;
event Owner(uint256, address);
constructor(address _router) {
router = InterchainQueryRouter(_router);
}
/**
* @dev Fetches owner of InterchainQueryRouter on provided domain and passes along with provided secret to `this.receiveRouterOwner`
*/
function queryRouterOwner(uint32 domain, uint256 secret) external {
address target = TypeCasts.bytes32ToAddress(router.routers(domain));
CallLib.StaticCallWithCallback[]
memory calls = new CallLib.StaticCallWithCallback[](1);
calls[0] = CallLib.build(
target,
abi.encodeWithSelector(Ownable.owner.selector),
abi.encodeWithSelector(this.receiveRouterOwner.selector, secret)
);
router.query(domain, calls);
}
/**
* @dev `msg.sender` must be restricted to `this.router` to prevent any local account from spoofing query data.
*/
function receiveRouterOwner(uint256 secret, address owner) external {
require(msg.sender == address(router), "TestQuery: not from router");
emit Owner(secret, owner);
}
}
// File contracts/test/TestQuerySender.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
contract TestQuerySender {
InterchainQueryRouter queryRouter;
address public lastAddressResult;
uint256 public lastUint256Result;
bytes32 public lastBytes32Result;
event ReceivedAddressResult(address result);
event ReceivedUint256Result(uint256 result);
event ReceivedBytes32Result(bytes32 result);
function initialize(address _queryRouterAddress) external {
queryRouter = InterchainQueryRouter(_queryRouterAddress);
}
function queryAddress(
uint32 _destinationDomain,
address _target,
bytes calldata _targetData,
uint256 _gasAmount
) external payable {
queryAndPayFor(
_destinationDomain,
_target,
_targetData,
this.handleQueryAddressResult.selector,
_gasAmount
);
}
function handleQueryAddressResult(address _result) external {
emit ReceivedAddressResult(_result);
lastAddressResult = _result;
}
function queryUint256(
uint32 _destinationDomain,
address _target,
bytes calldata _targetData,
uint256 _gasAmount
) external payable {
queryAndPayFor(
_destinationDomain,
_target,
_targetData,
this.handleQueryUint256Result.selector,
_gasAmount
);
}
function handleQueryUint256Result(uint256 _result) external {
emit ReceivedUint256Result(_result);
lastUint256Result = _result;
}
function queryBytes32(
uint32 _destinationDomain,
address _target,
bytes calldata _targetData,
uint256 _gasAmount
) external payable {
queryAndPayFor(
_destinationDomain,
_target,
_targetData,
this.handleQueryBytes32Result.selector,
_gasAmount
);
}
function handleQueryBytes32Result(bytes32 _result) external {
emit ReceivedBytes32Result(_result);
lastBytes32Result = _result;
}
function queryAndPayFor(
uint32 _destinationDomain,
address _target,
bytes calldata _targetData,
bytes4 _callbackSelector,
uint256 /*_gasAmount*/
) internal {
queryRouter.query(
_destinationDomain,
_target,
_targetData,
abi.encodePacked(_callbackSelector)
);
}
}
// File contracts/test/TestRouter.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.6.11;
contract TestRouter is Router {
event InitializeOverload();
constructor(address _mailbox) Router(_mailbox) {}
function initialize(
address _hook,
address _interchainSecurityModule
) public initializer {
_MailboxClient_initialize(_hook, _interchainSecurityModule, msg.sender);
}
function _handle(uint32, bytes32, bytes calldata) internal pure override {}
function isRemoteRouter(
uint32 _domain,
bytes32 _potentialRemoteRouter
) external view returns (bool) {
return _isRemoteRouter(_domain, _potentialRemoteRouter);
}
function mustHaveRemoteRouter(
uint32 _domain
) external view returns (bytes32) {
return _mustHaveRemoteRouter(_domain);
}
function dispatch(uint32 _destination, bytes memory _msg) external payable {
_dispatch(_destination, _msg);
}
}
// File contracts/test/TestSendReceiver.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
contract TestSendReceiver is IMessageRecipient {
using TypeCasts for address;
uint256 public constant HANDLE_GAS_AMOUNT = 50_000;
event Handled(bytes32 blockHash);
function dispatchToSelf(
IMailbox _mailbox,
uint32 _destinationDomain,
bytes calldata _messageBody
) external payable {
// TODO: handle topping up?
_mailbox.dispatch{value: msg.value}(
_destinationDomain,
address(this).addressToBytes32(),
_messageBody
);
}
function dispatchToSelf(
IMailbox _mailbox,
uint32 _destinationDomain,
bytes calldata _messageBody,
IPostDispatchHook hook
) external payable {
// TODO: handle topping up?
_mailbox.dispatch{value: msg.value}(
_destinationDomain,
address(this).addressToBytes32(),
_messageBody,
bytes(""),
hook
);
}
function handle(uint32, bytes32, bytes calldata) external payable override {
bytes32 blockHash = previousBlockHash();
bool isBlockHashEndIn0 = uint256(blockHash) % 16 == 0;
require(!isBlockHashEndIn0, "block hash ends in 0");
emit Handled(blockHash);
}
function previousBlockHash() internal view returns (bytes32) {
return blockhash(block.number - 1);
}
}
// File contracts/test/TestTokenRecipient.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
contract TestTokenRecipient is ILiquidityLayerMessageRecipient {
bytes32 public lastSender;
bytes public lastData;
address public lastToken;
uint256 public lastAmount;
address public lastCaller;
string public lastCallMessage;
event ReceivedMessage(
uint32 indexed origin,
bytes32 indexed sender,
string message,
address token,
uint256 amount
);
event ReceivedCall(address indexed caller, uint256 amount, string message);
function handleWithTokens(
uint32 _origin,
bytes32 _sender,
bytes calldata _data,
address _token,
uint256 _amount
) external override {
emit ReceivedMessage(_origin, _sender, string(_data), _token, _amount);
lastSender = _sender;
lastData = _data;
lastToken = _token;
lastAmount = _amount;
}
function fooBar(uint256 amount, string calldata message) external {
emit ReceivedCall(msg.sender, amount, message);
lastCaller = msg.sender;
lastCallMessage = message;
}
}
// File contracts/token/libs/TokenMessage.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
library TokenMessage {
function format(
bytes32 _recipient,
uint256 _amount,
bytes memory _metadata
) internal pure returns (bytes memory) {
return abi.encodePacked(_recipient, _amount, _metadata);
}
function recipient(bytes calldata message) internal pure returns (bytes32) {
return bytes32(message[0:32]);
}
function amount(bytes calldata message) internal pure returns (uint256) {
return uint256(bytes32(message[32:64]));
}
// alias for ERC721
function tokenId(bytes calldata message) internal pure returns (uint256) {
return amount(message);
}
function metadata(
bytes calldata message
) internal pure returns (bytes calldata) {
return message[64:];
}
}
// File contracts/token/libs/TokenRouter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane Token Router that extends Router with abstract token (ERC20/ERC721) remote transfer functionality.
* @author Abacus Works
*/
abstract contract TokenRouter is GasRouter {
using TypeCasts for bytes32;
using TypeCasts for address;
using TokenMessage for bytes;
/**
* @dev Emitted on `transferRemote` when a transfer message is dispatched.
* @param destination The identifier of the destination chain.
* @param recipient The address of the recipient on the destination chain.
* @param amount The amount of tokens burnt on the origin chain.
*/
event SentTransferRemote(
uint32 indexed destination,
bytes32 indexed recipient,
uint256 amount
);
/**
* @dev Emitted on `_handle` when a transfer message is processed.
* @param origin The identifier of the origin chain.
* @param recipient The address of the recipient on the destination chain.
* @param amount The amount of tokens minted on the destination chain.
*/
event ReceivedTransferRemote(
uint32 indexed origin,
bytes32 indexed recipient,
uint256 amount
);
constructor(address _mailbox) GasRouter(_mailbox) {}
/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_transferFromSender` implementation.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @return messageId The identifier of the dispatched message.
*/
function transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId
) external payable virtual returns (bytes32 messageId) {
return
_transferRemote(_destination, _recipient, _amountOrId, msg.value);
}
/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_transferFromSender` implementation.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @param _gasPayment The amount of native token to pay for interchain gas.
* @return messageId The identifier of the dispatched message.
*/
function _transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _gasPayment
) internal returns (bytes32 messageId) {
bytes memory metadata = _transferFromSender(_amountOrId);
messageId = _dispatch(
_destination,
_gasPayment,
TokenMessage.format(_recipient, _amountOrId, metadata)
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
}
/**
* @dev Should transfer `_amountOrId` of tokens from `msg.sender` to this token router.
* @dev Called by `transferRemote` before message dispatch.
* @dev Optionally returns `metadata` associated with the transfer to be passed in message.
*/
function _transferFromSender(
uint256 _amountOrId
) internal virtual returns (bytes memory metadata);
/**
* @notice Returns the balance of `account` on this token router.
* @param account The address to query the balance of.
* @return The balance of `account`.
*/
function balanceOf(address account) external virtual returns (uint256);
/**
* @dev Mints tokens to recipient when router receives transfer message.
* @dev Emits `ReceivedTransferRemote` event on the destination chain.
* @param _origin The identifier of the origin chain.
* @param _message The encoded remote transfer message containing the recipient address and amount.
*/
function _handle(
uint32 _origin,
bytes32,
bytes calldata _message
) internal virtual override {
bytes32 recipient = _message.recipient();
uint256 amount = _message.amount();
bytes calldata metadata = _message.metadata();
_transferTo(recipient.bytes32ToAddress(), amount, metadata);
emit ReceivedTransferRemote(_origin, recipient, amount);
}
/**
* @dev Should transfer `_amountOrId` of tokens from this token router to `_recipient`.
* @dev Called by `handle` after message decoding.
* @dev Optionally handles `metadata` associated with transfer passed in message.
*/
function _transferTo(
address _recipient,
uint256 _amountOrId,
bytes calldata metadata
) internal virtual;
}
// File contracts/token/HypERC20.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC20 Token Router that extends ERC20 with remote transfer functionality.
* @author Abacus Works
* @dev Supply on each chain is not constant but the aggregate supply across all chains is.
*/
contract HypERC20 is ERC20Upgradeable, TokenRouter {
uint8 private immutable _decimals;
constructor(uint8 __decimals, address _mailbox) TokenRouter(_mailbox) {
_decimals = __decimals;
}
/**
* @notice Initializes the Hyperlane router, ERC20 metadata, and mints initial supply to deployer.
* @param _totalSupply The initial supply of the token.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
*/
function initialize(
uint256 _totalSupply,
string memory _name,
string memory _symbol
) external initializer {
// Initialize ERC20 metadata
__ERC20_init(_name, _symbol);
_mint(msg.sender, _totalSupply);
}
function decimals() public view override returns (uint8) {
return _decimals;
}
function balanceOf(
address _account
)
public
view
virtual
override(TokenRouter, ERC20Upgradeable)
returns (uint256)
{
return ERC20Upgradeable.balanceOf(_account);
}
/**
* @dev Burns `_amount` of token from `msg.sender` balance.
* @inheritdoc TokenRouter
*/
function _transferFromSender(
uint256 _amount
) internal override returns (bytes memory) {
_burn(msg.sender, _amount);
return bytes(""); // no metadata
}
/**
* @dev Mints `_amount` of token to `_recipient` balance.
* @inheritdoc TokenRouter
*/
function _transferTo(
address _recipient,
uint256 _amount,
bytes calldata // no metadata
) internal virtual override {
_mint(_recipient, _amount);
}
}
// File contracts/token/libs/FastTokenRouter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Common FastTokenRouter functionality for ERC20 Tokens with remote transfer support.
* @author Abacus Works
*/
abstract contract FastTokenRouter is TokenRouter {
using TypeCasts for bytes32;
using TokenMessage for bytes;
uint256 public fastTransferId;
// maps `fastTransferId` to the filler address.
mapping(bytes32 => address) filledFastTransfers;
/**
* @dev delegates transfer logic to `_transferTo`.
* @inheritdoc TokenRouter
*/
function _handle(
uint32 _origin,
bytes32,
bytes calldata _message
) internal virtual override {
bytes32 recipient = _message.recipient();
uint256 amount = _message.amount();
bytes calldata metadata = _message.metadata();
_transferTo(recipient.bytes32ToAddress(), amount, _origin, metadata);
emit ReceivedTransferRemote(_origin, recipient, amount);
}
/**
* @dev Transfers `_amount` of token to `_recipient`/`fastFiller` who provided LP.
* @dev Called by `handle` after message decoding.
*/
function _transferTo(
address _recipient,
uint256 _amount,
uint32 _origin,
bytes calldata _metadata
) internal virtual {
address _tokenRecipient = _getTokenRecipient(
_recipient,
_amount,
_origin,
_metadata
);
_fastTransferTo(_tokenRecipient, _amount);
}
/**
* @dev allows an external user to full an unfilled fast transfer order.
* @param _recipient The recepient of the wrapped token on base chain.
* @param _amount The amount of wrapped tokens that is being bridged.
* @param _fastFee The fee the bridging entity will pay.
* @param _fastTransferId Id assigned on the remote chain to uniquely identify the transfer.
*/
function fillFastTransfer(
address _recipient,
uint256 _amount,
uint256 _fastFee,
uint32 _origin,
uint256 _fastTransferId
) external virtual {
bytes32 filledFastTransfersKey = _getFastTransfersKey(
_origin,
_fastTransferId,
_amount,
_fastFee,
_recipient
);
require(
filledFastTransfers[filledFastTransfersKey] == address(0),
"request already filled"
);
filledFastTransfers[filledFastTransfersKey] = msg.sender;
_fastRecieveFrom(msg.sender, _amount - _fastFee);
_fastTransferTo(_recipient, _amount - _fastFee);
}
/**
* @dev Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_fastTransferFromSender` implementation.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @return messageId The identifier of the dispatched message.
*/
function fastTransferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _fastFee
) public payable virtual returns (bytes32 messageId) {
uint256 _fastTransferId = fastTransferId + 1;
fastTransferId = _fastTransferId;
bytes memory metadata = _fastTransferFromSender(
_amountOrId,
_fastFee,
_fastTransferId
);
messageId = _dispatch(
_destination,
TokenMessage.format(_recipient, _amountOrId, metadata)
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
}
/**
* @dev Burns `_amount` of token from `msg.sender` balance.
* @dev Pays `_fastFee` of tokens to LP on source chain.
* @dev Returns `fastFee` as bytes in the form of metadata.
*/
function _fastTransferFromSender(
uint256 _amount,
uint256 _fastFee,
uint256 _fastTransferId
) internal virtual returns (bytes memory) {
_fastRecieveFrom(msg.sender, _amount);
return abi.encode(_fastFee, _fastTransferId);
}
/**
* @dev returns an address that indicates who should recieve the bridged tokens.
* @dev if _fastFees was inlcuded and someone filled the order before the mailbox made the contract call, the filler gets the funds.
*/
function _getTokenRecipient(
address _recipient,
uint256 _amount,
uint32 _origin,
bytes calldata _metadata
) internal view returns (address) {
if (_metadata.length == 0) {
return _recipient;
}
// decode metadata to extract `_fastFee` and `_fastTransferId`.
(uint256 _fastFee, uint256 _fastTransferId) = abi.decode(
_metadata,
(uint256, uint256)
);
address _fillerAddress = filledFastTransfers[
_getFastTransfersKey(
_origin,
_fastTransferId,
_amount,
_fastFee,
_recipient
)
];
if (_fillerAddress != address(0)) {
return _fillerAddress;
}
return _recipient;
}
/**
* @dev generates the key for storing the filler address of fast transfers.
*/
function _getFastTransfersKey(
uint32 _origin,
uint256 _fastTransferId,
uint256 _amount,
uint256 _fastFee,
address _recipient
) internal pure returns (bytes32) {
return
keccak256(
abi.encodePacked(
_origin,
_fastTransferId,
_amount,
_fastFee,
_recipient
)
);
}
/**
* @dev Should transfer `_amount` of tokens to `_recipient`.
* @dev The implementation is delegated.
*/
function _fastTransferTo(
address _recipient,
uint256 _amount
) internal virtual;
/**
* @dev Should collect `amount` of tokens from `_sender`.
* @dev The implementation is delegated.
*/
function _fastRecieveFrom(
address _sender,
uint256 _amount
) internal virtual;
}
// File contracts/token/extensions/FastHypERC20.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC20 Token Router that extends ERC20 with remote transfer functionality.
* @author Abacus Works
* @dev Supply on each chain is not constant but the aggregate supply across all chains is.
*/
contract FastHypERC20 is FastTokenRouter, HypERC20 {
constructor(
uint8 __decimals,
address _mailbox
) HypERC20(__decimals, _mailbox) {}
/**
* @dev delegates transfer logic to `_transferTo`.
* @inheritdoc TokenRouter
*/
function _handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) internal virtual override(FastTokenRouter, TokenRouter) {
FastTokenRouter._handle(_origin, _sender, _message);
}
/**
* @dev Mints `_amount` of tokens to `_recipient`.
* @inheritdoc FastTokenRouter
*/
function _fastTransferTo(
address _recipient,
uint256 _amount
) internal override {
_mint(_recipient, _amount);
}
/**
* @dev Burns `_amount` of tokens from `_recipient`.
* @inheritdoc FastTokenRouter
*/
function _fastRecieveFrom(
address _sender,
uint256 _amount
) internal override {
_burn(_sender, _amount);
}
function balanceOf(
address _account
) public view virtual override(HypERC20, TokenRouter) returns (uint256) {
return ERC20Upgradeable.balanceOf(_account);
}
}
// File contracts/token/HypERC20Collateral.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC20 Token Collateral that wraps an existing ERC20 with remote transfer functionality.
* @author Abacus Works
*/
contract HypERC20Collateral is TokenRouter {
using SafeERC20 for IERC20;
IERC20 public immutable wrappedToken;
/**
* @notice Constructor
* @param erc20 Address of the token to keep as collateral
*/
constructor(address erc20, address _mailbox) TokenRouter(_mailbox) {
wrappedToken = IERC20(erc20);
}
function balanceOf(
address _account
) external view override returns (uint256) {
return wrappedToken.balanceOf(_account);
}
/**
* @dev Transfers `_amount` of `wrappedToken` from `msg.sender` to this contract.
* @inheritdoc TokenRouter
*/
function _transferFromSender(
uint256 _amount
) internal override returns (bytes memory) {
wrappedToken.safeTransferFrom(msg.sender, address(this), _amount);
return bytes(""); // no metadata
}
/**
* @dev Transfers `_amount` of `wrappedToken` from this contract to `_recipient`.
* @inheritdoc TokenRouter
*/
function _transferTo(
address _recipient,
uint256 _amount,
bytes calldata // no metadata
) internal virtual override {
wrappedToken.safeTransfer(_recipient, _amount);
}
}
// File contracts/token/extensions/FastHypERC20Collateral.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC20 Token Collateral that wraps an existing ERC20 with remote transfer functionality.
* @author Abacus Works
*/
contract FastHypERC20Collateral is FastTokenRouter, HypERC20Collateral {
using SafeERC20 for IERC20;
/**
* @notice Constructor
* @param erc20 Address of the token to keep as collateral
*/
constructor(
address erc20,
address _mailbox
) HypERC20Collateral(erc20, _mailbox) {}
/**
* @dev delegates transfer logic to `_transferTo`.
* @inheritdoc FastTokenRouter
*/
function _handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) internal virtual override(FastTokenRouter, TokenRouter) {
FastTokenRouter._handle(_origin, _sender, _message);
}
/**
* @dev Transfers `_amount` of `wrappedToken` to `_recipient`.
* @inheritdoc FastTokenRouter
*/
function _fastTransferTo(
address _recipient,
uint256 _amount
) internal override {
wrappedToken.safeTransfer(_recipient, _amount);
}
/**
* @dev Transfers in `_amount` of `wrappedToken` from `_recipient`.
* @inheritdoc FastTokenRouter
*/
function _fastRecieveFrom(
address _sender,
uint256 _amount
) internal override {
wrappedToken.safeTransferFrom(_sender, address(this), _amount);
}
}
// File contracts/token/HypERC721Collateral.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC721 Token Collateral that wraps an existing ERC721 with remote transfer functionality.
* @author Abacus Works
*/
contract HypERC721Collateral is TokenRouter {
IERC721 public immutable wrappedToken;
/**
* @notice Constructor
* @param erc721 Address of the token to keep as collateral
*/
constructor(address erc721, address _mailbox) TokenRouter(_mailbox) {
wrappedToken = IERC721(erc721);
}
function ownerOf(uint256 _tokenId) external view returns (address) {
return IERC721(wrappedToken).ownerOf(_tokenId);
}
/**
* @dev Returns the balance of `_account` for `wrappedToken`.
* @inheritdoc TokenRouter
*/
function balanceOf(
address _account
) external view override returns (uint256) {
return IERC721(wrappedToken).balanceOf(_account);
}
/**
* @dev Transfers `_tokenId` of `wrappedToken` from `msg.sender` to this contract.
* @inheritdoc TokenRouter
*/
function _transferFromSender(
uint256 _tokenId
) internal virtual override returns (bytes memory) {
// safeTransferFrom not used here because recipient is this contract
wrappedToken.transferFrom(msg.sender, address(this), _tokenId);
return bytes(""); // no metadata
}
/**
* @dev Transfers `_tokenId` of `wrappedToken` from this contract to `_recipient`.
* @inheritdoc TokenRouter
*/
function _transferTo(
address _recipient,
uint256 _tokenId,
bytes calldata // no metadata
) internal override {
wrappedToken.safeTransferFrom(address(this), _recipient, _tokenId);
}
}
// File contracts/token/extensions/HypERC721URICollateral.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC721 Token Collateral that wraps an existing ERC721 with remote transfer and URI relay functionality.
* @author Abacus Works
*/
contract HypERC721URICollateral is HypERC721Collateral {
// solhint-disable-next-line no-empty-blocks
constructor(
address erc721,
address _mailbox
) HypERC721Collateral(erc721, _mailbox) {}
/**
* @dev Transfers `_tokenId` of `wrappedToken` from `msg.sender` to this contract.
* @return The URI of `_tokenId` on `wrappedToken`.
* @inheritdoc HypERC721Collateral
*/
function _transferFromSender(
uint256 _tokenId
) internal override returns (bytes memory) {
HypERC721Collateral._transferFromSender(_tokenId);
return
bytes(
IERC721MetadataUpgradeable(address(wrappedToken)).tokenURI(
_tokenId
)
);
}
}
// File contracts/token/HypERC721.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC721 Token Router that extends ERC721 with remote transfer functionality.
* @author Abacus Works
*/
contract HypERC721 is ERC721EnumerableUpgradeable, TokenRouter {
constructor(address _mailbox) TokenRouter(_mailbox) {}
/**
* @notice Initializes the Hyperlane router, ERC721 metadata, and mints initial supply to deployer.
* @param _mintAmount The amount of NFTs to mint to `msg.sender`.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
*/
function initialize(
uint256 _mintAmount,
string memory _name,
string memory _symbol
) external initializer {
address owner = msg.sender;
_transferOwnership(owner);
__ERC721_init(_name, _symbol);
for (uint256 i = 0; i < _mintAmount; i++) {
_safeMint(owner, i);
}
}
function balanceOf(
address _account
)
public
view
virtual
override(TokenRouter, ERC721Upgradeable, IERC721Upgradeable)
returns (uint256)
{
return ERC721Upgradeable.balanceOf(_account);
}
/**
* @dev Asserts `msg.sender` is owner and burns `_tokenId`.
* @inheritdoc TokenRouter
*/
function _transferFromSender(
uint256 _tokenId
) internal virtual override returns (bytes memory) {
require(ownerOf(_tokenId) == msg.sender, "!owner");
_burn(_tokenId);
return bytes(""); // no metadata
}
/**
* @dev Mints `_tokenId` to `_recipient`.
* @inheritdoc TokenRouter
*/
function _transferTo(
address _recipient,
uint256 _tokenId,
bytes calldata // no metadata
) internal virtual override {
_safeMint(_recipient, _tokenId);
}
}
// File contracts/token/extensions/HypERC721URIStorage.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC721 Token that extends ERC721URIStorage with remote transfer and URI relay functionality.
* @author Abacus Works
*/
contract HypERC721URIStorage is HypERC721, ERC721URIStorageUpgradeable {
constructor(address _mailbox) HypERC721(_mailbox) {}
function balanceOf(
address account
)
public
view
override(HypERC721, ERC721Upgradeable, IERC721Upgradeable)
returns (uint256)
{
return HypERC721.balanceOf(account);
}
/**
* @return _tokenURI The URI of `_tokenId`.
* @inheritdoc HypERC721
*/
function _transferFromSender(
uint256 _tokenId
) internal override returns (bytes memory _tokenURI) {
_tokenURI = bytes(tokenURI(_tokenId)); // requires minted
HypERC721._transferFromSender(_tokenId);
}
/**
* @dev Sets the URI for `_tokenId` to `_tokenURI`.
* @inheritdoc HypERC721
*/
function _transferTo(
address _recipient,
uint256 _tokenId,
bytes calldata _tokenURI
) internal override {
HypERC721._transferTo(_recipient, _tokenId, _tokenURI);
_setTokenURI(_tokenId, string(_tokenURI)); // requires minted
}
function tokenURI(
uint256 tokenId
)
public
view
override(ERC721Upgradeable, ERC721URIStorageUpgradeable)
returns (string memory)
{
return ERC721URIStorageUpgradeable.tokenURI(tokenId);
}
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId,
uint256 batchSize
) internal override(ERC721EnumerableUpgradeable, ERC721Upgradeable) {
ERC721EnumerableUpgradeable._beforeTokenTransfer(
from,
to,
tokenId,
batchSize
);
}
function supportsInterface(
bytes4 interfaceId
)
public
view
override(ERC721EnumerableUpgradeable, ERC721URIStorageUpgradeable)
returns (bool)
{
return ERC721EnumerableUpgradeable.supportsInterface(interfaceId);
}
function _burn(
uint256 tokenId
) internal override(ERC721URIStorageUpgradeable, ERC721Upgradeable) {
ERC721URIStorageUpgradeable._burn(tokenId);
}
}
// File contracts/token/HypNative.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane Native Token Router that extends ERC20 with remote transfer functionality.
* @author Abacus Works
* @dev Supply on each chain is not constant but the aggregate supply across all chains is.
*/
contract HypNative is TokenRouter {
/**
* @dev Emitted when native tokens are donated to the contract.
* @param sender The address of the sender.
* @param amount The amount of native tokens donated.
*/
event Donation(address indexed sender, uint256 amount);
constructor(address _mailbox) TokenRouter(_mailbox) {}
/**
* @inheritdoc TokenRouter
* @dev uses (`msg.value` - `_amount`) as interchain gas payment and `msg.sender` as refund address.
*/
function transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amount
) public payable virtual override returns (bytes32 messageId) {
require(msg.value >= _amount, "Native: amount exceeds msg.value");
uint256 gasPayment = msg.value - _amount;
return _transferRemote(_destination, _recipient, _amount, gasPayment);
}
function balanceOf(
address _account
) external view override returns (uint256) {
return _account.balance;
}
/**
* @inheritdoc TokenRouter
* @dev No-op because native amount is transferred in `msg.value`
* @dev Compiler will not include this in the bytecode.
*/
function _transferFromSender(
uint256
) internal pure override returns (bytes memory) {
return bytes(""); // no metadata
}
/**
* @dev Sends `_amount` of native token to `_recipient` balance.
* @inheritdoc TokenRouter
*/
function _transferTo(
address _recipient,
uint256 _amount,
bytes calldata // no metadata
) internal virtual override {
Address.sendValue(payable(_recipient), _amount);
}
receive() external payable {
emit Donation(msg.sender, msg.value);
}
}
// File contracts/token/extensions/HypNativeScaled.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane Native Token that scales native value by a fixed factor for consistency with other tokens.
* @dev The scale factor multiplies the `message.amount` to the local native token amount.
* Conversely, it divides the local native `msg.value` amount by `scale` to encode the `message.amount`.
* @author Abacus Works
*/
contract HypNativeScaled is HypNative {
uint256 public immutable scale;
constructor(uint256 _scale, address _mailbox) HypNative(_mailbox) {
scale = _scale;
}
/**
* @inheritdoc HypNative
* @dev Sends scaled `msg.value` (divided by `scale`) to `_recipient`.
*/
function transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amount
) public payable override returns (bytes32 messageId) {
require(msg.value >= _amount, "Native: amount exceeds msg.value");
uint256 gasPayment = msg.value - _amount;
uint256 scaledAmount = _amount / scale;
return
_transferRemote(_destination, _recipient, scaledAmount, gasPayment);
}
/**
* @dev Sends scaled `_amount` (multipled by `scale`) to `_recipient`.
* @inheritdoc TokenRouter
*/
function _transferTo(
address _recipient,
uint256 _amount,
bytes calldata metadata // no metadata
) internal override {
uint256 scaledAmount = _amount * scale;
HypNative._transferTo(_recipient, scaledAmount, metadata);
}
}
// File contracts/upgrade/ProxyAdmin.sol
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/transparent/ProxyAdmin.sol)
pragma solidity ^0.8.0;
// File contracts/upgrade/TimelockController.sol
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (governance/TimelockController.sol)
pragma solidity ^0.8.0;
// File contracts/upgrade/TransparentUpgradeableProxy.sol
// Original license: SPDX_License_Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
pragma solidity ^0.8.0;
// File contracts/interfaces/IRouter.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
interface IRouter {
function domains() external view returns (uint32[] memory);
function routers(uint32 _domain) external view returns (bytes32);
function enrollRemoteRouter(uint32 _domain, bytes32 _router) external;
function enrollRemoteRouters(
uint32[] calldata _domains,
bytes32[] calldata _routers
) external;
}
pragma solidity ^0.8.13;
// ============ External Imports ============
/*
* @title The Hello World App
* @dev You can use this simple app as a starting point for your own application.
*/
contract HelloWorld is Router {
// A generous upper bound on the amount of gas to use in the handle
// function when a message is processed. Used for paying for gas.
uint256 public constant HANDLE_GAS_AMOUNT = 50_000;
// A counter of how many messages have been sent from this contract.
uint256 public sent;
// A counter of how many messages have been received by this contract.
uint256 public received;
// Keyed by domain, a counter of how many messages that have been sent
// from this contract to the domain.
mapping(uint32 => uint256) public sentTo;
// Keyed by domain, a counter of how many messages that have been received
// by this contract from the domain.
mapping(uint32 => uint256) public receivedFrom;
// ============ Events ============
event SentHelloWorld(
uint32 indexed origin,
uint32 indexed destination,
string message
);
event ReceivedHelloWorld(
uint32 indexed origin,
uint32 indexed destination,
bytes32 sender,
string message
);
event HandleGasAmountSet(
uint32 indexed destination,
uint256 handleGasAmount
);
constructor(address _mailbox, address _hook) Router(_mailbox) {
// Transfer ownership of the contract to deployer
_transferOwnership(msg.sender);
setHook(_hook);
}
// ============ External functions ============
/**
* @notice Sends a message to the _destinationDomain. Any msg.value is
* used as interchain gas payment.
* @param _destinationDomain The destination domain to send the message to.
* @param _message The message to send.
*/
function sendHelloWorld(
uint32 _destinationDomain,
string calldata _message
) external payable {
sent += 1;
sentTo[_destinationDomain] += 1;
_dispatch(_destinationDomain, bytes(_message));
emit SentHelloWorld(
mailbox.localDomain(),
_destinationDomain,
_message
);
}
/**
* @notice Fetches the amount of gas that will be used when a message is
* dispatched to the given domain.
*/
function quoteDispatch(
uint32 _destinationDomain,
bytes calldata _message
) external view returns (uint256) {
return _quoteDispatch(_destinationDomain, _message);
}
// ============ Internal functions ============
function _metadata(
uint32 /*_destinationDomain*/
) internal view override returns (bytes memory) {
return StandardHookMetadata.overrideGasLimit(HANDLE_GAS_AMOUNT);
}
/**
* @notice Handles a message from a remote router.
* @dev Only called for messages sent from a remote router, as enforced by Router.sol.
* @param _origin The domain of the origin of the message.
* @param _sender The sender of the message.
* @param _message The message body.
*/
function _handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) internal override {
received += 1;
receivedFrom[_origin] += 1;
emit ReceivedHelloWorld(
_origin,
mailbox.localDomain(),
_sender,
string(_message)
);
}
}
exec
/bin/bash -lc "rg -n '"'^contract InterchainGasPaymaster|''^contract StorageGasOracle|''^contract Mailbox|''^contract InterchainAccountRouter|''^contract InterchainQueryRouter|''^abstract contract TokenRouter|''^abstract contract FastTokenRouter|''^contract HypERC20|''^contract FastHypERC20|''^contract HypERC20Collateral|''^contract FastHypERC20Collateral|''^contract HypERC721Collateral|''^contract HypERC721 |''^contract HypNative|''^contract HypNativeScaled|''^contract PortalAdapter|''^contract LiquidityLayerRouter|''^contract CircleBridgeAdapter|''^contract ValidatorAnnounce|''^abstract contract AbstractMultisigIsm|''^abstract contract AbstractMerkleRootMultisigIsm|''^abstract contract AbstractMessageIdMultisigIsm|''^contract StaticMerkleRootMultisigIsm|''^contract StaticMessageIdMultisigIsm|''^contract DomainRoutingIsm|''^contract DefaultFallbackRoutingIsm|''^contract ERC5164Ism|''^contract OPStackIsm'"' AUDIT_BUNDLE.txt" in /tmp/codex-3gxliacl
succeeded in 0ms:
10084:contract InterchainGasPaymaster is
10386:contract StorageGasOracle is IGasOracle, Ownable {
11629:abstract contract AbstractMultisigIsm is IMultisigIsm {
11770:contract ERC5164Ism is AbstractMessageIdAuthorizedIsm {
11823:contract OPStackIsm is
12057:abstract contract AbstractMerkleRootMultisigIsm is AbstractMultisigIsm {
12203:abstract contract AbstractMessageIdMultisigIsm is AbstractMultisigIsm {
12276:contract StaticMerkleRootMultisigIsm is
12286:contract StaticMessageIdMultisigIsm is
12293:contract StaticMerkleRootMultisigIsmFactory is
12301:contract StaticMessageIdMultisigIsmFactory is StaticThresholdAddressSetFactory {
12358:contract ValidatorAnnounce is MailboxClient, IValidatorAnnounce {
12596:contract DomainRoutingIsm is AbstractRoutingIsm, OwnableUpgradeable {
12723:contract DefaultFallbackRoutingIsm is DomainRoutingIsm, MailboxClient {
12813:contract DomainRoutingIsmFactory is AbstractDomainRoutingIsmFactory {
13126:contract Mailbox is IMailbox, Indexed, Versioned, OwnableUpgradeable {
13597:contract InterchainAccountRouter is Router {
14223:contract InterchainQueryRouter is Router {
14485:contract CircleBridgeAdapter is ILiquidityLayerAdapter, Router {
14813:contract PortalAdapter is ILiquidityLayerAdapter, Router {
15058:contract LiquidityLayerRouter is Router, ILiquidityLayerRouter {
16297:abstract contract TokenRouter is GasRouter {
16428:contract HypERC20 is ERC20Upgradeable, TokenRouter {
16502:abstract contract FastTokenRouter is TokenRouter {
16712:contract FastHypERC20 is FastTokenRouter, HypERC20 {
16772:contract HypERC20Collateral is TokenRouter {
16828:contract FastHypERC20Collateral is FastTokenRouter, HypERC20Collateral {
16886:contract HypERC721Collateral is TokenRouter {
16983:contract HypERC721 is ERC721EnumerableUpgradeable, TokenRouter {
17150:contract HypNative is TokenRouter {
17221:contract HypNativeScaled is HypNative {
Finished reading the bundle once end-to-end. Now I’m re-reading the security-critical sections that were truncated in the large chunks and tracing exploit paths.
✓ Read AUDIT_BUNDLE.txt fully and identify first-party Solidity sources in scope
→ Analyze contracts for exploit paths in requested categories
• Produce JSON report with concrete findings and locations
exec
/bin/bash -lc "sed -n '10047,10430p' AUDIT_BUNDLE.txt" in /tmp/codex-3gxliacl
succeeded in 0ms:
// File contracts/hooks/igp/InterchainGasPaymaster.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
// ============ External Imports ============
/**
* @title InterchainGasPaymaster
* @notice Manages payments on a source chain to cover gas costs of relaying
* messages to destination chains and includes the gas overhead per destination
* @dev The intended use of this contract is to store overhead gas amounts for destination
* domains, e.g. Mailbox and ISM gas usage, such that users of this IGP are only required
* to specify the gas amount used by their own applications.
*/
contract InterchainGasPaymaster is
IInterchainGasPaymaster,
AbstractPostDispatchHook,
IGasOracle,
Indexed,
OwnableUpgradeable
{
using Address for address payable;
using Message for bytes;
using StandardHookMetadata for bytes;
// ============ Constants ============
/// @notice The scale of gas oracle token exchange rates.
uint256 internal constant TOKEN_EXCHANGE_RATE_SCALE = 1e10;
/// @notice default for user call if metadata not provided
uint256 internal immutable DEFAULT_GAS_USAGE = 50_000;
// ============ Public Storage ============
/// @notice Destination domain => gas oracle and overhead gas amount.
mapping(uint32 => DomainGasConfig) public destinationGasConfigs;
/// @notice The benficiary that can receive native tokens paid into this contract.
address public beneficiary;
// ============ Events ============
/**
* @notice Emitted when the gas oracle for a remote domain is set.
* @param remoteDomain The remote domain.
* @param gasOracle The gas oracle.
* @param gasOverhead The destination gas overhead.
*/
event DestinationGasConfigSet(
uint32 remoteDomain,
address gasOracle,
uint96 gasOverhead
);
/**
* @notice Emitted when the beneficiary is set.
* @param beneficiary The new beneficiary.
*/
event BeneficiarySet(address beneficiary);
struct DomainGasConfig {
IGasOracle gasOracle;
uint96 gasOverhead;
}
struct GasParam {
uint32 remoteDomain;
DomainGasConfig config;
}
// ============ External Functions ============
/// @inheritdoc IPostDispatchHook
function hookType() external pure override returns (uint8) {
return uint8(IPostDispatchHook.Types.INTERCHAIN_GAS_PAYMASTER);
}
/**
* @param _owner The owner of the contract.
* @param _beneficiary The beneficiary.
*/
function initialize(
address _owner,
address _beneficiary
) public initializer {
__Ownable_init();
_transferOwnership(_owner);
_setBeneficiary(_beneficiary);
}
/**
* @notice Transfers the entire native token balance to the beneficiary.
* @dev The beneficiary must be able to receive native tokens.
*/
function claim() external {
// Transfer the entire balance to the beneficiary.
(bool success, ) = beneficiary.call{value: address(this).balance}("");
require(success, "IGP: claim failed");
}
/**
* @notice Sets the gas oracles for remote domains specified in the config array.
* @param _configs An array of configs including the remote domain and gas oracles to set.
*/
function setDestinationGasConfigs(
GasParam[] calldata _configs
) external onlyOwner {
uint256 _len = _configs.length;
for (uint256 i = 0; i < _len; i++) {
_setDestinationGasConfig(
_configs[i].remoteDomain,
_configs[i].config.gasOracle,
_configs[i].config.gasOverhead
);
}
}
/**
* @notice Sets the beneficiary.
* @param _beneficiary The new beneficiary.
*/
function setBeneficiary(address _beneficiary) external onlyOwner {
_setBeneficiary(_beneficiary);
}
// ============ Public Functions ============
/**
* @notice Deposits msg.value as a payment for the relaying of a message
* to its destination chain.
* @dev Overpayment will result in a refund of native tokens to the _refundAddress.
* Callers should be aware that this may present reentrancy issues.
* @param _messageId The ID of the message to pay for.
* @param _destinationDomain The domain of the message's destination chain.
* @param _gasLimit The amount of destination gas to pay for.
* @param _refundAddress The address to refund any overpayment to.
*/
function payForGas(
bytes32 _messageId,
uint32 _destinationDomain,
uint256 _gasLimit,
address _refundAddress
) public payable override {
uint256 _requiredPayment = quoteGasPayment(
_destinationDomain,
_gasLimit
);
require(
msg.value >= _requiredPayment,
"IGP: insufficient interchain gas payment"
);
uint256 _overpayment = msg.value - _requiredPayment;
if (_overpayment > 0) {
require(_refundAddress != address(0), "no refund address");
payable(_refundAddress).sendValue(_overpayment);
}
emit GasPayment(
_messageId,
_destinationDomain,
_gasLimit,
_requiredPayment
);
}
/**
* @notice Quotes the amount of native tokens to pay for interchain gas.
* @param _destinationDomain The domain of the message's destination chain.
* @param _gasLimit The amount of destination gas to pay for.
* @return The amount of native tokens required to pay for interchain gas.
*/
function quoteGasPayment(
uint32 _destinationDomain,
uint256 _gasLimit
) public view virtual override returns (uint256) {
// Get the gas data for the destination domain.
(
uint128 _tokenExchangeRate,
uint128 _gasPrice
) = getExchangeRateAndGasPrice(_destinationDomain);
// The total cost quoted in destination chain's native token.
uint256 _destinationGasCost = _gasLimit * uint256(_gasPrice);
// Convert to the local native token.
return
(_destinationGasCost * _tokenExchangeRate) /
TOKEN_EXCHANGE_RATE_SCALE;
}
/**
* @notice Gets the token exchange rate and gas price from the configured gas oracle
* for a given destination domain.
* @param _destinationDomain The destination domain.
* @return tokenExchangeRate The exchange rate of the remote native token quoted in the local native token.
* @return gasPrice The gas price on the remote chain.
*/
function getExchangeRateAndGasPrice(
uint32 _destinationDomain
)
public
view
override
returns (uint128 tokenExchangeRate, uint128 gasPrice)
{
IGasOracle _gasOracle = destinationGasConfigs[_destinationDomain]
.gasOracle;
require(
address(_gasOracle) != address(0),
string.concat(
"Configured IGP doesn't support domain ",
Strings.toString(_destinationDomain)
)
);
return _gasOracle.getExchangeRateAndGasPrice(_destinationDomain);
}
/**
* @notice Returns the stored destinationGasOverhead added to the _gasLimit.
* @dev If there is no stored destinationGasOverhead, 0 is used. This is useful in the case
* the ISM deployer wants to subsidize the overhead gas cost. Then, can specify the gas oracle
* they want to use with the destination domain, but set the overhead to 0.
* @param _destinationDomain The domain of the message's destination chain.
* @param _gasLimit The amount of destination gas to pay for. This is only for application gas usage as
* the gas usage for the mailbox and the ISM is already accounted in the DomainGasConfig.gasOverhead
*/
function destinationGasLimit(
uint32 _destinationDomain,
uint256 _gasLimit
) public view returns (uint256) {
return
uint256(destinationGasConfigs[_destinationDomain].gasOverhead) +
_gasLimit;
}
// ============ Internal Functions ============
/// @inheritdoc AbstractPostDispatchHook
function _postDispatch(
bytes calldata metadata,
bytes calldata message
) internal override {
payForGas(
message.id(),
message.destination(),
destinationGasLimit(
message.destination(),
metadata.gasLimit(DEFAULT_GAS_USAGE)
),
metadata.refundAddress(message.senderAddress())
);
}
/// @inheritdoc AbstractPostDispatchHook
function _quoteDispatch(
bytes calldata metadata,
bytes calldata message
) internal view override returns (uint256) {
return
quoteGasPayment(
message.destination(),
destinationGasLimit(
message.destination(),
metadata.gasLimit(DEFAULT_GAS_USAGE)
)
);
}
/**
* @notice Sets the beneficiary.
* @param _beneficiary The new beneficiary.
*/
function _setBeneficiary(address _beneficiary) internal {
beneficiary = _beneficiary;
emit BeneficiarySet(_beneficiary);
}
/**
* @notice Sets the gas oracle and destination gas overhead for a remote domain.
* @param _remoteDomain The remote domain.
* @param _gasOracle The gas oracle.
* @param _gasOverhead The destination gas overhead.
*/
function _setDestinationGasConfig(
uint32 _remoteDomain,
IGasOracle _gasOracle,
uint96 _gasOverhead
) internal {
destinationGasConfigs[_remoteDomain] = DomainGasConfig(
_gasOracle,
_gasOverhead
);
emit DestinationGasConfigSet(
_remoteDomain,
address(_gasOracle),
_gasOverhead
);
}
}
// File contracts/hooks/igp/StorageGasOracle.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
// ============ Internal Imports ============
// ============ External Imports ============
/**
* @notice A gas oracle that uses data stored within the contract.
* @dev This contract is intended to be owned by an address that will
* update the stored remote gas data.
*/
contract StorageGasOracle is IGasOracle, Ownable {
// ============ Public Storage ============
/// @notice Keyed by remote domain, gas data on that remote domain.
mapping(uint32 => IGasOracle.RemoteGasData) public remoteGasData;
// ============ Events ============
/**
* @notice Emitted when an entry in `remoteGasData` is set.
* @param remoteDomain The remote domain in which the gas data was set for.
* @param tokenExchangeRate The exchange rate of the remote native token quoted in the local native token.
* @param gasPrice The gas price on the remote chain.
*/
event RemoteGasDataSet(
uint32 indexed remoteDomain,
uint128 tokenExchangeRate,
uint128 gasPrice
);
struct RemoteGasDataConfig {
uint32 remoteDomain;
uint128 tokenExchangeRate;
uint128 gasPrice;
}
// ============ External Functions ============
/**
* @notice Returns the stored `remoteGasData` for the `_destinationDomain`.
* @param _destinationDomain The destination domain.
* @return tokenExchangeRate The exchange rate of the remote native token quoted in the local native token.
* @return gasPrice The gas price on the remote chain.
*/
function getExchangeRateAndGasPrice(
uint32 _destinationDomain
)
external
view
override
returns (uint128 tokenExchangeRate, uint128 gasPrice)
{
// Intentionally allow unset / zero values
IGasOracle.RemoteGasData memory _data = remoteGasData[
_destinationDomain
exec
/bin/bash -lc "sed -n '11853,12340p' AUDIT_BUNDLE.txt" in /tmp/codex-3gxliacl
succeeded in 0ms:
// File contracts/isms/libs/MerkleRootMultisigIsmMetadata.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
/**
* Format of metadata:
* [ 0: 32] Origin merkle tree address
* [ 32: 36] Index of message ID in merkle tree
* [ 36: 68] Signed checkpoint message ID
* [ 68:1092] Merkle proof
* [1092:1096] Signed checkpoint index (computed from proof and index)
* [1096:????] Validator signatures (length := threshold * 65)
*/
library MerkleRootMultisigIsmMetadata {
uint8 private constant ORIGIN_MERKLE_TREE_OFFSET = 0;
uint8 private constant MESSAGE_INDEX_OFFSET = 32;
uint8 private constant MESSAGE_ID_OFFSET = 36;
uint8 private constant MERKLE_PROOF_OFFSET = 68;
uint16 private constant MERKLE_PROOF_LENGTH = 32 * 32;
uint16 private constant SIGNED_INDEX_OFFSET = 1092;
uint16 private constant SIGNATURES_OFFSET = 1096;
uint8 private constant SIGNATURE_LENGTH = 65;
/**
* @notice Returns the origin merkle tree hook of the signed checkpoint as bytes32.
* @param _metadata ABI encoded Multisig ISM metadata.
* @return Origin merkle tree hook of the signed checkpoint as bytes32
*/
function originMerkleTreeHook(
bytes calldata _metadata
) internal pure returns (bytes32) {
return
bytes32(
_metadata[ORIGIN_MERKLE_TREE_OFFSET:ORIGIN_MERKLE_TREE_OFFSET +
32]
);
}
/**
* @notice Returns the index of the message being proven.
* @param _metadata ABI encoded Multisig ISM metadata.
* @return Index of the target message in the merkle tree.
*/
function messageIndex(
bytes calldata _metadata
) internal pure returns (uint32) {
return
uint32(
bytes4(_metadata[MESSAGE_INDEX_OFFSET:MESSAGE_INDEX_OFFSET + 4])
);
}
/**
* @notice Returns the index of the signed checkpoint.
* @param _metadata ABI encoded Multisig ISM metadata.
* @return Index of the signed checkpoint
*/
function signedIndex(
bytes calldata _metadata
) internal pure returns (uint32) {
return
uint32(
bytes4(_metadata[SIGNED_INDEX_OFFSET:SIGNED_INDEX_OFFSET + 4])
);
}
/**
* @notice Returns the message ID of the signed checkpoint.
* @param _metadata ABI encoded Multisig ISM metadata.
* @return Message ID of the signed checkpoint
*/
function signedMessageId(
bytes calldata _metadata
) internal pure returns (bytes32) {
return bytes32(_metadata[MESSAGE_ID_OFFSET:MESSAGE_ID_OFFSET + 32]);
}
/**
* @notice Returns the merkle proof branch of the message.
* @dev This appears to be more gas efficient than returning a calldata
* slice and using that.
* @param _metadata ABI encoded Multisig ISM metadata.
* @return Merkle proof branch of the message.
*/
function proof(
bytes calldata _metadata
) internal pure returns (bytes32[32] memory) {
return
abi.decode(
_metadata[MERKLE_PROOF_OFFSET:MERKLE_PROOF_OFFSET +
MERKLE_PROOF_LENGTH],
(bytes32[32])
);
}
/**
* @notice Returns the validator ECDSA signature at `_index`.
* @dev Assumes signatures are sorted by validator
* @dev Assumes `_metadata` encodes `threshold` signatures.
* @dev Assumes `_index` is less than `threshold`
* @param _metadata ABI encoded Multisig ISM metadata.
* @param _index The index of the signature to return.
* @return The validator ECDSA signature at `_index`.
*/
function signatureAt(
bytes calldata _metadata,
uint256 _index
) internal pure returns (bytes calldata) {
uint256 _start = SIGNATURES_OFFSET + (_index * SIGNATURE_LENGTH);
uint256 _end = _start + SIGNATURE_LENGTH;
return _metadata[_start:_end];
}
}
// File contracts/libs/CheckpointLib.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
// ============ External Imports ============
library CheckpointLib {
/**
* @notice Returns the digest validators are expected to sign when signing checkpoints.
* @param _origin The origin domain of the checkpoint.
* @param _originmerkleTreeHook The address of the origin merkle tree hook as bytes32.
* @param _checkpointRoot The root of the checkpoint.
* @param _checkpointIndex The index of the checkpoint.
* @param _messageId The message ID of the checkpoint.
* @dev Message ID must match leaf content of checkpoint root at index.
* @return The digest of the checkpoint.
*/
function digest(
uint32 _origin,
bytes32 _originmerkleTreeHook,
bytes32 _checkpointRoot,
uint32 _checkpointIndex,
bytes32 _messageId
) internal pure returns (bytes32) {
bytes32 _domainHash = domainHash(_origin, _originmerkleTreeHook);
return
ECDSA.toEthSignedMessageHash(
keccak256(
abi.encodePacked(
_domainHash,
_checkpointRoot,
_checkpointIndex,
_messageId
)
)
);
}
/**
* @notice Returns the domain hash that validators are expected to use
* when signing checkpoints.
* @param _origin The origin domain of the checkpoint.
* @param _originmerkleTreeHook The address of the origin merkle tree as bytes32.
* @return The domain hash.
*/
function domainHash(
uint32 _origin,
bytes32 _originmerkleTreeHook
) internal pure returns (bytes32) {
// Including the origin merkle tree address in the signature allows the slashing
// protocol to enroll multiple trees. Otherwise, a valid signature for
// tree A would be indistinguishable from a fraudulent signature for tree B.
// The slashing protocol should slash if validators sign attestations for
// anything other than a whitelisted tree.
return
keccak256(
abi.encodePacked(_origin, _originmerkleTreeHook, "HYPERLANE")
);
}
}
// File contracts/isms/multisig/AbstractMerkleRootMultisigIsm.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
// ============ Internal Imports ============
/**
* @title `AbstractMerkleRootMultisigIsm` — multi-sig ISM with the validators-censorship resistance guarantee.
* @notice This ISM allows using a newer signed checkpoint (say #33) to prove existence of an older message (#22) in the validators' MerkleTree.
* This guarantees censorship resistance as validators cannot hide a message
* by refusing to sign its checkpoint but later signing a checkpoint for a newer message.
* If validators decide to censor a message, they are left with only one option — to not produce checkpoints at all.
* Otherwise, the very next signed checkpoint (#33) can be used by any relayer to prove the previous message inclusion using this ISM.
* This is censorship resistance is missing in the sibling implementation `AbstractMessageIdMultisigIsm`,
* since it can only verify messages having the corresponding checkpoints.
* @dev Provides the default implementation of verifying signatures over a checkpoint and the message inclusion in that checkpoint.
* This abstract contract can be overridden for customizing the `validatorsAndThreshold()` (static or dynamic).
* @dev May be adapted in future to support batch message verification against a single root.
*/
abstract contract AbstractMerkleRootMultisigIsm is AbstractMultisigIsm {
using MerkleRootMultisigIsmMetadata for bytes;
using Message for bytes;
// ============ Constants ============
// solhint-disable-next-line const-name-snakecase
uint8 public constant moduleType =
uint8(IInterchainSecurityModule.Types.MERKLE_ROOT_MULTISIG);
/**
* @inheritdoc AbstractMultisigIsm
*/
function digest(
bytes calldata _metadata,
bytes calldata _message
) internal pure override returns (bytes32) {
require(
_metadata.messageIndex() <= _metadata.signedIndex(),
"Invalid merkle index metadata"
);
// We verify a merkle proof of (messageId, index) I to compute root J
bytes32 _signedRoot = MerkleLib.branchRoot(
_message.id(),
_metadata.proof(),
_metadata.messageIndex()
);
// We provide (messageId, index) J in metadata for digest derivation
return
CheckpointLib.digest(
_message.origin(),
_metadata.originMerkleTreeHook(),
_signedRoot,
_metadata.signedIndex(),
_metadata.signedMessageId()
);
}
/**
* @inheritdoc AbstractMultisigIsm
*/
function signatureAt(
bytes calldata _metadata,
uint256 _index
) internal pure virtual override returns (bytes calldata) {
return _metadata.signatureAt(_index);
}
}
// File contracts/isms/libs/MessageIdMultisigIsmMetadata.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
/**
* Format of metadata:
* [ 0: 32] Origin merkle tree address
* [ 32: 64] Signed checkpoint root
* [ 64: 68] Signed checkpoint index
* [ 68:????] Validator signatures (length := threshold * 65)
*/
library MessageIdMultisigIsmMetadata {
uint8 private constant ORIGIN_MERKLE_TREE_OFFSET = 0;
uint8 private constant MERKLE_ROOT_OFFSET = 32;
uint8 private constant MERKLE_INDEX_OFFSET = 64;
uint8 private constant SIGNATURES_OFFSET = 68;
uint8 private constant SIGNATURE_LENGTH = 65;
/**
* @notice Returns the origin merkle tree hook of the signed checkpoint as bytes32.
* @param _metadata ABI encoded Multisig ISM metadata.
* @return Origin merkle tree hook of the signed checkpoint as bytes32
*/
function originMerkleTreeHook(
bytes calldata _metadata
) internal pure returns (bytes32) {
return
bytes32(
_metadata[ORIGIN_MERKLE_TREE_OFFSET:ORIGIN_MERKLE_TREE_OFFSET +
32]
);
}
/**
* @notice Returns the merkle root of the signed checkpoint.
* @param _metadata ABI encoded Multisig ISM metadata.
* @return Merkle root of the signed checkpoint
*/
function root(bytes calldata _metadata) internal pure returns (bytes32) {
return bytes32(_metadata[MERKLE_ROOT_OFFSET:MERKLE_ROOT_OFFSET + 32]);
}
/**
* @notice Returns the merkle index of the signed checkpoint.
* @param _metadata ABI encoded Multisig ISM metadata.
* @return Merkle index of the signed checkpoint
*/
function index(bytes calldata _metadata) internal pure returns (uint32) {
return
uint32(
bytes4(_metadata[MERKLE_INDEX_OFFSET:MERKLE_INDEX_OFFSET + 4])
);
}
/**
* @notice Returns the validator ECDSA signature at `_index`.
* @dev Assumes signatures are sorted by validator
* @dev Assumes `_metadata` encodes `threshold` signatures.
* @dev Assumes `_index` is less than `threshold`
* @param _metadata ABI encoded Multisig ISM metadata.
* @param _index The index of the signature to return.
* @return The validator ECDSA signature at `_index`.
*/
function signatureAt(
bytes calldata _metadata,
uint256 _index
) internal pure returns (bytes calldata) {
uint256 _start = SIGNATURES_OFFSET + (_index * SIGNATURE_LENGTH);
uint256 _end = _start + SIGNATURE_LENGTH;
return _metadata[_start:_end];
}
}
// File contracts/isms/multisig/AbstractMessageIdMultisigIsm.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
// ============ Internal Imports ============
/**
* @title `AbstractMessageIdMultisigIsm` — multi-sig ISM for the censorship-friendly validators.
* @notice This ISM minimizes gas/performance overhead of the checkpoints verification by compromising on the censorship resistance.
* For censorship resistance consider using `AbstractMerkleRootMultisigIsm`.
* If the validators (`validatorsAndThreshold`) skip messages by not sign checkpoints for them,
* the relayers will not be able to aggregate a quorum of signatures sufficient to deliver these messages via this ISM.
* Integrations are free to choose the trade-off between the censorship resistance and the gas/processing overhead.
* @dev Provides the default implementation of verifying signatures over a checkpoint related to a specific message ID.
* This abstract contract can be customized to change the `validatorsAndThreshold()` (static or dynamic).
*/
abstract contract AbstractMessageIdMultisigIsm is AbstractMultisigIsm {
using Message for bytes;
using MessageIdMultisigIsmMetadata for bytes;
// ============ Constants ============
// solhint-disable-next-line const-name-snakecase
uint8 public constant moduleType =
uint8(IInterchainSecurityModule.Types.MESSAGE_ID_MULTISIG);
/**
* @inheritdoc AbstractMultisigIsm
*/
function digest(
bytes calldata _metadata,
bytes calldata _message
) internal pure override returns (bytes32) {
return
CheckpointLib.digest(
_message.origin(),
_metadata.originMerkleTreeHook(),
_metadata.root(),
_metadata.index(),
_message.id()
);
}
/**
* @inheritdoc AbstractMultisigIsm
*/
function signatureAt(
bytes calldata _metadata,
uint256 _index
) internal pure virtual override returns (bytes calldata) {
return _metadata.signatureAt(_index);
}
}
// File contracts/isms/multisig/StaticMultisigIsm.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
// ============ Internal Imports ============
/**
* @title AbstractMetaProxyMultisigIsm
* @notice Manages per-domain m-of-n Validator set that is used
* to verify interchain messages.
*/
abstract contract AbstractMetaProxyMultisigIsm is AbstractMultisigIsm {
/**
* @inheritdoc AbstractMultisigIsm
*/
function validatorsAndThreshold(
bytes calldata
) public pure override returns (address[] memory, uint8) {
return abi.decode(MetaProxy.metadata(), (address[], uint8));
}
}
// solhint-disable no-empty-blocks
/**
* @title StaticMerkleRootMultisigIsm
* @notice Manages per-domain m-of-n validator set that is used
* to verify interchain messages using a merkle root signature quorum
* and merkle proof of inclusion.
*/
contract StaticMerkleRootMultisigIsm is
AbstractMerkleRootMultisigIsm,
AbstractMetaProxyMultisigIsm
{}
/**
* @title StaticMessageIdMultisigIsm
* @notice Manages per-domain m-of-n validator set that is used
* to verify interchain messages using a message ID signature quorum.
*/
contract StaticMessageIdMultisigIsm is
AbstractMessageIdMultisigIsm,
AbstractMetaProxyMultisigIsm
{}
// solhint-enable no-empty-blocks
contract StaticMerkleRootMultisigIsmFactory is
StaticThresholdAddressSetFactory
{
function _deployImplementation() internal override returns (address) {
return address(new StaticMerkleRootMultisigIsm());
}
}
contract StaticMessageIdMultisigIsmFactory is StaticThresholdAddressSetFactory {
function _deployImplementation() internal override returns (address) {
return address(new StaticMessageIdMultisigIsm());
}
}
// File contracts/interfaces/IValidatorAnnounce.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.6.11;
interface IValidatorAnnounce {
/// @notice Returns a list of validators that have made announcements
function getAnnouncedValidators() external view returns (address[] memory);
/**
* @notice Returns a list of all announced storage locations for `validators`
* @param _validators The list of validators to get storage locations for
* @return A list of announced storage locations
*/
function getAnnouncedStorageLocations(
address[] calldata _validators
) external view returns (string[][] memory);
/**
* @notice Announces a validator signature storage location
* @param _storageLocation Information encoding the location of signed
* checkpoints
* @param _signature The signed validator announcement
* @return True upon success
*/
function announce(
address _validator,
string calldata _storageLocation,
bytes calldata _signature
) external returns (bool);
}
exec
/bin/bash -lc "sed -n '13576,14360p' AUDIT_BUNDLE.txt" in /tmp/codex-3gxliacl
succeeded in 0ms:
// File contracts/middleware/InterchainAccountRouter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
// ============ Internal Imports ============
// ============ External Imports ============
/*
* @title A contract that allows accounts on chain A to call contracts via a
* proxy contract on chain B.
*/
contract InterchainAccountRouter is Router {
// ============ Libraries ============
using TypeCasts for address;
using TypeCasts for bytes32;
// ============ Constants ============
address internal implementation;
bytes32 internal bytecodeHash;
// ============ Public Storage ============
mapping(uint32 => bytes32) public isms;
// ============ Upgrade Gap ============
uint256[47] private __GAP;
// ============ Events ============
/**
* @notice Emitted when a default ISM is set for a remote domain
* @param domain The remote domain
* @param ism The address of the remote ISM
*/
event RemoteIsmEnrolled(uint32 indexed domain, bytes32 ism);
/**
* @notice Emitted when an interchain call is dispatched to a remote domain
* @param destination The destination domain on which to make the call
* @param owner The local owner of the remote ICA
* @param router The address of the remote router
* @param ism The address of the remote ISM
*/
event RemoteCallDispatched(
uint32 indexed destination,
address indexed owner,
bytes32 router,
bytes32 ism
);
/**
* @notice Emitted when an interchain account contract is deployed
* @param origin The domain of the chain where the message was sent from
* @param owner The address of the account that sent the message
* @param ism The address of the local ISM
* @param account The address of the proxy account that was created
*/
event InterchainAccountCreated(
uint32 indexed origin,
bytes32 indexed owner,
address ism,
address account
);
// ============ Constructor ============
constructor(address _mailbox) Router(_mailbox) {}
// ============ Initializers ============
/**
* @notice Initializes the contract with HyperlaneConnectionClient contracts
* @param _interchainGasPaymaster Unused but required by HyperlaneConnectionClient
* @param _interchainSecurityModule The address of the local ISM contract
* @param _owner The address with owner privileges
*/
function initialize(
address _interchainGasPaymaster,
address _interchainSecurityModule,
address _owner
) external initializer {
_MailboxClient_initialize(
_interchainGasPaymaster,
_interchainSecurityModule,
_owner
);
implementation = address(new OwnableMulticall(address(this)));
// cannot be stored immutably because it is dynamically sized
bytes memory _bytecode = MinimalProxy.bytecode(implementation);
bytecodeHash = keccak256(_bytecode);
}
/**
* @notice Registers the address of remote InterchainAccountRouter
* and ISM contracts to use as a default when making interchain calls
* @param _destination The remote domain
* @param _router The address of the remote InterchainAccountRouter
* @param _ism The address of the remote ISM
*/
function enrollRemoteRouterAndIsm(
uint32 _destination,
bytes32 _router,
bytes32 _ism
) external onlyOwner {
_enrollRemoteRouterAndIsm(_destination, _router, _ism);
}
/**
* @notice Registers the address of remote InterchainAccountRouters
* and ISM contracts to use as defaults when making interchain calls
* @param _destinations The remote domains
* @param _routers The address of the remote InterchainAccountRouters
* @param _isms The address of the remote ISMs
*/
function enrollRemoteRouterAndIsms(
uint32[] calldata _destinations,
bytes32[] calldata _routers,
bytes32[] calldata _isms
) external onlyOwner {
require(
_destinations.length == _routers.length &&
_destinations.length == _isms.length,
"length mismatch"
);
for (uint256 i = 0; i < _destinations.length; i++) {
_enrollRemoteRouterAndIsm(_destinations[i], _routers[i], _isms[i]);
}
}
// ============ External Functions ============
/**
* @notice Dispatches a single remote call to be made by an owner's
* interchain account on the destination domain
* @dev Uses the default router and ISM addresses for the destination
* domain, reverting if none have been configured
* @param _destination The remote domain of the chain to make calls on
* @param _to The address of the contract to call
* @param _value The value to include in the call
* @param _data The calldata
* @return The Hyperlane message ID
*/
function callRemote(
uint32 _destination,
address _to,
uint256 _value,
bytes memory _data
) external returns (bytes32) {
bytes32 _router = routers(_destination);
bytes32 _ism = isms[_destination];
bytes memory _body = InterchainAccountMessage.encode(
msg.sender,
_ism,
_to,
_value,
_data
);
return _dispatchMessage(_destination, _router, _ism, _body);
}
/**
* @notice Dispatches a sequence of remote calls to be made by an owner's
* interchain account on the destination domain
* @dev Uses the default router and ISM addresses for the destination
* domain, reverting if none have been configured
* @dev Recommend using CallLib.build to format the interchain calls.
* @param _destination The remote domain of the chain to make calls on
* @param _calls The sequence of calls to make
* @return The Hyperlane message ID
*/
function callRemote(
uint32 _destination,
CallLib.Call[] calldata _calls
) external returns (bytes32) {
bytes32 _router = routers(_destination);
bytes32 _ism = isms[_destination];
return callRemoteWithOverrides(_destination, _router, _ism, _calls);
}
/**
* @notice Handles dispatched messages by relaying calls to the interchain account
* @param _origin The origin domain of the interchain account
* @param _sender The sender of the interchain message
* @param _message The InterchainAccountMessage containing the account
* owner, ISM, and sequence of calls to be relayed
* @dev Does not need to be onlyRemoteRouter, as this application is designed
* to receive messages from untrusted remote contracts.
*/
function handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) external payable override onlyMailbox {
(
bytes32 _owner,
bytes32 _ism,
CallLib.Call[] memory _calls
) = InterchainAccountMessage.decode(_message);
OwnableMulticall _interchainAccount = getDeployedInterchainAccount(
_origin,
_owner,
_sender,
_ism.bytes32ToAddress()
);
_interchainAccount.multicall(_calls);
}
/**
* @notice Returns the local address of an interchain account
* @dev This interchain account is not guaranteed to have been deployed
* @param _origin The remote origin domain of the interchain account
* @param _router The remote origin InterchainAccountRouter
* @param _owner The remote owner of the interchain account
* @param _ism The local address of the ISM
* @return The local address of the interchain account
*/
function getLocalInterchainAccount(
uint32 _origin,
address _owner,
address _router,
address _ism
) external view returns (OwnableMulticall) {
return
getLocalInterchainAccount(
_origin,
_owner.addressToBytes32(),
_router.addressToBytes32(),
_ism
);
}
/**
* @notice Returns the remote address of a locally owned interchain account
* @dev This interchain account is not guaranteed to have been deployed
* @dev This function will only work if the destination domain is
* EVM compatible
* @param _destination The remote destination domain of the interchain account
* @param _owner The local owner of the interchain account
* @return The remote address of the interchain account
*/
function getRemoteInterchainAccount(
uint32 _destination,
address _owner
) external view returns (address) {
address _router = routers(_destination).bytes32ToAddress();
address _ism = isms[_destination].bytes32ToAddress();
return getRemoteInterchainAccount(_owner, _router, _ism);
}
// ============ Public Functions ============
/**
* @notice Returns and deploys (if not already) an interchain account
* @param _origin The remote origin domain of the interchain account
* @param _owner The remote owner of the interchain account
* @param _router The remote origin InterchainAccountRouter
* @param _ism The local address of the ISM
* @return The address of the interchain account
*/
function getDeployedInterchainAccount(
uint32 _origin,
address _owner,
address _router,
address _ism
) public returns (OwnableMulticall) {
return
getDeployedInterchainAccount(
_origin,
_owner.addressToBytes32(),
_router.addressToBytes32(),
_ism
);
}
/**
* @notice Returns and deploys (if not already) an interchain account
* @param _origin The remote origin domain of the interchain account
* @param _owner The remote owner of the interchain account
* @param _router The remote origin InterchainAccountRouter
* @param _ism The local address of the ISM
* @return The address of the interchain account
*/
function getDeployedInterchainAccount(
uint32 _origin,
bytes32 _owner,
bytes32 _router,
address _ism
) public returns (OwnableMulticall) {
bytes32 _salt = _getSalt(
_origin,
_owner,
_router,
_ism.addressToBytes32()
);
address payable _account = _getLocalInterchainAccount(_salt);
if (!Address.isContract(_account)) {
bytes memory _bytecode = MinimalProxy.bytecode(implementation);
_account = payable(Create2.deploy(0, _salt, _bytecode));
emit InterchainAccountCreated(_origin, _owner, _ism, _account);
}
return OwnableMulticall(_account);
}
/**
* @notice Returns the local address of a remotely owned interchain account
* @dev This interchain account is not guaranteed to have been deployed
* @param _origin The remote origin domain of the interchain account
* @param _owner The remote owner of the interchain account
* @param _router The remote InterchainAccountRouter
* @param _ism The local address of the ISM
* @return The local address of the interchain account
*/
function getLocalInterchainAccount(
uint32 _origin,
bytes32 _owner,
bytes32 _router,
address _ism
) public view returns (OwnableMulticall) {
return
OwnableMulticall(
_getLocalInterchainAccount(
_getSalt(_origin, _owner, _router, _ism.addressToBytes32())
)
);
}
/**
* @notice Returns the remote address of a locally owned interchain account
* @dev This interchain account is not guaranteed to have been deployed
* @dev This function will only work if the destination domain is
* EVM compatible
* @param _owner The local owner of the interchain account
* @param _router The remote InterchainAccountRouter
* @param _ism The remote address of the ISM
* @return The remote address of the interchain account
*/
function getRemoteInterchainAccount(
address _owner,
address _router,
address _ism
) public view returns (address) {
require(_router != address(0), "no router specified for destination");
// Derives the address of the first contract deployed by _router using
// the CREATE opcode.
address _implementation = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
bytes1(0xd6),
bytes1(0x94),
_router,
bytes1(0x01)
)
)
)
)
);
bytes memory _proxyBytecode = MinimalProxy.bytecode(_implementation);
bytes32 _bytecodeHash = keccak256(_proxyBytecode);
bytes32 _salt = _getSalt(
localDomain,
_owner.addressToBytes32(),
address(this).addressToBytes32(),
_ism.addressToBytes32()
);
return Create2.computeAddress(_salt, _bytecodeHash, _router);
}
/**
* @notice Dispatches a sequence of remote calls to be made by an owner's
* interchain account on the destination domain
* @dev Recommend using CallLib.build to format the interchain calls
* @param _destination The remote domain of the chain to make calls on
* @param _router The remote router address
* @param _ism The remote ISM address
* @param _calls The sequence of calls to make
* @return The Hyperlane message ID
*/
function callRemoteWithOverrides(
uint32 _destination,
bytes32 _router,
bytes32 _ism,
CallLib.Call[] calldata _calls
) public returns (bytes32) {
bytes memory _body = InterchainAccountMessage.encode(
msg.sender,
_ism,
_calls
);
return _dispatchMessage(_destination, _router, _ism, _body);
}
// ============ Internal Functions ============
/**
* @dev Required for use of Router, compiler will not include this function in the bytecode
*/
function _handle(uint32, bytes32, bytes calldata) internal pure override {
assert(false);
}
/**
* @notice Overrides Router._enrollRemoteRouter to also enroll a default ISM
* @param _destination The remote domain
* @param _address The address of the remote InterchainAccountRouter
* @dev Sets the default ISM to the zero address
*/
function _enrollRemoteRouter(
uint32 _destination,
bytes32 _address
) internal override {
_enrollRemoteRouterAndIsm(_destination, _address, bytes32(0));
}
// ============ Private Functions ============
/**
* @notice Registers the address of a remote ISM contract to use as default
* @param _destination The remote domain
* @param _ism The address of the remote ISM
*/
function _enrollRemoteIsm(uint32 _destination, bytes32 _ism) private {
isms[_destination] = _ism;
emit RemoteIsmEnrolled(_destination, _ism);
}
/**
* @notice Registers the address of remote InterchainAccountRouter
* and ISM contracts to use as a default when making interchain calls
* @param _destination The remote domain
* @param _router The address of the remote InterchainAccountRouter
* @param _ism The address of the remote ISM
*/
function _enrollRemoteRouterAndIsm(
uint32 _destination,
bytes32 _router,
bytes32 _ism
) private {
require(
routers(_destination) == bytes32(0) &&
isms[_destination] == bytes32(0),
"router and ISM defaults are immutable once set"
);
Router._enrollRemoteRouter(_destination, _router);
_enrollRemoteIsm(_destination, _ism);
}
/**
* @notice Dispatches an InterchainAccountMessage to the remote router
* @param _destination The remote domain
* @param _router The address of the remote InterchainAccountRouter
* @param _ism The address of the remote ISM
* @param _body The InterchainAccountMessage body
*/
function _dispatchMessage(
uint32 _destination,
bytes32 _router,
bytes32 _ism,
bytes memory _body
) private returns (bytes32) {
require(_router != bytes32(0), "no router specified for destination");
emit RemoteCallDispatched(_destination, msg.sender, _router, _ism);
return mailbox.dispatch(_destination, _router, _body);
}
/**
* @notice Returns the salt used to deploy an interchain account
* @param _origin The remote origin domain of the interchain account
* @param _owner The remote owner of the interchain account
* @param _router The remote origin InterchainAccountRouter
* @param _ism The local address of the ISM
* @return The CREATE2 salt used for deploying the interchain account
*/
function _getSalt(
uint32 _origin,
bytes32 _owner,
bytes32 _router,
bytes32 _ism
) private pure returns (bytes32) {
return keccak256(abi.encodePacked(_origin, _owner, _router, _ism));
}
/**
* @notice Returns the address of the interchain account on the local chain
* @param _salt The CREATE2 salt used for deploying the interchain account
* @return The address of the interchain account
*/
function _getLocalInterchainAccount(
bytes32 _salt
) private view returns (address payable) {
return payable(Create2.computeAddress(_salt, bytecodeHash));
}
}
// File contracts/middleware/libs/InterchainQueryMessage.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
/**
* Format of message:
* [ 0: 32] Sender address
* [ 32: 64] Message type (left padded with zeroes)
* [ 64:???] Encoded call array
*/
library InterchainQueryMessage {
uint256 private constant SENDER_OFFSET = 0;
uint256 private constant TYPE_OFFSET = 32;
uint256 private constant CALLS_OFFSET = 64;
enum MessageType {
QUERY,
RESPONSE
}
/**
* @notice Parses and returns the query sender from the provided message
* @param _message The interchain query message
* @return The query sender as bytes32
*/
function sender(bytes calldata _message) internal pure returns (bytes32) {
return bytes32(_message[SENDER_OFFSET:TYPE_OFFSET]);
}
/**
* @notice Parses and returns the message type from the provided message
* @param _message The interchain query message
* @return The message type (query or response)
*/
function messageType(
bytes calldata _message
) internal pure returns (MessageType) {
// left padded with zeroes
return MessageType(uint8(bytes1(_message[CALLS_OFFSET - 1])));
}
/**
* @notice Returns formatted InterchainQueryMessage, type == QUERY
* @param _sender The query sender as bytes32
* @param _calls The sequence of queries to make, with the corresponding
* response callbacks
* @return Formatted message body
*/
function encode(
bytes32 _sender,
CallLib.StaticCallWithCallback[] calldata _calls
) internal pure returns (bytes memory) {
return abi.encode(_sender, MessageType.QUERY, _calls);
}
/**
* @notice Returns formatted InterchainQueryMessage, type == QUERY
* @param _sender The query sender as bytes32
* @param _to The address of the contract to query
* @param _data The calldata encoding the query
* @param _callback The calldata of the callback that will be made on the sender.
* The return value of the query will be appended.
* @return Formatted message body
*/
function encode(
bytes32 _sender,
address _to,
bytes memory _data,
bytes memory _callback
) internal pure returns (bytes memory) {
CallLib.StaticCallWithCallback[]
memory _calls = new CallLib.StaticCallWithCallback[](1);
_calls[0] = CallLib.build(_to, _data, _callback);
return abi.encode(_sender, MessageType.QUERY, _calls);
}
/**
* @notice Parses and returns the calls and callbacks from the message
* @param _message The interchain query message, type == QUERY
* @return _calls The sequence of queries to make with the corresponding
* response callbacks
*/
function callsWithCallbacks(
bytes calldata _message
) internal pure returns (CallLib.StaticCallWithCallback[] memory _calls) {
assert(messageType(_message) == MessageType.QUERY);
(, , _calls) = abi.decode(
_message,
(bytes32, MessageType, CallLib.StaticCallWithCallback[])
);
}
/**
* @notice Returns formatted InterchainQueryMessage, type == RESPONSE
* @param _sender The query sender as bytes32
* @param _calls The sequence of callbacks to make
* @return Formatted message body
*/
function encode(
bytes32 _sender,
bytes[] memory _calls
) internal pure returns (bytes memory) {
return abi.encode(_sender, MessageType.RESPONSE, _calls);
}
/**
* @notice Parses and returns the callbacks from the message
* @param _message The interchain query message, type == RESPONSE
* @return _calls The sequence of callbacks to make
*/
function rawCalls(
bytes calldata _message
) internal pure returns (bytes[] memory _calls) {
assert(messageType(_message) == MessageType.RESPONSE);
(, , _calls) = abi.decode(_message, (bytes32, MessageType, bytes[]));
}
}
// File contracts/middleware/InterchainQueryRouter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
// ============ Internal Imports ============
// ============ External Imports ============
/**
* @title Interchain Query Router that performs remote view calls on other chains and returns the result.
* @dev Currently does not support Sovereign Consensus (user specified Interchain Security Modules).
*/
contract InterchainQueryRouter is Router {
using TypeCasts for address;
using TypeCasts for bytes32;
using InterchainQueryMessage for bytes;
/**
* @notice Emitted when a query is dispatched to another chain.
* @param destination The domain of the chain to query.
* @param sender The address that dispatched the query.
*/
event QueryDispatched(uint32 indexed destination, address indexed sender);
/**
* @notice Emitted when a query is executed on the and callback dispatched to the origin chain.
* @param originDomain The domain of the chain that dispatched the query and receives the callback.
* @param sender The address to receive the result.
*/
event QueryExecuted(uint32 indexed originDomain, bytes32 indexed sender);
/**
* @notice Emitted when a query is resolved on the origin chain.
* @param destination The domain of the chain that was queried.
* @param sender The address that resolved the query.
*/
event QueryResolved(uint32 indexed destination, address indexed sender);
constructor(address _mailbox) Router(_mailbox) {}
/**
* @notice Initializes the Router contract with Hyperlane core contracts and the address of the interchain security module.
* @param _interchainGasPaymaster The address of the interchain gas paymaster contract.
* @param _interchainSecurityModule The address of the interchain security module contract.
* @param _owner The address with owner privileges.
*/
function initialize(
address _interchainGasPaymaster,
address _interchainSecurityModule,
address _owner
) external initializer {
_MailboxClient_initialize(
_interchainGasPaymaster,
_interchainSecurityModule,
_owner
);
}
/**
* @notice Dispatches a sequence of static calls (query) to the destination domain and set of callbacks to resolve the results on the dispatcher.
* @param _destination The domain of the chain to query.
* @param _to The address of the contract to query
* @param _data The calldata encoding the query
* @param _callback The calldata of the callback that will be made on the sender.
* The return value of the query will be appended.
* @dev Callbacks must be returned to the `msg.sender` for security reasons. Require this contract is the `msg.sender` on callbacks.
*/
function query(
uint32 _destination,
address _to,
bytes memory _data,
bytes memory _callback
) public returns (bytes32 messageId) {
emit QueryDispatched(_destination, msg.sender);
messageId = _dispatch(
_destination,
InterchainQueryMessage.encode(
msg.sender.addressToBytes32(),
_to,
_data,
_callback
)
);
}
/**
* @notice Dispatches a sequence of static calls (query) to the destination domain and set of callbacks to resolve the results on the dispatcher.
* @param _destination The domain of the chain to query.
* @param calls The sequence of static calls to dispatch and callbacks on the sender to resolve the results.
* @dev Recommend using CallLib.build to format the interchain calls.
* @dev Callbacks must be returned to the `msg.sender` for security reasons. Require this contract is the `msg.sender` on callbacks.
*/
function query(
uint32 _destination,
CallLib.StaticCallWithCallback[] calldata calls
) public returns (bytes32 messageId) {
emit QueryDispatched(_destination, msg.sender);
messageId = _dispatch(
_destination,
InterchainQueryMessage.encode(msg.sender.addressToBytes32(), calls)
);
}
/**
* @notice Handles a message from remote enrolled Interchain Query Router.
* @param _origin The domain of the chain that sent the message.
* @param _message The ABI-encoded interchain query.
*/
function _handle(
uint32 _origin,
bytes32, // router sender
bytes calldata _message
) internal override {
InterchainQueryMessage.MessageType messageType = _message.messageType();
bytes32 sender = _message.sender();
if (messageType == InterchainQueryMessage.MessageType.QUERY) {
CallLib.StaticCallWithCallback[]
memory callsWithCallback = InterchainQueryMessage
.callsWithCallbacks(_message);
bytes[] memory callbacks = CallLib.multistaticcall(
callsWithCallback
);
emit QueryExecuted(_origin, sender);
_dispatch(
_origin,
InterchainQueryMessage.encode(sender, callbacks)
);
} else if (messageType == InterchainQueryMessage.MessageType.RESPONSE) {
address senderAddress = sender.bytes32ToAddress();
bytes[] memory rawCalls = _message.rawCalls();
CallLib.multicallto(senderAddress, rawCalls);
emit QueryResolved(_origin, senderAddress);
} else {
assert(false);
}
}
}
// File contracts/middleware/liquidity-layer/interfaces/circle/ICircleMessageTransmitter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity ^0.8.13;
interface ICircleMessageTransmitter {
/**
* @notice Receive a message. Messages with a given nonce
* can only be broadcast once for a (sourceDomain, destinationDomain)
* pair. The message body of a valid message is passed to the
* specified recipient for further processing.
*
exec
/bin/bash -lc "sed -n '16250,16780p' AUDIT_BUNDLE.txt" in /tmp/codex-3gxliacl
succeeded in 0ms:
// File contracts/token/libs/TokenMessage.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
library TokenMessage {
function format(
bytes32 _recipient,
uint256 _amount,
bytes memory _metadata
) internal pure returns (bytes memory) {
return abi.encodePacked(_recipient, _amount, _metadata);
}
function recipient(bytes calldata message) internal pure returns (bytes32) {
return bytes32(message[0:32]);
}
function amount(bytes calldata message) internal pure returns (uint256) {
return uint256(bytes32(message[32:64]));
}
// alias for ERC721
function tokenId(bytes calldata message) internal pure returns (uint256) {
return amount(message);
}
function metadata(
bytes calldata message
) internal pure returns (bytes calldata) {
return message[64:];
}
}
// File contracts/token/libs/TokenRouter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane Token Router that extends Router with abstract token (ERC20/ERC721) remote transfer functionality.
* @author Abacus Works
*/
abstract contract TokenRouter is GasRouter {
using TypeCasts for bytes32;
using TypeCasts for address;
using TokenMessage for bytes;
/**
* @dev Emitted on `transferRemote` when a transfer message is dispatched.
* @param destination The identifier of the destination chain.
* @param recipient The address of the recipient on the destination chain.
* @param amount The amount of tokens burnt on the origin chain.
*/
event SentTransferRemote(
uint32 indexed destination,
bytes32 indexed recipient,
uint256 amount
);
/**
* @dev Emitted on `_handle` when a transfer message is processed.
* @param origin The identifier of the origin chain.
* @param recipient The address of the recipient on the destination chain.
* @param amount The amount of tokens minted on the destination chain.
*/
event ReceivedTransferRemote(
uint32 indexed origin,
bytes32 indexed recipient,
uint256 amount
);
constructor(address _mailbox) GasRouter(_mailbox) {}
/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_transferFromSender` implementation.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @return messageId The identifier of the dispatched message.
*/
function transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId
) external payable virtual returns (bytes32 messageId) {
return
_transferRemote(_destination, _recipient, _amountOrId, msg.value);
}
/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_transferFromSender` implementation.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @param _gasPayment The amount of native token to pay for interchain gas.
* @return messageId The identifier of the dispatched message.
*/
function _transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _gasPayment
) internal returns (bytes32 messageId) {
bytes memory metadata = _transferFromSender(_amountOrId);
messageId = _dispatch(
_destination,
_gasPayment,
TokenMessage.format(_recipient, _amountOrId, metadata)
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
}
/**
* @dev Should transfer `_amountOrId` of tokens from `msg.sender` to this token router.
* @dev Called by `transferRemote` before message dispatch.
* @dev Optionally returns `metadata` associated with the transfer to be passed in message.
*/
function _transferFromSender(
uint256 _amountOrId
) internal virtual returns (bytes memory metadata);
/**
* @notice Returns the balance of `account` on this token router.
* @param account The address to query the balance of.
* @return The balance of `account`.
*/
function balanceOf(address account) external virtual returns (uint256);
/**
* @dev Mints tokens to recipient when router receives transfer message.
* @dev Emits `ReceivedTransferRemote` event on the destination chain.
* @param _origin The identifier of the origin chain.
* @param _message The encoded remote transfer message containing the recipient address and amount.
*/
function _handle(
uint32 _origin,
bytes32,
bytes calldata _message
) internal virtual override {
bytes32 recipient = _message.recipient();
uint256 amount = _message.amount();
bytes calldata metadata = _message.metadata();
_transferTo(recipient.bytes32ToAddress(), amount, metadata);
emit ReceivedTransferRemote(_origin, recipient, amount);
}
/**
* @dev Should transfer `_amountOrId` of tokens from this token router to `_recipient`.
* @dev Called by `handle` after message decoding.
* @dev Optionally handles `metadata` associated with transfer passed in message.
*/
function _transferTo(
address _recipient,
uint256 _amountOrId,
bytes calldata metadata
) internal virtual;
}
// File contracts/token/HypERC20.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC20 Token Router that extends ERC20 with remote transfer functionality.
* @author Abacus Works
* @dev Supply on each chain is not constant but the aggregate supply across all chains is.
*/
contract HypERC20 is ERC20Upgradeable, TokenRouter {
uint8 private immutable _decimals;
constructor(uint8 __decimals, address _mailbox) TokenRouter(_mailbox) {
_decimals = __decimals;
}
/**
* @notice Initializes the Hyperlane router, ERC20 metadata, and mints initial supply to deployer.
* @param _totalSupply The initial supply of the token.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
*/
function initialize(
uint256 _totalSupply,
string memory _name,
string memory _symbol
) external initializer {
// Initialize ERC20 metadata
__ERC20_init(_name, _symbol);
_mint(msg.sender, _totalSupply);
}
function decimals() public view override returns (uint8) {
return _decimals;
}
function balanceOf(
address _account
)
public
view
virtual
override(TokenRouter, ERC20Upgradeable)
returns (uint256)
{
return ERC20Upgradeable.balanceOf(_account);
}
/**
* @dev Burns `_amount` of token from `msg.sender` balance.
* @inheritdoc TokenRouter
*/
function _transferFromSender(
uint256 _amount
) internal override returns (bytes memory) {
_burn(msg.sender, _amount);
return bytes(""); // no metadata
}
/**
* @dev Mints `_amount` of token to `_recipient` balance.
* @inheritdoc TokenRouter
*/
function _transferTo(
address _recipient,
uint256 _amount,
bytes calldata // no metadata
) internal virtual override {
_mint(_recipient, _amount);
}
}
// File contracts/token/libs/FastTokenRouter.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Common FastTokenRouter functionality for ERC20 Tokens with remote transfer support.
* @author Abacus Works
*/
abstract contract FastTokenRouter is TokenRouter {
using TypeCasts for bytes32;
using TokenMessage for bytes;
uint256 public fastTransferId;
// maps `fastTransferId` to the filler address.
mapping(bytes32 => address) filledFastTransfers;
/**
* @dev delegates transfer logic to `_transferTo`.
* @inheritdoc TokenRouter
*/
function _handle(
uint32 _origin,
bytes32,
bytes calldata _message
) internal virtual override {
bytes32 recipient = _message.recipient();
uint256 amount = _message.amount();
bytes calldata metadata = _message.metadata();
_transferTo(recipient.bytes32ToAddress(), amount, _origin, metadata);
emit ReceivedTransferRemote(_origin, recipient, amount);
}
/**
* @dev Transfers `_amount` of token to `_recipient`/`fastFiller` who provided LP.
* @dev Called by `handle` after message decoding.
*/
function _transferTo(
address _recipient,
uint256 _amount,
uint32 _origin,
bytes calldata _metadata
) internal virtual {
address _tokenRecipient = _getTokenRecipient(
_recipient,
_amount,
_origin,
_metadata
);
_fastTransferTo(_tokenRecipient, _amount);
}
/**
* @dev allows an external user to full an unfilled fast transfer order.
* @param _recipient The recepient of the wrapped token on base chain.
* @param _amount The amount of wrapped tokens that is being bridged.
* @param _fastFee The fee the bridging entity will pay.
* @param _fastTransferId Id assigned on the remote chain to uniquely identify the transfer.
*/
function fillFastTransfer(
address _recipient,
uint256 _amount,
uint256 _fastFee,
uint32 _origin,
uint256 _fastTransferId
) external virtual {
bytes32 filledFastTransfersKey = _getFastTransfersKey(
_origin,
_fastTransferId,
_amount,
_fastFee,
_recipient
);
require(
filledFastTransfers[filledFastTransfersKey] == address(0),
"request already filled"
);
filledFastTransfers[filledFastTransfersKey] = msg.sender;
_fastRecieveFrom(msg.sender, _amount - _fastFee);
_fastTransferTo(_recipient, _amount - _fastFee);
}
/**
* @dev Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_fastTransferFromSender` implementation.
* @dev Emits `SentTransferRemote` event on the origin chain.
* @param _destination The identifier of the destination chain.
* @param _recipient The address of the recipient on the destination chain.
* @param _amountOrId The amount or identifier of tokens to be sent to the remote recipient.
* @return messageId The identifier of the dispatched message.
*/
function fastTransferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amountOrId,
uint256 _fastFee
) public payable virtual returns (bytes32 messageId) {
uint256 _fastTransferId = fastTransferId + 1;
fastTransferId = _fastTransferId;
bytes memory metadata = _fastTransferFromSender(
_amountOrId,
_fastFee,
_fastTransferId
);
messageId = _dispatch(
_destination,
TokenMessage.format(_recipient, _amountOrId, metadata)
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
}
/**
* @dev Burns `_amount` of token from `msg.sender` balance.
* @dev Pays `_fastFee` of tokens to LP on source chain.
* @dev Returns `fastFee` as bytes in the form of metadata.
*/
function _fastTransferFromSender(
uint256 _amount,
uint256 _fastFee,
uint256 _fastTransferId
) internal virtual returns (bytes memory) {
_fastRecieveFrom(msg.sender, _amount);
return abi.encode(_fastFee, _fastTransferId);
}
/**
* @dev returns an address that indicates who should recieve the bridged tokens.
* @dev if _fastFees was inlcuded and someone filled the order before the mailbox made the contract call, the filler gets the funds.
*/
function _getTokenRecipient(
address _recipient,
uint256 _amount,
uint32 _origin,
bytes calldata _metadata
) internal view returns (address) {
if (_metadata.length == 0) {
return _recipient;
}
// decode metadata to extract `_fastFee` and `_fastTransferId`.
(uint256 _fastFee, uint256 _fastTransferId) = abi.decode(
_metadata,
(uint256, uint256)
);
address _fillerAddress = filledFastTransfers[
_getFastTransfersKey(
_origin,
_fastTransferId,
_amount,
_fastFee,
_recipient
)
];
if (_fillerAddress != address(0)) {
return _fillerAddress;
}
return _recipient;
}
/**
* @dev generates the key for storing the filler address of fast transfers.
*/
function _getFastTransfersKey(
uint32 _origin,
uint256 _fastTransferId,
uint256 _amount,
uint256 _fastFee,
address _recipient
) internal pure returns (bytes32) {
return
keccak256(
abi.encodePacked(
_origin,
_fastTransferId,
_amount,
_fastFee,
_recipient
)
);
}
/**
* @dev Should transfer `_amount` of tokens to `_recipient`.
* @dev The implementation is delegated.
*/
function _fastTransferTo(
address _recipient,
uint256 _amount
) internal virtual;
/**
* @dev Should collect `amount` of tokens from `_sender`.
* @dev The implementation is delegated.
*/
function _fastRecieveFrom(
address _sender,
uint256 _amount
) internal virtual;
}
// File contracts/token/extensions/FastHypERC20.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC20 Token Router that extends ERC20 with remote transfer functionality.
* @author Abacus Works
* @dev Supply on each chain is not constant but the aggregate supply across all chains is.
*/
contract FastHypERC20 is FastTokenRouter, HypERC20 {
constructor(
uint8 __decimals,
address _mailbox
) HypERC20(__decimals, _mailbox) {}
/**
* @dev delegates transfer logic to `_transferTo`.
* @inheritdoc TokenRouter
*/
function _handle(
uint32 _origin,
bytes32 _sender,
bytes calldata _message
) internal virtual override(FastTokenRouter, TokenRouter) {
FastTokenRouter._handle(_origin, _sender, _message);
}
/**
* @dev Mints `_amount` of tokens to `_recipient`.
* @inheritdoc FastTokenRouter
*/
function _fastTransferTo(
address _recipient,
uint256 _amount
) internal override {
_mint(_recipient, _amount);
}
/**
* @dev Burns `_amount` of tokens from `_recipient`.
* @inheritdoc FastTokenRouter
*/
function _fastRecieveFrom(
address _sender,
uint256 _amount
) internal override {
_burn(_sender, _amount);
}
function balanceOf(
address _account
) public view virtual override(HypERC20, TokenRouter) returns (uint256) {
return ERC20Upgradeable.balanceOf(_account);
}
}
// File contracts/token/HypERC20Collateral.sol
// Original license: SPDX_License_Identifier: Apache-2.0
pragma solidity >=0.8.0;
/**
* @title Hyperlane ERC20 Token Collateral that wraps an existing ERC20 with remote transfer functionality.
* @author Abacus Works
*/
contract HypERC20Collateral is TokenRouter {
using SafeERC20 for IERC20;
IERC20 public immutable wrappedToken;
/**
* @notice Constructor
* @param erc20 Address of the token to keep as collateral
*/
exec
/bin/bash -lc "rg -n 'initializer|reinitializer|_disableInitializers|delegatecall|call\\{|staticcall\\(|functionCallWithValue|functionCall\\(|sendValue\\(|approve\\(|safeTransferFrom\\(|transferFrom\\(|recover\\(|ECDSA\\.recover|onlyOwner|onlyMailbox|onlyLiquidityLayerRouter' AUDIT_BUNDLE.txt | sed -n '1,260p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
78: function sendValue(address payable recipient, uint256 amount) internal {
81: (bool success, ) = recipient.call{value: amount}("");
103: function functionCall(address target, bytes memory data) internal returns (bytes memory) {
104: return functionCallWithValue(target, data, 0, "Address: low-level call failed");
113: function functionCall(
118: return functionCallWithValue(target, data, 0, errorMessage);
132: function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
133: return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
137: * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
142: function functionCallWithValue(
149: (bool success, bytes memory returndata) = target.call{value: value}(data);
174: (bool success, bytes memory returndata) = target.staticcall(data);
199: (bool success, bytes memory returndata) = target.delegatecall(data);
271: * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
272: * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
283: * function initialize() initializer public {
289: * function initializeV2() reinitializer(2) public {
295: * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
298: * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
299: * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
307: * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
313: * _disableInitializers();
336: * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
339: * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
344: modifier initializer() {
362: * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
366: * A reinitializer may be used after the original initialization step. This is essential to configure modules that
369: * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
372: * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
379: modifier reinitializer(uint8 version) {
390: * {initializer} and {reinitializer} modifiers, directly or indirectly.
398: * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
405: function _disableInitializers() internal virtual {
414: * @dev Returns the highest version that has been initialized. See {reinitializer}.
486: * `onlyOwner`, which can be applied to your functions to restrict their use to
508: modifier onlyOwner() {
529: * `onlyOwner` functions. Can only be called by the current owner.
534: function renounceOwnership() public virtual onlyOwner {
542: function transferOwnership(address newOwner) public virtual onlyOwner {
656: function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
672: function safeTransferFrom(address from, address to, uint256 tokenId) external;
690: function transferFrom(address from, address to, uint256 tokenId) external;
705: function approve(address to, uint256 tokenId) external;
834: function approve(address spender, uint256 amount) external returns (bool);
845: function transferFrom(address from, address to, uint256 amount) external returns (bool);
1019: function approve(address spender, uint256 amount) public virtual override returns (bool) {
1021: _approve(owner, spender, amount);
1041: function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
1062: _approve(owner, spender, allowance(owner, spender) + addedValue);
1085: _approve(owner, spender, currentAllowance - subtractedValue);
1191: function _approve(address owner, address spender, uint256 amount) internal virtual {
1212: _approve(owner, spender, currentAllowance - amount);
1955: function approve(address to, uint256 tokenId) public virtual override {
1964: _approve(to, tokenId);
1993: function transferFrom(address from, address to, uint256 tokenId) public virtual override {
2003: function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
2004: safeTransferFrom(from, to, tokenId, "");
2010: function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
2209: function _approve(address to, uint256 tokenId) internal virtual {
3536: * `onlyOwner`, which can be applied to your functions to restrict their use to
3554: modifier onlyOwner() {
3575: * `onlyOwner` functions. Can only be called by the current owner.
3580: function renounceOwnership() public virtual onlyOwner {
3588: function transferOwnership(address newOwner) public virtual onlyOwner {
3859: * `onlyOwner` maintenance operations. This gives time for users of the
4200: (bool success, ) = target.call{value: value}(data);
4410: function sendValue(address payable recipient, uint256 amount) internal {
4413: (bool success, ) = recipient.call{value: amount}("");
4435: function functionCall(address target, bytes memory data) internal returns (bytes memory) {
4436: return functionCallWithValue(target, data, 0, "Address: low-level call failed");
4445: function functionCall(
4450: return functionCallWithValue(target, data, 0, errorMessage);
4464: function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
4465: return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
4469: * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
4474: function functionCallWithValue(
4481: (bool success, bytes memory returndata) = target.call{value: value}(data);
4506: (bool success, bytes memory returndata) = target.staticcall(data);
4531: (bool success, bytes memory returndata) = target.delegatecall(data);
4903: * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
4926: let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
4932: // delegatecall returns 0 on error.
5234: (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b");
5249: (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440");
5261: function changeProxyAdmin(ITransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner {
5272: function upgrade(ITransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner {
5288: ) public payable virtual onlyOwner {
5468: function approve(address spender, uint256 amount) external returns (bool);
5479: function transferFrom(address from, address to, uint256 amount) external returns (bool);
5648: function approve(address spender, uint256 amount) public virtual override returns (bool) {
5650: _approve(owner, spender, amount);
5670: function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
5691: _approve(owner, spender, allowance(owner, spender) + addedValue);
5714: _approve(owner, spender, currentAllowance - subtractedValue);
5820: function _approve(address owner, address spender, uint256 amount) internal virtual {
5841: _approve(owner, spender, currentAllowance - amount);
5977: function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
6065: bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
6142: function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
6158: function safeTransferFrom(address from, address to, uint256 tokenId) external;
6176: function transferFrom(address from, address to, uint256 tokenId) external;
6191: function approve(address to, uint256 tokenId) external;
6363: function approve(address to, uint256 tokenId) public virtual override {
6372: _approve(to, tokenId);
6401: function transferFrom(address from, address to, uint256 tokenId) public virtual override {
6411: function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
6412: safeTransferFrom(from, to, tokenId, "");
6418: function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual override {
6617: function _approve(address to, uint256 tokenId) internal virtual {
6999: function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
7023: function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
7036: // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
7050: address signer = ecrecover(hash, v, r, s);
7062: function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
8556: modifier onlyMailbox() {
8574: function setHook(address _hook) public onlyContractOrNull(_hook) onlyOwner {
8584: ) public onlyContractOrNull(_module) onlyOwner {
8801: function unenrollRemoteRouter(uint32 _domain) external virtual onlyOwner {
8813: ) external virtual onlyOwner {
8825: ) external virtual onlyOwner {
8839: ) external virtual onlyOwner {
8856: ) external payable virtual override onlyMailbox {
9148: ) external onlyOwner {
9159: function setDestinationGas(uint32 domain, uint256 gas) external onlyOwner {
9360: function setAuthorizedHook(bytes32 _hook) external initializer {
9392: payable(message.recipientAddress()).sendValue(_msgValue);
10153: ) public initializer {
10165: (bool success, ) = beneficiary.call{value: address(this).balance}("");
10175: ) external onlyOwner {
10190: function setBeneficiary(address _beneficiary) external onlyOwner {
10223: payable(_refundAddress).sendValue(_overpayment);
10441: ) external onlyOwner {
10454: ) external onlyOwner {
10931: function pause() external onlyOwner {
10935: function unpause() external onlyOwner {
11035: function setProtocolFee(uint256 _protocolFee) external onlyOwner {
11043: function setBeneficiary(address _beneficiary) external onlyOwner {
11051: payable(beneficiary).sendValue(address(this).balance);
11068: payable(metadata.refundAddress(message.senderAddress())).sendValue(
11155: function setHook(uint32 _destination, address _hook) public onlyOwner {
11159: function setHooks(HookConfig[] calldata configs) external onlyOwner {
11259: ) external onlyOwner {
11690: address _signer = ECDSA.recover(_digest, signatureAt(_metadata, i));
12414: address _signer = ECDSA.recover(_announcementDigest, _signature);
12521: function pause() external onlyOwner {
12525: function unpause() external onlyOwner {
12612: function initialize(address _owner) public initializer {
12627: ) public initializer {
12645: ) external onlyOwner {
12653: function remove(uint32 _domain) external onlyOwner {
12855: Address.functionCallWithValue(
12862: function staticcall(
12872: function staticcall(
12875: return bytes.concat(_call.callback, staticcall(_call._call));
12889: function multistaticcall(
12896: callbacks[i] = staticcall(_calls[i]);
12908: Address.functionCall(to, calls[i]);
13193: ) external initializer {
13460: function setDefaultIsm(address _module) public onlyOwner {
13473: function setDefaultHook(address _hook) public onlyOwner {
13486: function setRequiredHook(address _hook) public onlyOwner {
13505: (bool success, bytes memory returnData) = _recipient.staticcall(
13562: modifier onlyOwner() {
13567: function multicall(CallLib.Call[] calldata calls) external onlyOwner {
13668: ) external initializer {
13692: ) external onlyOwner {
13707: ) external onlyOwner {
13780: ) external payable override onlyMailbox {
14259: ) external initializer {
14329: bytes[] memory callbacks = CallLib.multistaticcall(
14406: * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
14434: * - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
14532: modifier onlyLiquidityLayerRouter() {
14550: ) external initializer {
14566: ) external onlyLiquidityLayerRouter returns (bytes memory) {
14581: IERC20(_token).approve(address(tokenMessenger), _amount),
14602: ) external onlyLiquidityLayerRouter returns (address, uint256) {
14648: ) external onlyOwner {
14657: ) external onlyOwner {
14682: ) external onlyOwner {
14852: modifier onlyLiquidityLayerRouter() {
14866: ) public initializer {
14885: ) external onlyLiquidityLayerRouter returns (bytes memory) {
14896: IERC20(_token).approve(address(portalTokenBridge), _amount),
14928: ) external onlyLiquidityLayerRouter returns (address, uint256) {
14996: ) external onlyOwner {
15078: ) external initializer {
15097: IERC20(_token).safeTransferFrom(msg.sender, address(_adapter), _amount);
15169: ) external onlyOwner {
15266: token.transferFrom(msg.sender, address(this), _amount);
15409: _disableInitializers();
15557: token.transferFrom(msg.sender, address(this), amount);
15710: function setInterchainSecurityModule(address _ism) external onlyOwner {
16121: ) public initializer {
16445: ) external initializer {
16798: wrappedToken.safeTransferFrom(msg.sender, address(this), _amount);
16871: wrappedToken.safeTransferFrom(_sender, address(this), _amount);
16919: wrappedToken.transferFrom(msg.sender, address(this), _tokenId);
16932: wrappedToken.safeTransferFrom(address(this), _recipient, _tokenId);
16996: ) external initializer {
17200: Address.sendValue(payable(_recipient), _amount);
exec
/bin/bash -lc "rg -n 'function initialize\\(|function setAuthorizedHook\\(' AUDIT_BUNDLE.txt | sed -n '1,120p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
283: * function initialize() initializer public {
9360: function setAuthorizedHook(bytes32 _hook) external initializer {
10150: function initialize(
12612: function initialize(address _owner) public initializer {
12623: function initialize(
13188: function initialize(
13664: function initialize(
14255: function initialize(
14545: function initialize(
14862: function initialize(
15074: function initialize(
16027: function initialize(address _queryRouterAddress) external {
16118: function initialize(
16441: function initialize(
16992: function initialize(
exec
/bin/bash -lc "sed -n '16090,16140p' AUDIT_BUNDLE.txt && sed -n '16436,16458p' AUDIT_BUNDLE.txt && sed -n '16988,17015p' AUDIT_BUNDLE.txt" in /tmp/codex-3gxliacl
succeeded in 0ms:
function queryAndPayFor(
uint32 _destinationDomain,
address _target,
bytes calldata _targetData,
bytes4 _callbackSelector,
uint256 /*_gasAmount*/
) internal {
queryRouter.query(
_destinationDomain,
_target,
_targetData,
abi.encodePacked(_callbackSelector)
);
}
}
// File contracts/test/TestRouter.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.6.11;
contract TestRouter is Router {
event InitializeOverload();
constructor(address _mailbox) Router(_mailbox) {}
function initialize(
address _hook,
address _interchainSecurityModule
) public initializer {
_MailboxClient_initialize(_hook, _interchainSecurityModule, msg.sender);
}
function _handle(uint32, bytes32, bytes calldata) internal pure override {}
function isRemoteRouter(
uint32 _domain,
bytes32 _potentialRemoteRouter
) external view returns (bool) {
return _isRemoteRouter(_domain, _potentialRemoteRouter);
}
function mustHaveRemoteRouter(
uint32 _domain
) external view returns (bytes32) {
return _mustHaveRemoteRouter(_domain);
}
function dispatch(uint32 _destination, bytes memory _msg) external payable {
* @notice Initializes the Hyperlane router, ERC20 metadata, and mints initial supply to deployer.
* @param _totalSupply The initial supply of the token.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
*/
function initialize(
uint256 _totalSupply,
string memory _name,
string memory _symbol
) external initializer {
// Initialize ERC20 metadata
__ERC20_init(_name, _symbol);
_mint(msg.sender, _totalSupply);
}
function decimals() public view override returns (uint8) {
return _decimals;
}
function balanceOf(
address _account
)
public
* @param _mintAmount The amount of NFTs to mint to `msg.sender`.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
*/
function initialize(
uint256 _mintAmount,
string memory _name,
string memory _symbol
) external initializer {
address owner = msg.sender;
_transferOwnership(owner);
__ERC721_init(_name, _symbol);
for (uint256 i = 0; i < _mintAmount; i++) {
_safeMint(owner, i);
}
}
function balanceOf(
address _account
)
public
view
virtual
override(TokenRouter, ERC721Upgradeable, IERC721Upgradeable)
returns (uint256)
{
return ERC721Upgradeable.balanceOf(_account);
exec
/bin/bash -lc "sed -n '11220,11270p' AUDIT_BUNDLE.txt && sed -n '11182,11210p' AUDIT_BUNDLE.txt" in /tmp/codex-3gxliacl
succeeded in 0ms:
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
contract DestinationRecipientRoutingHook is DomainRoutingHook {
using Message for bytes;
/// @notice destination => recipient =>custom hook
mapping(uint32 => mapping(bytes32 => address)) public customHooks;
constructor(
address mailbox,
address owner
) DomainRoutingHook(mailbox, owner) {}
function _postDispatch(
bytes calldata metadata,
bytes calldata message
) internal override {
address customHookPreset = customHooks[message.destination()][
message.recipient()
];
if (customHookPreset != address(0)) {
IPostDispatchHook(customHookPreset).postDispatch{value: msg.value}(
metadata,
message
);
} else {
super._postDispatch(metadata, message);
}
}
function configCustomHook(
uint32 destinationDomain,
bytes32 recipient,
address hook
) external onlyOwner {
customHooks[destinationDomain][recipient] = hook;
}
}
// File contracts/hooks/routing/FallbackDomainRoutingHook.sol
// Original license: SPDX_License_Identifier: MIT
pragma solidity >=0.8.0;
/*@@@@@@@ @@@@@@@@@
);
}
/// @inheritdoc AbstractPostDispatchHook
function _quoteDispatch(
bytes calldata metadata,
bytes calldata message
) internal view virtual override returns (uint256) {
return _getConfiguredHook(message).quoteDispatch(metadata, message);
}
function _getConfiguredHook(
bytes calldata message
) internal view virtual returns (IPostDispatchHook hook) {
hook = hooks[message.destination()];
require(
address(hook) != address(0),
string.concat(
"No hook configured for destination: ",
message.destination().toString()
)
);
}
}
// File contracts/hooks/routing/DestinationRecipientRoutingHook.sol
// Original license: SPDX_License_Identifier: MIT
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '9190,9245p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
9190 pragma solidity >=0.8.0;
9191
9192 /*@@@@@@@ @@@@@@@@@
9193 @@@@@@@@@ @@@@@@@@@
9194 @@@@@@@@@ @@@@@@@@@
9195 @@@@@@@@@ @@@@@@@@@
9196 @@@@@@@@@@@@@@@@@@@@@@@@@
9197 @@@@@ HYPERLANE @@@@@@@
9198 @@@@@@@@@@@@@@@@@@@@@@@@@
9199 @@@@@@@@@ @@@@@@@@@
9200 @@@@@@@@@ @@@@@@@@@
9201 @@@@@@@@@ @@@@@@@@@
9202 @@@@@@@@@ @@@@@@@@*/
9203
9204 // ============ Internal Imports ============
9205
9206
9207 /**
9208 * @title AbstractPostDispatch
9209 * @notice Abstract post dispatch hook supporting the current global hook metadata variant.
9210 */
9211 abstract contract AbstractPostDispatchHook is IPostDispatchHook {
9212 using StandardHookMetadata for bytes;
9213
9214 // ============ External functions ============
9215
9216 /// @inheritdoc IPostDispatchHook
9217 function supportsMetadata(
9218 bytes calldata metadata
9219 ) public pure virtual override returns (bool) {
9220 return
9221 metadata.length == 0 ||
9222 metadata.variant() == StandardHookMetadata.VARIANT;
9223 }
9224
9225 /// @inheritdoc IPostDispatchHook
9226 function postDispatch(
9227 bytes calldata metadata,
9228 bytes calldata message
9229 ) external payable override {
9230 require(
9231 supportsMetadata(metadata),
9232 "AbstractPostDispatchHook: invalid metadata variant"
9233 );
9234 _postDispatch(metadata, message);
9235 }
9236
9237 /// @inheritdoc IPostDispatchHook
9238 function quoteDispatch(
9239 bytes calldata metadata,
9240 bytes calldata message
9241 ) public view override returns (uint256) {
9242 require(
9243 supportsMetadata(metadata),
9244 "AbstractPostDispatchHook: invalid metadata variant"
9245 );
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '9460,9518p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
9460 // left-padded address for ISM to verify messages
9461 bytes32 public immutable ism;
9462 // Domain of chain on which the ISM is deployed
9463 uint32 public immutable destinationDomain;
9464
9465 // ============ Constructor ============
9466
9467 constructor(
9468 address _mailbox,
9469 uint32 _destinationDomain,
9470 bytes32 _ism
9471 ) MailboxClient(_mailbox) {
9472 require(_ism != bytes32(0), "AbstractMessageIdAuthHook: invalid ISM");
9473 require(
9474 _destinationDomain != 0,
9475 "AbstractMessageIdAuthHook: invalid destination domain"
9476 );
9477 ism = _ism;
9478 destinationDomain = _destinationDomain;
9479 }
9480
9481 /// @inheritdoc IPostDispatchHook
9482 function hookType() external pure returns (uint8) {
9483 return uint8(IPostDispatchHook.Types.ID_AUTH_ISM);
9484 }
9485
9486 // ============ Internal functions ============
9487
9488 /// @inheritdoc AbstractPostDispatchHook
9489 function _postDispatch(
9490 bytes calldata metadata,
9491 bytes calldata message
9492 ) internal override {
9493 bytes32 id = message.id();
9494 require(
9495 _isLatestDispatched(id),
9496 "AbstractMessageIdAuthHook: message not latest dispatched"
9497 );
9498 require(
9499 message.destination() == destinationDomain,
9500 "AbstractMessageIdAuthHook: invalid destination domain"
9501 );
9502 bytes memory payload = abi.encodeCall(
9503 AbstractMessageIdAuthorizedIsm.verifyMessageId,
9504 id
9505 );
9506 _sendMessageId(metadata, payload);
9507 }
9508
9509 /**
9510 * @notice Send a message to the ISM.
9511 * @param metadata The metadata for the hook caller
9512 * @param payload The payload for call to the ISM
9513 */
9514 function _sendMessageId(
9515 bytes calldata metadata,
9516 bytes memory payload
9517 ) internal virtual;
9518 }
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '10700,10770p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
10700 @@@@@@@@@@@@@@@@@@@@@@@@@
10701 @@@@@ HYPERLANE @@@@@@@
10702 @@@@@@@@@@@@@@@@@@@@@@@@@
10703 @@@@@@@@@ @@@@@@@@@
10704 @@@@@@@@@ @@@@@@@@@
10705 @@@@@@@@@ @@@@@@@@@
10706 @@@@@@@@@ @@@@@@@@*/
10707
10708
10709
10710
10711
10712
10713
10714 contract MerkleTreeHook is AbstractPostDispatchHook, MailboxClient, Indexed {
10715 using Message for bytes;
10716 using MerkleLib for MerkleLib.Tree;
10717 using StandardHookMetadata for bytes;
10718
10719 // An incremental merkle tree used to store outbound message IDs.
10720 MerkleLib.Tree internal _tree;
10721
10722 event InsertedIntoTree(bytes32 messageId, uint32 index);
10723
10724 constructor(address _mailbox) MailboxClient(_mailbox) {}
10725
10726 // count cannot exceed 2**TREE_DEPTH, see MerkleLib.sol
10727 function count() public view returns (uint32) {
10728 return uint32(_tree.count);
10729 }
10730
10731 function root() public view returns (bytes32) {
10732 return _tree.root();
10733 }
10734
10735 function tree() public view returns (MerkleLib.Tree memory) {
10736 return _tree;
10737 }
10738
10739 function latestCheckpoint() external view returns (bytes32, uint32) {
10740 return (root(), count() - 1);
10741 }
10742
10743 // ============ External Functions ============
10744
10745 /// @inheritdoc IPostDispatchHook
10746 function hookType() external pure override returns (uint8) {
10747 return uint8(IPostDispatchHook.Types.MERKLE_TREE);
10748 }
10749
10750 // ============ Internal Functions ============
10751
10752 /// @inheritdoc AbstractPostDispatchHook
10753 function _postDispatch(
10754 bytes calldata,
10755 /*metadata*/
10756 bytes calldata message
10757 ) internal override {
10758 require(msg.value == 0, "MerkleTreeHook: no value expected");
10759
10760 // ensure messages which were not dispatched are not inserted into the tree
10761 bytes32 id = message.id();
10762 require(_isLatestDispatched(id), "message not dispatching");
10763
10764 uint32 index = count();
10765 _tree.insert(id);
10766 emit InsertedIntoTree(id, index);
10767 }
10768
10769 /// @inheritdoc AbstractPostDispatchHook
10770 function _quoteDispatch(
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '13180,13215p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
13180 event RequiredHookSet(address indexed hook);
13181
13182 // ============ Constructor ============
13183 constructor(uint32 _localDomain) {
13184 localDomain = _localDomain;
13185 }
13186
13187 // ============ Initializers ============
13188 function initialize(
13189 address _owner,
13190 address _defaultIsm,
13191 address _defaultHook,
13192 address _requiredHook
13193 ) external initializer {
13194 __Ownable_init();
13195 setDefaultIsm(_defaultIsm);
13196 setDefaultHook(_defaultHook);
13197 setRequiredHook(_requiredHook);
13198 transferOwnership(_owner);
13199 }
13200
13201 // ============ External Functions ============
13202 /**
13203 * @notice Dispatches a message to the destination domain & recipient
13204 * using the default hook and empty metadata.
13205 * @param _destinationDomain Domain of destination chain
13206 * @param _recipientAddress Address of recipient on destination chain as bytes32
13207 * @param _messageBody Raw bytes content of message body
13208 * @return The message ID inserted into the Mailbox's merkle tree
13209 */
13210 function dispatch(
13211 uint32 _destinationDomain,
13212 bytes32 _recipientAddress,
13213 bytes calldata _messageBody
13214 ) external payable override returns (bytes32) {
13215 return
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '9352,9372p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
9352
9353 // ============ Events ============
9354
9355 /// @notice Emitted when a message is received from the external bridge
9356 event ReceivedMessage(bytes32 indexed messageId);
9357
9358 // ============ Initializer ============
9359
9360 function setAuthorizedHook(bytes32 _hook) external initializer {
9361 require(
9362 _hook != bytes32(0),
9363 "AbstractMessageIdAuthorizedIsm: invalid authorized hook"
9364 );
9365 authorizedHook = _hook;
9366 }
9367
9368 // ============ External Functions ============
9369
9370 /**
9371 * @notice Verify a message was received by ISM.
9372 * @param message Message to verify.
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '10146,10162p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
10146 /**
10147 * @param _owner The owner of the contract.
10148 * @param _beneficiary The beneficiary.
10149 */
10150 function initialize(
10151 address _owner,
10152 address _beneficiary
10153 ) public initializer {
10154 __Ownable_init();
10155 _transferOwnership(_owner);
10156 _setBeneficiary(_beneficiary);
10157 }
10158
10159 /**
10160 * @notice Transfers the entire native token balance to the beneficiary.
10161 * @dev The beneficiary must be able to receive native tokens.
10162 */
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '13660,13684p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
13660 * @param _interchainGasPaymaster Unused but required by HyperlaneConnectionClient
13661 * @param _interchainSecurityModule The address of the local ISM contract
13662 * @param _owner The address with owner privileges
13663 */
13664 function initialize(
13665 address _interchainGasPaymaster,
13666 address _interchainSecurityModule,
13667 address _owner
13668 ) external initializer {
13669 _MailboxClient_initialize(
13670 _interchainGasPaymaster,
13671 _interchainSecurityModule,
13672 _owner
13673 );
13674
13675 implementation = address(new OwnableMulticall(address(this)));
13676 // cannot be stored immutably because it is dynamically sized
13677 bytes memory _bytecode = MinimalProxy.bytecode(implementation);
13678 bytecodeHash = keccak256(_bytecode);
13679 }
13680
13681 /**
13682 * @notice Registers the address of remote InterchainAccountRouter
13683 * and ISM contracts to use as a default when making interchain calls
13684 * @param _destination The remote domain
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '14250,14272p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
14250 * @notice Initializes the Router contract with Hyperlane core contracts and the address of the interchain security module.
14251 * @param _interchainGasPaymaster The address of the interchain gas paymaster contract.
14252 * @param _interchainSecurityModule The address of the interchain security module contract.
14253 * @param _owner The address with owner privileges.
14254 */
14255 function initialize(
14256 address _interchainGasPaymaster,
14257 address _interchainSecurityModule,
14258 address _owner
14259 ) external initializer {
14260 _MailboxClient_initialize(
14261 _interchainGasPaymaster,
14262 _interchainSecurityModule,
14263 _owner
14264 );
14265 }
14266
14267 /**
14268 * @notice Dispatches a sequence of static calls (query) to the destination domain and set of callbacks to resolve the results on the dispatcher.
14269 * @param _destination The domain of the chain to query.
14270 * @param _to The address of the contract to query
14271 * @param _data The calldata encoding the query
14272 * @param _callback The calldata of the callback that will be made on the sender.
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '14542,14560p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
14542 * @param _circleMessageTransmitter The Circle MessageTransmitter contract.
14543 * @param _liquidityLayerRouter The LiquidityLayerRouter contract.
14544 */
14545 function initialize(
14546 address _owner,
14547 address _tokenMessenger,
14548 address _circleMessageTransmitter,
14549 address _liquidityLayerRouter
14550 ) external initializer {
14551 __Ownable_init();
14552 _transferOwnership(_owner);
14553
14554 tokenMessenger = ITokenMessenger(_tokenMessenger);
14555 circleMessageTransmitter = ICircleMessageTransmitter(
14556 _circleMessageTransmitter
14557 );
14558 liquidityLayerRouter = _liquidityLayerRouter;
14559 }
14560
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '14860,14878p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
14860 * @param _liquidityLayerRouter The LiquidityLayerRouter contract.
14861 */
14862 function initialize(
14863 address _owner,
14864 address _portalTokenBridge,
14865 address _liquidityLayerRouter
14866 ) public initializer {
14867 // Transfer ownership of the contract to deployer
14868 _transferOwnership(_owner);
14869
14870 portalTokenBridge = IPortalTokenBridge(_portalTokenBridge);
14871 liquidityLayerRouter = _liquidityLayerRouter;
14872 }
14873
14874 /**
14875 * Sends tokens as requested by the router
14876 * @param _destinationDomain The hyperlane domain of the destination
14877 * @param _token The token address
14878 * @param _amount The amount of tokens to send
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '15072,15090p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
15072 * @param _owner The address with owner privileges.
15073 */
15074 function initialize(
15075 address _interchainGasPaymaster,
15076 address _interchainSecurityModule,
15077 address _owner
15078 ) external initializer {
15079 _MailboxClient_initialize(
15080 _interchainGasPaymaster,
15081 _interchainSecurityModule,
15082 _owner
15083 );
15084 }
15085
15086 function dispatchWithTokens(
15087 uint32 _destinationDomain,
15088 bytes32 _recipientAddress,
15089 address _token,
15090 uint256 _amount,
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '16438,16454p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
16438 * @param _name The name of the token.
16439 * @param _symbol The symbol of the token.
16440 */
16441 function initialize(
16442 uint256 _totalSupply,
16443 string memory _name,
16444 string memory _symbol
16445 ) external initializer {
16446 // Initialize ERC20 metadata
16447 __ERC20_init(_name, _symbol);
16448 _mint(msg.sender, _totalSupply);
16449 }
16450
16451 function decimals() public view override returns (uint8) {
16452 return _decimals;
16453 }
16454
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '16990,17010p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
16990 * @param _symbol The symbol of the token.
16991 */
16992 function initialize(
16993 uint256 _mintAmount,
16994 string memory _name,
16995 string memory _symbol
16996 ) external initializer {
16997 address owner = msg.sender;
16998 _transferOwnership(owner);
16999
17000 __ERC721_init(_name, _symbol);
17001 for (uint256 i = 0; i < _mintAmount; i++) {
17002 _safeMint(owner, i);
17003 }
17004 }
17005
17006 function balanceOf(
17007 address _account
17008 )
17009 public
17010 view
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '12608,12638p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
12608
12609 /**
12610 * @param _owner The owner of the contract.
12611 */
12612 function initialize(address _owner) public initializer {
12613 __Ownable_init();
12614 _transferOwnership(_owner);
12615 }
12616
12617 /**
12618 * @notice Sets the ISMs to be used for the specified origin domains
12619 * @param _owner The owner of the contract.
12620 * @param _domains The origin domains
12621 * @param __modules The ISMs to use to verify messages
12622 */
12623 function initialize(
12624 address _owner,
12625 uint32[] calldata _domains,
12626 IInterchainSecurityModule[] calldata __modules
12627 ) public initializer {
12628 __Ownable_init();
12629 require(_domains.length == __modules.length, "length mismatch");
12630 uint256 _length = _domains.length;
12631 for (uint256 i = 0; i < _length; ++i) {
12632 _set(_domains[i], address(__modules[i]));
12633 }
12634 _transferOwnership(_owner);
12635 }
12636
12637 /**
12638 * @notice Sets the ISM to be used for the specified origin domain
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '11184,11208p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
11184
11185 /// @inheritdoc AbstractPostDispatchHook
11186 function _quoteDispatch(
11187 bytes calldata metadata,
11188 bytes calldata message
11189 ) internal view virtual override returns (uint256) {
11190 return _getConfiguredHook(message).quoteDispatch(metadata, message);
11191 }
11192
11193 function _getConfiguredHook(
11194 bytes calldata message
11195 ) internal view virtual returns (IPostDispatchHook hook) {
11196 hook = hooks[message.destination()];
11197 require(
11198 address(hook) != address(0),
11199 string.concat(
11200 "No hook configured for destination: ",
11201 message.destination().toString()
11202 )
11203 );
11204 }
11205 }
11206
11207
11208 // File contracts/hooks/routing/DestinationRecipientRoutingHook.sol
exec
/bin/bash -lc "nl -ba AUDIT_BUNDLE.txt | sed -n '11220,11256p'" in /tmp/codex-3gxliacl
succeeded in 0ms:
11220 @@@@@@@@@ @@@@@@@@@
11221 @@@@@@@@@ @@@@@@@@@
11222 @@@@@@@@@ @@@@@@@@@
11223 @@@@@@@@@ @@@@@@@@*/
11224
11225
11226
11227 contract DestinationRecipientRoutingHook is DomainRoutingHook {
11228 using Message for bytes;
11229
11230 /// @notice destination => recipient =>custom hook
11231 mapping(uint32 => mapping(bytes32 => address)) public customHooks;
11232
11233 constructor(
11234 address mailbox,
11235 address owner
11236 ) DomainRoutingHook(mailbox, owner) {}
11237
11238 function _postDispatch(
11239 bytes calldata metadata,
11240 bytes calldata message
11241 ) internal override {
11242 address customHookPreset = customHooks[message.destination()][
11243 message.recipient()
11244 ];
11245 if (customHookPreset != address(0)) {
11246 IPostDispatchHook(customHookPreset).postDispatch{value: msg.value}(
11247 metadata,
11248 message
11249 );
11250 } else {
11251 super._postDispatch(metadata, message);
11252 }
11253 }
11254
11255 function configCustomHook(
11256 uint32 destinationDomain,
codex
{"no_findings":false,"summary":"Found 3 issues: multiple constructor-deployed contracts can be hijacked through permissionless initializers, mailbox-authenticated hooks can be triggered directly by anyone for the current latest message, and recipient-specific routing quotes do not match execution.","findings":[{"severity":"high","category":"auth","title":"Permissionless initializers let the first caller seize control and mint supply","exploit_scenario":"Several production contracts are deployed with constructors but defer critical setup to unrestricted `initializer` functions. If deployment and initialization are not atomic, an attacker can front-run the intended initializer call, become owner, install malicious hooks/ISMs/bridge endpoints, or mint the full initial token/NFT supply to themselves. For example, hijacking `Mailbox.initialize` gives the attacker control over `defaultIsm/defaultHook/requiredHook`, while hijacking `HypERC20.initialize` or `HypERC721.initialize` mints the initial supply to the attacker.","recommendation":"Do not expose public initialization for constructor-deployed instances. Move critical setup into the constructor or gate initialization to the constructor-established owner with a separate one-time guard. If these are meant to be proxy implementations, disable initializers on the implementation and initialize atomically via proxy constructor calldata.","evidence":"`initialize`/`setAuthorizedHook` are externally callable with only the OZ `initializer` modifier, which enforces one-time execution but does not authenticate the caller. Some of these initializers also mint to `msg.sender` or rewrite owner/configuration.","locations":["AUDIT_BUNDLE.txt:9360 (contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol)","AUDIT_BUNDLE.txt:10150 (contracts/hooks/igp/InterchainGasPaymaster.sol)","AUDIT_BUNDLE.txt:12612 (contracts/isms/routing/DomainRoutingIsm.sol)","AUDIT_BUNDLE.txt:12623 (contracts/isms/routing/DomainRoutingIsm.sol)","AUDIT_BUNDLE.txt:13188 (contracts/Mailbox.sol)","AUDIT_BUNDLE.txt:13664 (contracts/middleware/InterchainAccountRouter.sol)","AUDIT_BUNDLE.txt:14255 (contracts/middleware/InterchainQueryRouter.sol)","AUDIT_BUNDLE.txt:14545 (contracts/middleware/liquidity-layer/adapters/CircleBridgeAdapter.sol)","AUDIT_BUNDLE.txt:14862 (contracts/middleware/liquidity-layer/adapters/PortalAdapter.sol)","AUDIT_BUNDLE.txt:15074 (contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol)","AUDIT_BUNDLE.txt:16441 (contracts/token/HypERC20.sol)","AUDIT_BUNDLE.txt:16992 (contracts/token/HypERC721.sol)"]},{"severity":"high","category":"auth","title":"Anyone can call post-dispatch hooks directly for the current latest message","exploit_scenario":"After any legitimate dispatch, the full message bytes are public in the `Dispatch` event and `latestDispatchedId` stays set until the next dispatch. Because `AbstractPostDispatchHook.postDispatch` is permissionless, an attacker can call `MerkleTreeHook.postDispatch` directly and insert the same message ID into the Merkle tree again, corrupting checkpoint state. The same pattern also lets an attacker directly trigger message-id authorization hooks (`ERC5164Hook`/`OPStackHook`) for the current latest message, even if that message did not actually opt into those hooks through mailbox dispatch.","recommendation":"Require `msg.sender == address(mailbox)` for hooks whose security depends on mailbox state, or override `postDispatch` in those hooks to enforce mailbox-only access. Add single-use guards per message ID where duplicate execution would be unsafe.","evidence":"`AbstractPostDispatchHook.postDispatch` has no caller check. `MerkleTreeHook` and `AbstractMessageIdAuthHook` only verify that the supplied message matches `mailbox.latestDispatchedId()`, which any external caller can satisfy by replaying the just-emitted message bytes.","locations":["AUDIT_BUNDLE.txt:9226 (contracts/hooks/libs/AbstractPostDispatchHook.sol)","AUDIT_BUNDLE.txt:9489 (contracts/hooks/libs/AbstractMessageIdAuthHook.sol)","AUDIT_BUNDLE.txt:10753 (contracts/hooks/MerkleTreeHook.sol)"]},{"severity":"low","category":"other","title":"DestinationRecipientRoutingHook quotes the wrong hook for recipient-specific routes","exploit_scenario":"If the owner configures a `customHooks[destination][recipient]`, dispatch execution will route to that custom hook, but fee quoting still uses the destination-level hook from `DomainRoutingHook`. Users relying on `quoteDispatch` can underpay and have dispatches revert, making recipient-specific routes unusable unless callers manually overpay.","recommendation":"Override `_quoteDispatch` in `DestinationRecipientRoutingHook` so it resolves the same hook that `_postDispatch` will actually call, including recipient-specific overrides.","evidence":"`DestinationRecipientRoutingHook._postDispatch` consults `customHooks[destination][recipient]`, but inherited `DomainRoutingHook._quoteDispatch` only calls `_getConfiguredHook(message)`, which is destination-only and never checks the recipient override map.","locations":["AUDIT_BUNDLE.txt:11186 (contracts/hooks/routing/DomainRoutingHook.sol)","AUDIT_BUNDLE.txt:11238 (contracts/hooks/routing/DestinationRecipientRoutingHook.sol)"]}]}
tokens used
145,563
{
"codex": {
"ran_at": "2026-04-04T03:30:25.678088+00:00",
"returncode": 0
},
"project": {
"mode": "source",
"file_count": 1,
"import_count": 0,
"first_party_contracts_count": 1
},
"summary": "Found 3 issues: multiple constructor-deployed contracts can be hijacked through permissionless initializers, mailbox-authenticated hooks can be triggered directly by anyone for the current latest message, and recipient-specific routing quotes do not match execution.",
"raw_final": {
"summary": "Found 3 issues: multiple constructor-deployed contracts can be hijacked through permissionless initializers, mailbox-authenticated hooks can be triggered directly by anyone for the current latest message, and recipient-specific routing quotes do not match execution.",
"findings": [
{
"title": "Permissionless initializers let the first caller seize control and mint supply",
"category": "auth",
"evidence": "`initialize`/`setAuthorizedHook` are externally callable with only the OZ `initializer` modifier, which enforces one-time execution but does not authenticate the caller. Some of these initializers also mint to `msg.sender` or rewrite owner/configuration.",
"severity": "high",
"locations": [
"AUDIT_BUNDLE.txt:9360 (contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol)",
"AUDIT_BUNDLE.txt:10150 (contracts/hooks/igp/InterchainGasPaymaster.sol)",
"AUDIT_BUNDLE.txt:12612 (contracts/isms/routing/DomainRoutingIsm.sol)",
"AUDIT_BUNDLE.txt:12623 (contracts/isms/routing/DomainRoutingIsm.sol)",
"AUDIT_BUNDLE.txt:13188 (contracts/Mailbox.sol)",
"AUDIT_BUNDLE.txt:13664 (contracts/middleware/InterchainAccountRouter.sol)",
"AUDIT_BUNDLE.txt:14255 (contracts/middleware/InterchainQueryRouter.sol)",
"AUDIT_BUNDLE.txt:14545 (contracts/middleware/liquidity-layer/adapters/CircleBridgeAdapter.sol)",
"AUDIT_BUNDLE.txt:14862 (contracts/middleware/liquidity-layer/adapters/PortalAdapter.sol)",
"AUDIT_BUNDLE.txt:15074 (contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol)",
"AUDIT_BUNDLE.txt:16441 (contracts/token/HypERC20.sol)",
"AUDIT_BUNDLE.txt:16992 (contracts/token/HypERC721.sol)"
],
"recommendation": "Do not expose public initialization for constructor-deployed instances. Move critical setup into the constructor or gate initialization to the constructor-established owner with a separate one-time guard. If these are meant to be proxy implementations, disable initializers on the implementation and initialize atomically via proxy constructor calldata.",
"exploit_scenario": "Several production contracts are deployed with constructors but defer critical setup to unrestricted `initializer` functions. If deployment and initialization are not atomic, an attacker can front-run the intended initializer call, become owner, install malicious hooks/ISMs/bridge endpoints, or mint the full initial token/NFT supply to themselves. For example, hijacking `Mailbox.initialize` gives the attacker control over `defaultIsm/defaultHook/requiredHook`, while hijacking `HypERC20.initialize` or `HypERC721.initialize` mints the initial supply to the attacker."
},
{
"title": "Anyone can call post-dispatch hooks directly for the current latest message",
"category": "auth",
"evidence": "`AbstractPostDispatchHook.postDispatch` has no caller check. `MerkleTreeHook` and `AbstractMessageIdAuthHook` only verify that the supplied message matches `mailbox.latestDispatchedId()`, which any external caller can satisfy by replaying the just-emitted message bytes.",
"severity": "high",
"locations": [
"AUDIT_BUNDLE.txt:9226 (contracts/hooks/libs/AbstractPostDispatchHook.sol)",
"AUDIT_BUNDLE.txt:9489 (contracts/hooks/libs/AbstractMessageIdAuthHook.sol)",
"AUDIT_BUNDLE.txt:10753 (contracts/hooks/MerkleTreeHook.sol)"
],
"recommendation": "Require `msg.sender == address(mailbox)` for hooks whose security depends on mailbox state, or override `postDispatch` in those hooks to enforce mailbox-only access. Add single-use guards per message ID where duplicate execution would be unsafe.",
"exploit_scenario": "After any legitimate dispatch, the full message bytes are public in the `Dispatch` event and `latestDispatchedId` stays set until the next dispatch. Because `AbstractPostDispatchHook.postDispatch` is permissionless, an attacker can call `MerkleTreeHook.postDispatch` directly and insert the same message ID into the Merkle tree again, corrupting checkpoint state. The same pattern also lets an attacker directly trigger message-id authorization hooks (`ERC5164Hook`/`OPStackHook`) for the current latest message, even if that message did not actually opt into those hooks through mailbox dispatch."
},
{
"title": "DestinationRecipientRoutingHook quotes the wrong hook for recipient-specific routes",
"category": "other",
"evidence": "`DestinationRecipientRoutingHook._postDispatch` consults `customHooks[destination][recipient]`, but inherited `DomainRoutingHook._quoteDispatch` only calls `_getConfiguredHook(message)`, which is destination-only and never checks the recipient override map.",
"severity": "low",
"locations": [
"AUDIT_BUNDLE.txt:11186 (contracts/hooks/routing/DomainRoutingHook.sol)",
"AUDIT_BUNDLE.txt:11238 (contracts/hooks/routing/DestinationRecipientRoutingHook.sol)"
],
"recommendation": "Override `_quoteDispatch` in `DestinationRecipientRoutingHook` so it resolves the same hook that `_postDispatch` will actually call, including recipient-specific overrides.",
"exploit_scenario": "If the owner configures a `customHooks[destination][recipient]`, dispatch execution will route to that custom hook, but fee quoting still uses the destination-level hook from `DomainRoutingHook`. Users relying on `quoteDispatch` can underpay and have dispatches revert, making recipient-specific routes unusable unless callers manually overpay."
}
],
"no_findings": false
},
"no_findings": false,
"schema_version": 1
}
00000000: PUSH1 0x80 00000002: PUSH1 0x40 00000004: MSTORE 00000005: PUSH1 0x04 00000007: CALLDATASIZE 00000008: LT 00000009: PUSH2 0x0179 0000000c: JUMPI 0000000d: PUSH1 0x00 0000000f: CALLDATALOAD 00000010: PUSH1 0xe0 00000012: SHR 00000013: DUP1 00000014: PUSH4 0x81b4e8b4 00000019: GT 0000001a: PUSH2 0x00cb 0000001d: JUMPI 0000001e: DUP1 0000001f: PUSH4 0xd5438eae 00000024: GT 00000025: PUSH2 0x007f 00000028: JUMPI 00000029: DUP1 0000002a: PUSH4 0xefae508a 0000002f: GT 00000030: PUSH2 0x0059 00000033: JUMPI 00000034: DUP1 00000035: PUSH4 0xefae508a 0000003a: EQ 0000003b: PUSH2 0x04b5 0000003e: JUMPI 0000003f: DUP1 00000040: PUSH4 0xf2ed8c53 00000045: EQ 00000046: PUSH2 0x04d5 00000049: JUMPI 0000004a: DUP1 0000004b: PUSH4 0xf2fde38b 00000050: EQ 00000051: PUSH2 0x04f5 00000054: JUMPI 00000055: PUSH1 0x00 00000057: DUP1 00000058: REVERT 00000059: JUMPDEST 0000005a: DUP1 0000005b: PUSH4 0xd5438eae 00000060: EQ 00000061: PUSH2 0x0434 00000064: JUMPI 00000065: DUP1 00000066: PUSH4 0xde523cf3 0000006b: EQ 0000006c: PUSH2 0x0468 0000006f: JUMPI 00000070: DUP1 00000071: PUSH4 0xe9198bf9 00000076: EQ 00000077: PUSH2 0x0495 0000007a: JUMPI 0000007b: PUSH1 0x00 0000007d: DUP1 0000007e: REVERT 0000007f: JUMPDEST 00000080: DUP1 00000081: PUSH4 0x8da5cb5b 00000086: GT 00000087: PUSH2 0x00b0 0000008a: JUMPI 0000008b: DUP1 0000008c: PUSH4 0x8da5cb5b 00000091: EQ 00000092: PUSH2 0x03c9 00000095: JUMPI 00000096: DUP1 00000097: PUSH4 0xb1bd6436 0000009c: EQ 0000009d: PUSH2 0x03f4 000000a0: JUMPI 000000a1: DUP1 000000a2: PUSH4 0xb49c53a7 000000a7: EQ 000000a8: PUSH2 0x0414 000000ab: JUMPI 000000ac: PUSH1 0x00 000000ae: DUP1 000000af: REVERT 000000b0: JUMPDEST 000000b1: DUP1 000000b2: PUSH4 0x81b4e8b4 000000b7: EQ 000000b8: PUSH2 0x036d 000000bb: JUMPI 000000bc: DUP1 000000bd: PUSH4 0x8d3638f4 000000c2: EQ 000000c3: PUSH2 0x0380 000000c6: JUMPI 000000c7: PUSH1 0x00 000000c9: DUP1 000000ca: REVERT 000000cb: JUMPDEST 000000cc: DUP1 000000cd: PUSH4 0x56d5d475 000000d2: GT 000000d3: PUSH2 0x012d 000000d6: JUMPI 000000d7: DUP1 000000d8: PUSH4 0x71a15b38 000000dd: GT 000000de: PUSH2 0x0107 000000e1: JUMPI 000000e2: DUP1 000000e3: PUSH4 0x71a15b38 000000e8: EQ 000000e9: PUSH2 0x02ce 000000ec: JUMPI 000000ed: DUP1 000000ee: PUSH4 0x775313a1 000000f3: EQ 000000f4: PUSH2 0x02ee 000000f7: JUMPI 000000f8: DUP1 000000f9: PUSH4 0x7f5a7c7b 000000fe: EQ 000000ff: PUSH2 0x031b 00000102: JUMPI 00000103: PUSH1 0x00 00000105: DUP1 00000106: REVERT 00000107: JUMPDEST 00000108: DUP1 00000109: PUSH4 0x56d5d475 0000010e: EQ 0000010f: PUSH2 0x0271 00000112: JUMPI 00000113: DUP1 00000114: PUSH4 0x70a08231 00000119: EQ 0000011a: PUSH2 0x0284 0000011d: JUMPI 0000011e: DUP1 0000011f: PUSH4 0x715018a6 00000124: EQ 00000125: PUSH2 0x02b9 00000128: JUMPI 00000129: PUSH1 0x00 0000012b: DUP1 0000012c: REVERT 0000012d: JUMPDEST 0000012e: DUP1 0000012f: PUSH4 0x3dfd3873 00000134: GT 00000135: PUSH2 0x015e 00000138: JUMPI 00000139: DUP1 0000013a: PUSH4 0x3dfd3873 0000013f: EQ 00000140: PUSH2 0x020f 00000143: JUMPI 00000144: DUP1 00000145: PUSH4 0x440df4f4 0000014a: EQ 0000014b: PUSH2 0x022f 0000014e: JUMPI 0000014f: DUP1 00000150: PUSH4 0x49d462ef 00000155: EQ 00000156: PUSH2 0x0251 00000159: JUMPI 0000015a: PUSH1 0x00 0000015c: DUP1 0000015d: REVERT 0000015e: JUMPDEST 0000015f: DUP1 00000160: PUSH4 0x0e72cc06 00000165: EQ 00000166: PUSH2 0x01ba 00000169: JUMPI 0000016a: DUP1 0000016b: PUSH4 0x2ead72f6 00000170: EQ 00000171: PUSH2 0x01dc 00000174: JUMPI 00000175: PUSH1 0x00 00000177: DUP1 00000178: REVERT 00000179: JUMPDEST 0000017a: CALLDATASIZE 0000017b: PUSH2 0x01b5 0000017e: JUMPI 0000017f: PUSH1 0x40 00000181: MLOAD 00000182: CALLVALUE 00000183: DUP2 00000184: MSTORE 00000185: CALLER 00000186: SWAP1 00000187: PUSH32 0x5d8bc849764969eb1bcc6d0a2f55999d0167c1ccec240a4f39cf664ca9c4148e 000001a8: SWAP1 000001a9: PUSH1 0x20 000001ab: ADD 000001ac: PUSH1 0x40 000001ae: MLOAD 000001af: DUP1 000001b0: SWAP2 000001b1: SUB 000001b2: SWAP1 000001b3: LOG2 000001b4: STOP 000001b5: JUMPDEST 000001b6: PUSH1 0x00 000001b8: DUP1 000001b9: REVERT 000001ba: JUMPDEST 000001bb: CALLVALUE 000001bc: DUP1 000001bd: ISZERO 000001be: PUSH2 0x01c6 000001c1: JUMPI 000001c2: PUSH1 0x00 000001c4: DUP1 000001c5: REVERT 000001c6: JUMPDEST 000001c7: POP 000001c8: PUSH2 0x01da 000001cb: PUSH2 0x01d5 000001ce: CALLDATASIZE 000001cf: PUSH1 0x04 000001d1: PUSH2 0x1919 000001d4: JUMP 000001d5: JUMPDEST 000001d6: PUSH2 0x0515 000001d9: JUMP 000001da: JUMPDEST 000001db: STOP 000001dc: JUMPDEST 000001dd: CALLVALUE 000001de: DUP1 000001df: ISZERO 000001e0: PUSH2 0x01e8 000001e3: JUMPI 000001e4: PUSH1 0x00 000001e6: DUP1 000001e7: REVERT 000001e8: JUMPDEST 000001e9: POP 000001ea: PUSH2 0x01fc 000001ed: PUSH2 0x01f7 000001f0: CALLDATASIZE 000001f1: PUSH1 0x04 000001f3: PUSH2 0x1968 000001f6: JUMP 000001f7: JUMPDEST 000001f8: PUSH2 0x0630 000001fb: JUMP 000001fc: JUMPDEST 000001fd: PUSH1 0x40 000001ff: MLOAD 00000200: SWAP1 00000201: DUP2 00000202: MSTORE 00000203: PUSH1 0x20 00000205: ADD 00000206: JUMPDEST 00000207: PUSH1 0x40 00000209: MLOAD 0000020a: DUP1 0000020b: SWAP2 0000020c: SUB 0000020d: SWAP1 0000020e: RETURN 0000020f: JUMPDEST 00000210: CALLVALUE 00000211: DUP1 00000212: ISZERO 00000213: PUSH2 0x021b 00000216: JUMPI 00000217: PUSH1 0x00 00000219: DUP1 0000021a: REVERT 0000021b: JUMPDEST 0000021c: POP 0000021d: PUSH2 0x01da 00000220: PUSH2 0x022a 00000223: CALLDATASIZE 00000224: PUSH1 0x04 00000226: PUSH2 0x1919 00000229: JUMP 0000022a: JUMPDEST 0000022b: PUSH2 0x064f 0000022e: JUMP 0000022f: JUMPDEST 00000230: CALLVALUE 00000231: DUP1 00000232: ISZERO 00000233: PUSH2 0x023b 00000236: JUMPI 00000237: PUSH1 0x00 00000239: DUP1 0000023a: REVERT 0000023b: JUMPDEST 0000023c: POP 0000023d: PUSH2 0x0244 00000240: PUSH2 0x0765 00000243: JUMP 00000244: JUMPDEST 00000245: PUSH1 0x40 00000247: MLOAD 00000248: PUSH2 0x0206 0000024b: SWAP2 0000024c: SWAP1 0000024d: PUSH2 0x1983 00000250: JUMP 00000251: JUMPDEST 00000252: CALLVALUE 00000253: DUP1 00000254: ISZERO 00000255: PUSH2 0x025d 00000258: JUMPI 00000259: PUSH1 0x00 0000025b: DUP1 0000025c: REVERT 0000025d: JUMPDEST 0000025e: POP 0000025f: PUSH2 0x01da 00000262: PUSH2 0x026c 00000265: CALLDATASIZE 00000266: PUSH1 0x04 00000268: PUSH2 0x19cd 0000026b: JUMP 0000026c: JUMPDEST 0000026d: PUSH2 0x0776 00000270: JUMP 00000271: JUMPDEST 00000272: PUSH2 0x01da 00000275: PUSH2 0x027f 00000278: CALLDATASIZE 00000279: PUSH1 0x04 0000027b: PUSH2 0x19f7 0000027e: JUMP 0000027f: JUMPDEST 00000280: PUSH2 0x079c 00000283: JUMP 00000284: JUMPDEST 00000285: CALLVALUE 00000286: DUP1 00000287: ISZERO 00000288: PUSH2 0x0290 0000028b: JUMPI 0000028c: PUSH1 0x00 0000028e: DUP1 0000028f: REVERT 00000290: JUMPDEST 00000291: POP 00000292: PUSH2 0x01fc 00000295: PUSH2 0x029f 00000298: CALLDATASIZE 00000299: PUSH1 0x04 0000029b: PUSH2 0x1919 0000029e: JUMP 0000029f: JUMPDEST 000002a0: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 000002b5: AND 000002b6: BALANCE 000002b7: SWAP1 000002b8: JUMP 000002b9: JUMPDEST 000002ba: CALLVALUE 000002bb: DUP1 000002bc: ISZERO 000002bd: PUSH2 0x02c5 000002c0: JUMPI 000002c1: PUSH1 0x00 000002c3: DUP1 000002c4: REVERT 000002c5: JUMPDEST 000002c6: POP 000002c7: PUSH2 0x01da 000002ca: PUSH2 0x0910 000002cd: JUMP 000002ce: JUMPDEST 000002cf: CALLVALUE 000002d0: DUP1 000002d1: ISZERO 000002d2: PUSH2 0x02da 000002d5: JUMPI 000002d6: PUSH1 0x00 000002d8: DUP1 000002d9: REVERT 000002da: JUMPDEST 000002db: POP 000002dc: PUSH2 0x01da 000002df: PUSH2 0x02e9 000002e2: CALLDATASIZE 000002e3: PUSH1 0x04 000002e5: PUSH2 0x1ac3 000002e8: JUMP 000002e9: JUMPDEST 000002ea: PUSH2 0x0924 000002ed: JUMP 000002ee: JUMPDEST 000002ef: CALLVALUE 000002f0: DUP1 000002f1: ISZERO 000002f2: PUSH2 0x02fa 000002f5: JUMPI 000002f6: PUSH1 0x00 000002f8: DUP1 000002f9: REVERT 000002fa: JUMPDEST 000002fb: POP 000002fc: PUSH2 0x01fc 000002ff: PUSH2 0x0309 00000302: CALLDATASIZE 00000303: PUSH1 0x04 00000305: PUSH2 0x1968 00000308: JUMP 00000309: JUMPDEST 0000030a: PUSH1 0xca 0000030c: PUSH1 0x20 0000030e: MSTORE 0000030f: PUSH1 0x00 00000311: SWAP1 00000312: DUP2 00000313: MSTORE 00000314: PUSH1 0x40 00000316: SWAP1 00000317: KECCAK256 00000318: SLOAD 00000319: DUP2 0000031a: JUMP 0000031b: JUMPDEST 0000031c: CALLVALUE 0000031d: DUP1 0000031e: ISZERO 0000031f: PUSH2 0x0327 00000322: JUMPI 00000323: PUSH1 0x00 00000325: DUP1 00000326: REVERT 00000327: JUMPDEST 00000328: POP 00000329: PUSH1 0x65 0000032b: SLOAD 0000032c: PUSH2 0x0348 0000032f: SWAP1 00000330: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000345: AND 00000346: DUP2 00000347: JUMP 00000348: JUMPDEST 00000349: PUSH1 0x40 0000034b: MLOAD 0000034c: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000361: SWAP1 00000362: SWAP2 00000363: AND 00000364: DUP2 00000365: MSTORE 00000366: PUSH1 0x20 00000368: ADD 00000369: PUSH2 0x0206 0000036c: JUMP 0000036d: JUMPDEST 0000036e: PUSH2 0x01fc 00000371: PUSH2 0x037b 00000374: CALLDATASIZE 00000375: PUSH1 0x04 00000377: PUSH2 0x1b05 0000037a: JUMP 0000037b: JUMPDEST 0000037c: PUSH2 0x097f 0000037f: JUMP 00000380: JUMPDEST 00000381: CALLVALUE 00000382: DUP1 00000383: ISZERO 00000384: PUSH2 0x038c 00000387: JUMPI 00000388: PUSH1 0x00 0000038a: DUP1 0000038b: REVERT 0000038c: JUMPDEST 0000038d: POP 0000038e: PUSH2 0x03b4 00000391: PUSH32 0x0000000000000000000000000000000000000000000000000000000000000001 000003b2: DUP2 000003b3: JUMP 000003b4: JUMPDEST 000003b5: PUSH1 0x40 000003b7: MLOAD 000003b8: PUSH4 0xffffffff 000003bd: SWAP1 000003be: SWAP2 000003bf: AND 000003c0: DUP2 000003c1: MSTORE 000003c2: PUSH1 0x20 000003c4: ADD 000003c5: PUSH2 0x0206 000003c8: JUMP 000003c9: JUMPDEST 000003ca: CALLVALUE 000003cb: DUP1 000003cc: ISZERO 000003cd: PUSH2 0x03d5 000003d0: JUMPI 000003d1: PUSH1 0x00 000003d3: DUP1 000003d4: REVERT 000003d5: JUMPDEST 000003d6: POP 000003d7: PUSH1 0x33 000003d9: SLOAD 000003da: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 000003ef: AND 000003f0: PUSH2 0x0348 000003f3: JUMP 000003f4: JUMPDEST 000003f5: CALLVALUE 000003f6: DUP1 000003f7: ISZERO 000003f8: PUSH2 0x0400 000003fb: JUMPI 000003fc: PUSH1 0x00 000003fe: DUP1 000003ff: REVERT 00000400: JUMPDEST 00000401: POP 00000402: PUSH2 0x01da 00000405: PUSH2 0x040f 00000408: CALLDATASIZE 00000409: PUSH1 0x04 0000040b: PUSH2 0x1b38 0000040e: JUMP 0000040f: JUMPDEST 00000410: PUSH2 0x0a0e 00000413: JUMP 00000414: JUMPDEST 00000415: CALLVALUE 00000416: DUP1 00000417: ISZERO 00000418: PUSH2 0x0420 0000041b: JUMPI 0000041c: PUSH1 0x00 0000041e: DUP1 0000041f: REVERT 00000420: JUMPDEST 00000421: POP 00000422: PUSH2 0x01da 00000425: PUSH2 0x042f 00000428: CALLDATASIZE 00000429: PUSH1 0x04 0000042b: PUSH2 0x19cd 0000042e: JUMP 0000042f: JUMPDEST 00000430: PUSH2 0x0a98 00000433: JUMP 00000434: JUMPDEST 00000435: CALLVALUE 00000436: DUP1 00000437: ISZERO 00000438: PUSH2 0x0440 0000043b: JUMPI 0000043c: PUSH1 0x00 0000043e: DUP1 0000043f: REVERT 00000440: JUMPDEST 00000441: POP 00000442: PUSH2 0x0348 00000445: PUSH32 0x000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239 00000466: DUP2 00000467: JUMP 00000468: JUMPDEST 00000469: CALLVALUE 0000046a: DUP1 0000046b: ISZERO 0000046c: PUSH2 0x0474 0000046f: JUMPI 00000470: PUSH1 0x00 00000472: DUP1 00000473: REVERT 00000474: JUMPDEST 00000475: POP 00000476: PUSH1 0x66 00000478: SLOAD 00000479: PUSH2 0x0348 0000047c: SWAP1 0000047d: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000492: AND 00000493: DUP2 00000494: JUMP 00000495: JUMPDEST 00000496: CALLVALUE 00000497: DUP1 00000498: ISZERO 00000499: PUSH2 0x04a1 0000049c: JUMPI 0000049d: PUSH1 0x00 0000049f: DUP1 000004a0: REVERT 000004a1: JUMPDEST 000004a2: POP 000004a3: PUSH2 0x01da 000004a6: PUSH2 0x04b0 000004a9: CALLDATASIZE 000004aa: PUSH1 0x04 000004ac: PUSH2 0x1bad 000004af: JUMP 000004b0: JUMPDEST 000004b1: PUSH2 0x0aaa 000004b4: JUMP 000004b5: JUMPDEST 000004b6: CALLVALUE 000004b7: DUP1 000004b8: ISZERO 000004b9: PUSH2 0x04c1 000004bc: JUMPI 000004bd: PUSH1 0x00 000004bf: DUP1 000004c0: REVERT 000004c1: JUMPDEST 000004c2: POP 000004c3: PUSH2 0x01da 000004c6: PUSH2 0x04d0 000004c9: CALLDATASIZE 000004ca: PUSH1 0x04 000004cc: PUSH2 0x1968 000004cf: JUMP 000004d0: JUMPDEST 000004d1: PUSH2 0x0b89 000004d4: JUMP 000004d5: JUMPDEST 000004d6: CALLVALUE 000004d7: DUP1 000004d8: ISZERO 000004d9: PUSH2 0x04e1 000004dc: JUMPI 000004dd: PUSH1 0x00 000004df: DUP1 000004e0: REVERT 000004e1: JUMPDEST 000004e2: POP 000004e3: PUSH2 0x01fc 000004e6: PUSH2 0x04f0 000004e9: CALLDATASIZE 000004ea: PUSH1 0x04 000004ec: PUSH2 0x1968 000004ef: JUMP 000004f0: JUMPDEST 000004f1: PUSH2 0x0b9d 000004f4: JUMP 000004f5: JUMPDEST 000004f6: CALLVALUE 000004f7: DUP1 000004f8: ISZERO 000004f9: PUSH2 0x0501 000004fc: JUMPI 000004fd: PUSH1 0x00 000004ff: DUP1 00000500: REVERT 00000501: JUMPDEST 00000502: POP 00000503: PUSH2 0x01da 00000506: PUSH2 0x0510 00000509: CALLDATASIZE 0000050a: PUSH1 0x04 0000050c: PUSH2 0x1919 0000050f: JUMP 00000510: JUMPDEST 00000511: PUSH2 0x0bbe 00000514: JUMP 00000515: JUMPDEST 00000516: DUP1 00000517: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000052c: DUP2 0000052d: AND 0000052e: EXTCODESIZE 0000052f: ISZERO 00000530: ISZERO 00000531: DUP1 00000532: PUSH2 0x054f 00000535: JUMPI 00000536: POP 00000537: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000054c: DUP2 0000054d: AND 0000054e: ISZERO 0000054f: JUMPDEST 00000550: PUSH2 0x05e0 00000553: JUMPI 00000554: PUSH1 0x40 00000556: MLOAD 00000557: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 00000578: DUP2 00000579: MSTORE 0000057a: PUSH1 0x20 0000057c: PUSH1 0x04 0000057e: DUP3 0000057f: ADD 00000580: MSTORE 00000581: PUSH1 0x27 00000583: PUSH1 0x24 00000585: DUP3 00000586: ADD 00000587: MSTORE 00000588: PUSH32 0x4d61696c626f78436c69656e743a20696e76616c696420636f6e747261637420 000005a9: PUSH1 0x44 000005ab: DUP3 000005ac: ADD 000005ad: MSTORE 000005ae: PUSH32 0x73657474696e6700000000000000000000000000000000000000000000000000 000005cf: PUSH1 0x64 000005d1: DUP3 000005d2: ADD 000005d3: MSTORE 000005d4: PUSH1 0x84 000005d6: ADD 000005d7: JUMPDEST 000005d8: PUSH1 0x40 000005da: MLOAD 000005db: DUP1 000005dc: SWAP2 000005dd: SUB 000005de: SWAP1 000005df: REVERT 000005e0: JUMPDEST 000005e1: PUSH2 0x05e8 000005e4: PUSH2 0x0c72 000005e7: JUMP 000005e8: JUMPDEST 000005e9: POP 000005ea: PUSH1 0x66 000005ec: DUP1 000005ed: SLOAD 000005ee: PUSH32 0xffffffffffffffffffffffff0000000000000000000000000000000000000000 0000060f: AND 00000610: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000625: SWAP3 00000626: SWAP1 00000627: SWAP3 00000628: AND 00000629: SWAP2 0000062a: SWAP1 0000062b: SWAP2 0000062c: OR 0000062d: SWAP1 0000062e: SSTORE 0000062f: JUMP 00000630: JUMPDEST 00000631: PUSH1 0x00 00000633: DUP1 00000634: PUSH2 0x0647 00000637: PUSH1 0x97 00000639: PUSH4 0xffffffff 0000063e: DUP1 0000063f: DUP7 00000640: AND 00000641: SWAP1 00000642: PUSH2 0x0cf3 00000645: AND 00000646: JUMP 00000647: JUMPDEST 00000648: SWAP5 00000649: SWAP4 0000064a: POP 0000064b: POP 0000064c: POP 0000064d: POP 0000064e: JUMP 0000064f: JUMPDEST 00000650: DUP1 00000651: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000666: DUP2 00000667: AND 00000668: EXTCODESIZE 00000669: ISZERO 0000066a: ISZERO 0000066b: DUP1 0000066c: PUSH2 0x0689 0000066f: JUMPI 00000670: POP 00000671: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000686: DUP2 00000687: AND 00000688: ISZERO 00000689: JUMPDEST 0000068a: PUSH2 0x0715 0000068d: JUMPI 0000068e: PUSH1 0x40 00000690: MLOAD 00000691: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 000006b2: DUP2 000006b3: MSTORE 000006b4: PUSH1 0x20 000006b6: PUSH1 0x04 000006b8: DUP3 000006b9: ADD 000006ba: MSTORE 000006bb: PUSH1 0x27 000006bd: PUSH1 0x24 000006bf: DUP3 000006c0: ADD 000006c1: MSTORE 000006c2: PUSH32 0x4d61696c626f78436c69656e743a20696e76616c696420636f6e747261637420 000006e3: PUSH1 0x44 000006e5: DUP3 000006e6: ADD 000006e7: MSTORE 000006e8: PUSH32 0x73657474696e6700000000000000000000000000000000000000000000000000 00000709: PUSH1 0x64 0000070b: DUP3 0000070c: ADD 0000070d: MSTORE 0000070e: PUSH1 0x84 00000710: ADD 00000711: PUSH2 0x05d7 00000714: JUMP 00000715: JUMPDEST 00000716: PUSH2 0x071d 00000719: PUSH2 0x0c72 0000071c: JUMP 0000071d: JUMPDEST 0000071e: POP 0000071f: PUSH1 0x65 00000721: DUP1 00000722: SLOAD 00000723: PUSH32 0xffffffffffffffffffffffff0000000000000000000000000000000000000000 00000744: AND 00000745: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000075a: SWAP3 0000075b: SWAP1 0000075c: SWAP3 0000075d: AND 0000075e: SWAP2 0000075f: SWAP1 00000760: SWAP2 00000761: OR 00000762: SWAP1 00000763: SSTORE 00000764: JUMP 00000765: JUMPDEST 00000766: PUSH1 0x60 00000768: PUSH2 0x0771 0000076b: PUSH1 0x97 0000076d: PUSH2 0x0d0c 00000770: JUMP 00000771: JUMPDEST 00000772: SWAP1 00000773: POP 00000774: SWAP1 00000775: JUMP 00000776: JUMPDEST 00000777: PUSH2 0x077e 0000077a: PUSH2 0x0c72 0000077d: JUMP 0000077e: JUMPDEST 0000077f: PUSH4 0xffffffff 00000784: SWAP2 00000785: SWAP1 00000786: SWAP2 00000787: AND 00000788: PUSH1 0x00 0000078a: SWAP1 0000078b: DUP2 0000078c: MSTORE 0000078d: PUSH1 0xca 0000078f: PUSH1 0x20 00000791: MSTORE 00000792: PUSH1 0x40 00000794: SWAP1 00000795: KECCAK256 00000796: SSTORE 00000797: JUMP 00000798: JUMPDEST 00000799: POP 0000079a: POP 0000079b: JUMP 0000079c: JUMPDEST 0000079d: CALLER 0000079e: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 000007b3: PUSH32 0x000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239 000007d4: AND 000007d5: EQ 000007d6: PUSH2 0x0861 000007d9: JUMPI 000007da: PUSH1 0x40 000007dc: MLOAD 000007dd: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 000007fe: DUP2 000007ff: MSTORE 00000800: PUSH1 0x20 00000802: PUSH1 0x04 00000804: DUP3 00000805: ADD 00000806: MSTORE 00000807: PUSH1 0x21 00000809: PUSH1 0x24 0000080b: DUP3 0000080c: ADD 0000080d: MSTORE 0000080e: PUSH32 0x4d61696c626f78436c69656e743a2073656e646572206e6f74206d61696c626f 0000082f: PUSH1 0x44 00000831: DUP3 00000832: ADD 00000833: MSTORE 00000834: PUSH32 0x7800000000000000000000000000000000000000000000000000000000000000 00000855: PUSH1 0x64 00000857: DUP3 00000858: ADD 00000859: MSTORE 0000085a: PUSH1 0x84 0000085c: ADD 0000085d: PUSH2 0x05d7 00000860: JUMP 00000861: JUMPDEST 00000862: PUSH1 0x00 00000864: PUSH2 0x086c 00000867: DUP6 00000868: PUSH2 0x0dc7 0000086b: JUMP 0000086c: JUMPDEST 0000086d: SWAP1 0000086e: POP 0000086f: DUP4 00000870: DUP2 00000871: EQ 00000872: PUSH2 0x08fd 00000875: JUMPI 00000876: PUSH1 0x40 00000878: MLOAD 00000879: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 0000089a: DUP2 0000089b: MSTORE 0000089c: PUSH1 0x20 0000089e: PUSH1 0x04 000008a0: DUP3 000008a1: ADD 000008a2: MSTORE 000008a3: PUSH1 0x25 000008a5: PUSH1 0x24 000008a7: DUP3 000008a8: ADD 000008a9: MSTORE 000008aa: PUSH32 0x456e726f6c6c656420726f7574657220646f6573206e6f74206d617463682073 000008cb: PUSH1 0x44 000008cd: DUP3 000008ce: ADD 000008cf: MSTORE 000008d0: PUSH32 0x656e646572000000000000000000000000000000000000000000000000000000 000008f1: PUSH1 0x64 000008f3: DUP3 000008f4: ADD 000008f5: MSTORE 000008f6: PUSH1 0x84 000008f8: ADD 000008f9: PUSH2 0x05d7 000008fc: JUMP 000008fd: JUMPDEST 000008fe: PUSH2 0x0909 00000901: DUP6 00000902: DUP6 00000903: DUP6 00000904: DUP6 00000905: PUSH2 0x0e2d 00000908: JUMP 00000909: JUMPDEST 0000090a: POP 0000090b: POP 0000090c: POP 0000090d: POP 0000090e: POP 0000090f: JUMP 00000910: JUMPDEST 00000911: PUSH2 0x0918 00000914: PUSH2 0x0c72 00000917: JUMP 00000918: JUMPDEST 00000919: PUSH2 0x0922 0000091c: PUSH1 0x00 0000091e: PUSH2 0x0eb7 00000921: JUMP 00000922: JUMPDEST 00000923: JUMP 00000924: JUMPDEST 00000925: PUSH2 0x092c 00000928: PUSH2 0x0c72 0000092b: JUMP 0000092c: JUMPDEST 0000092d: DUP1 0000092e: PUSH1 0x00 00000930: JUMPDEST 00000931: DUP2 00000932: DUP2 00000933: LT 00000934: ISZERO 00000935: PUSH2 0x0979 00000938: JUMPI 00000939: PUSH2 0x0967 0000093c: DUP5 0000093d: DUP5 0000093e: DUP4 0000093f: DUP2 00000940: DUP2 00000941: LT 00000942: PUSH2 0x094d 00000945: JUMPI 00000946: PUSH2 0x094d 00000949: PUSH2 0x1c19 0000094c: JUMP 0000094d: JUMPDEST 0000094e: SWAP1 0000094f: POP 00000950: PUSH1 0x20 00000952: MUL 00000953: ADD 00000954: PUSH1 0x20 00000956: DUP2 00000957: ADD 00000958: SWAP1 00000959: PUSH2 0x0962 0000095c: SWAP2 0000095d: SWAP1 0000095e: PUSH2 0x1968 00000961: JUMP 00000962: JUMPDEST 00000963: PUSH2 0x0f2e 00000966: JUMP 00000967: JUMPDEST 00000968: PUSH2 0x0972 0000096b: PUSH1 0x01 0000096d: DUP3 0000096e: PUSH2 0x1c77 00000971: JUMP 00000972: JUMPDEST 00000973: SWAP1 00000974: POP 00000975: PUSH2 0x0930 00000978: JUMP 00000979: JUMPDEST 0000097a: POP 0000097b: POP 0000097c: POP 0000097d: POP 0000097e: JUMP 0000097f: JUMPDEST 00000980: PUSH1 0x00 00000982: DUP2 00000983: CALLVALUE 00000984: LT 00000985: ISZERO 00000986: PUSH2 0x09eb 00000989: JUMPI 0000098a: PUSH1 0x40 0000098c: MLOAD 0000098d: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 000009ae: DUP2 000009af: MSTORE 000009b0: PUSH1 0x20 000009b2: PUSH1 0x04 000009b4: DUP3 000009b5: ADD 000009b6: DUP2 000009b7: SWAP1 000009b8: MSTORE 000009b9: PUSH1 0x24 000009bb: DUP3 000009bc: ADD 000009bd: MSTORE 000009be: PUSH32 0x4e61746976653a20616d6f756e742065786365656473206d73672e76616c7565 000009df: PUSH1 0x44 000009e1: DUP3 000009e2: ADD 000009e3: MSTORE 000009e4: PUSH1 0x64 000009e6: ADD 000009e7: PUSH2 0x05d7 000009ea: JUMP 000009eb: JUMPDEST 000009ec: PUSH1 0x00 000009ee: PUSH2 0x09f7 000009f1: DUP4 000009f2: CALLVALUE 000009f3: PUSH2 0x1c8a 000009f6: JUMP 000009f7: JUMPDEST 000009f8: SWAP1 000009f9: POP 000009fa: PUSH2 0x0a05 000009fd: DUP6 000009fe: DUP6 000009ff: DUP6 00000a00: DUP5 00000a01: PUSH2 0x0f83 00000a04: JUMP 00000a05: JUMPDEST 00000a06: SWAP6 00000a07: SWAP5 00000a08: POP 00000a09: POP 00000a0a: POP 00000a0b: POP 00000a0c: POP 00000a0d: JUMP 00000a0e: JUMPDEST 00000a0f: PUSH2 0x0a16 00000a12: PUSH2 0x0c72 00000a15: JUMP 00000a16: JUMPDEST 00000a17: PUSH1 0x00 00000a19: JUMPDEST 00000a1a: DUP2 00000a1b: DUP2 00000a1c: LT 00000a1d: ISZERO 00000a1e: PUSH2 0x0a93 00000a21: JUMPI 00000a22: PUSH2 0x0a81 00000a25: DUP4 00000a26: DUP4 00000a27: DUP4 00000a28: DUP2 00000a29: DUP2 00000a2a: LT 00000a2b: PUSH2 0x0a36 00000a2e: JUMPI 00000a2f: PUSH2 0x0a36 00000a32: PUSH2 0x1c19 00000a35: JUMP 00000a36: JUMPDEST 00000a37: PUSH2 0x0a4c 00000a3a: SWAP3 00000a3b: PUSH1 0x20 00000a3d: PUSH1 0x40 00000a3f: SWAP1 00000a40: SWAP3 00000a41: MUL 00000a42: ADD 00000a43: SWAP1 00000a44: DUP2 00000a45: ADD 00000a46: SWAP2 00000a47: POP 00000a48: PUSH2 0x1968 00000a4b: JUMP 00000a4c: JUMPDEST 00000a4d: DUP5 00000a4e: DUP5 00000a4f: DUP5 00000a50: DUP2 00000a51: DUP2 00000a52: LT 00000a53: PUSH2 0x0a5e 00000a56: JUMPI 00000a57: PUSH2 0x0a5e 00000a5a: PUSH2 0x1c19 00000a5d: JUMP 00000a5e: JUMPDEST 00000a5f: SWAP1 00000a60: POP 00000a61: PUSH1 0x40 00000a63: MUL 00000a64: ADD 00000a65: PUSH1 0x20 00000a67: ADD 00000a68: CALLDATALOAD 00000a69: PUSH4 0xffffffff 00000a6e: SWAP1 00000a6f: SWAP2 00000a70: AND 00000a71: PUSH1 0x00 00000a73: SWAP1 00000a74: DUP2 00000a75: MSTORE 00000a76: PUSH1 0xca 00000a78: PUSH1 0x20 00000a7a: MSTORE 00000a7b: PUSH1 0x40 00000a7d: SWAP1 00000a7e: KECCAK256 00000a7f: SSTORE 00000a80: JUMP 00000a81: JUMPDEST 00000a82: PUSH2 0x0a8c 00000a85: PUSH1 0x01 00000a87: DUP3 00000a88: PUSH2 0x1c77 00000a8b: JUMP 00000a8c: JUMPDEST 00000a8d: SWAP1 00000a8e: POP 00000a8f: PUSH2 0x0a19 00000a92: JUMP 00000a93: JUMPDEST 00000a94: POP 00000a95: POP 00000a96: POP 00000a97: JUMP 00000a98: JUMPDEST 00000a99: PUSH2 0x0aa0 00000a9c: PUSH2 0x0c72 00000a9f: JUMP 00000aa0: JUMPDEST 00000aa1: PUSH2 0x0798 00000aa4: DUP3 00000aa5: DUP3 00000aa6: PUSH2 0x0ffe 00000aa9: JUMP 00000aaa: JUMPDEST 00000aab: PUSH2 0x0ab2 00000aae: PUSH2 0x0c72 00000ab1: JUMP 00000ab2: JUMPDEST 00000ab3: DUP3 00000ab4: DUP2 00000ab5: EQ 00000ab6: PUSH2 0x0b1b 00000ab9: JUMPI 00000aba: PUSH1 0x40 00000abc: MLOAD 00000abd: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 00000ade: DUP2 00000adf: MSTORE 00000ae0: PUSH1 0x20 00000ae2: PUSH1 0x04 00000ae4: DUP3 00000ae5: ADD 00000ae6: MSTORE 00000ae7: PUSH1 0x07 00000ae9: PUSH1 0x24 00000aeb: DUP3 00000aec: ADD 00000aed: MSTORE 00000aee: PUSH32 0x216c656e67746800000000000000000000000000000000000000000000000000 00000b0f: PUSH1 0x44 00000b11: DUP3 00000b12: ADD 00000b13: MSTORE 00000b14: PUSH1 0x64 00000b16: ADD 00000b17: PUSH2 0x05d7 00000b1a: JUMP 00000b1b: JUMPDEST 00000b1c: DUP3 00000b1d: PUSH1 0x00 00000b1f: JUMPDEST 00000b20: DUP2 00000b21: DUP2 00000b22: LT 00000b23: ISZERO 00000b24: PUSH2 0x0b81 00000b27: JUMPI 00000b28: PUSH2 0x0b6f 00000b2b: DUP7 00000b2c: DUP7 00000b2d: DUP4 00000b2e: DUP2 00000b2f: DUP2 00000b30: LT 00000b31: PUSH2 0x0b3c 00000b34: JUMPI 00000b35: PUSH2 0x0b3c 00000b38: PUSH2 0x1c19 00000b3b: JUMP 00000b3c: JUMPDEST 00000b3d: SWAP1 00000b3e: POP 00000b3f: PUSH1 0x20 00000b41: MUL 00000b42: ADD 00000b43: PUSH1 0x20 00000b45: DUP2 00000b46: ADD 00000b47: SWAP1 00000b48: PUSH2 0x0b51 00000b4b: SWAP2 00000b4c: SWAP1 00000b4d: PUSH2 0x1968 00000b50: JUMP 00000b51: JUMPDEST 00000b52: DUP6 00000b53: DUP6 00000b54: DUP5 00000b55: DUP2 00000b56: DUP2 00000b57: LT 00000b58: PUSH2 0x0b63 00000b5b: JUMPI 00000b5c: PUSH2 0x0b63 00000b5f: PUSH2 0x1c19 00000b62: JUMP 00000b63: JUMPDEST 00000b64: SWAP1 00000b65: POP 00000b66: PUSH1 0x20 00000b68: MUL 00000b69: ADD 00000b6a: CALLDATALOAD 00000b6b: PUSH2 0x0ffe 00000b6e: JUMP 00000b6f: JUMPDEST 00000b70: PUSH2 0x0b7a 00000b73: PUSH1 0x01 00000b75: DUP3 00000b76: PUSH2 0x1c77 00000b79: JUMP 00000b7a: JUMPDEST 00000b7b: SWAP1 00000b7c: POP 00000b7d: PUSH2 0x0b1f 00000b80: JUMP 00000b81: JUMPDEST 00000b82: POP 00000b83: POP 00000b84: POP 00000b85: POP 00000b86: POP 00000b87: POP 00000b88: JUMP 00000b89: JUMPDEST 00000b8a: PUSH2 0x0b91 00000b8d: PUSH2 0x0c72 00000b90: JUMP 00000b91: JUMPDEST 00000b92: PUSH2 0x0b9a 00000b95: DUP2 00000b96: PUSH2 0x0f2e 00000b99: JUMP 00000b9a: JUMPDEST 00000b9b: POP 00000b9c: JUMP 00000b9d: JUMPDEST 00000b9e: PUSH1 0x00 00000ba0: PUSH2 0x0bb8 00000ba3: DUP3 00000ba4: PUSH1 0x40 00000ba6: MLOAD 00000ba7: DUP1 00000ba8: PUSH1 0x20 00000baa: ADD 00000bab: PUSH1 0x40 00000bad: MSTORE 00000bae: DUP1 00000baf: PUSH1 0x00 00000bb1: DUP2 00000bb2: MSTORE 00000bb3: POP 00000bb4: PUSH2 0x1014 00000bb7: JUMP 00000bb8: JUMPDEST 00000bb9: SWAP3 00000bba: SWAP2 00000bbb: POP 00000bbc: POP 00000bbd: JUMP 00000bbe: JUMPDEST 00000bbf: PUSH2 0x0bc6 00000bc2: PUSH2 0x0c72 00000bc5: JUMP 00000bc6: JUMPDEST 00000bc7: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000bdc: DUP2 00000bdd: AND 00000bde: PUSH2 0x0c69 00000be1: JUMPI 00000be2: PUSH1 0x40 00000be4: MLOAD 00000be5: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 00000c06: DUP2 00000c07: MSTORE 00000c08: PUSH1 0x20 00000c0a: PUSH1 0x04 00000c0c: DUP3 00000c0d: ADD 00000c0e: MSTORE 00000c0f: PUSH1 0x26 00000c11: PUSH1 0x24 00000c13: DUP3 00000c14: ADD 00000c15: MSTORE 00000c16: PUSH32 0x4f776e61626c653a206e6577206f776e657220697320746865207a65726f2061 00000c37: PUSH1 0x44 00000c39: DUP3 00000c3a: ADD 00000c3b: MSTORE 00000c3c: PUSH32 0x6464726573730000000000000000000000000000000000000000000000000000 00000c5d: PUSH1 0x64 00000c5f: DUP3 00000c60: ADD 00000c61: MSTORE 00000c62: PUSH1 0x84 00000c64: ADD 00000c65: PUSH2 0x05d7 00000c68: JUMP 00000c69: JUMPDEST 00000c6a: PUSH2 0x0b9a 00000c6d: DUP2 00000c6e: PUSH2 0x0eb7 00000c71: JUMP 00000c72: JUMPDEST 00000c73: PUSH1 0x33 00000c75: SLOAD 00000c76: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000c8b: AND 00000c8c: CALLER 00000c8d: EQ 00000c8e: PUSH2 0x0922 00000c91: JUMPI 00000c92: PUSH1 0x40 00000c94: MLOAD 00000c95: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 00000cb6: DUP2 00000cb7: MSTORE 00000cb8: PUSH1 0x20 00000cba: PUSH1 0x04 00000cbc: DUP3 00000cbd: ADD 00000cbe: DUP2 00000cbf: SWAP1 00000cc0: MSTORE 00000cc1: PUSH1 0x24 00000cc3: DUP3 00000cc4: ADD 00000cc5: MSTORE 00000cc6: PUSH32 0x4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572 00000ce7: PUSH1 0x44 00000ce9: DUP3 00000cea: ADD 00000ceb: MSTORE 00000cec: PUSH1 0x64 00000cee: ADD 00000cef: PUSH2 0x05d7 00000cf2: JUMP 00000cf3: JUMPDEST 00000cf4: PUSH1 0x00 00000cf6: DUP1 00000cf7: PUSH2 0x0d00 00000cfa: DUP5 00000cfb: DUP5 00000cfc: PUSH2 0x102d 00000cff: JUMP 00000d00: JUMPDEST 00000d01: SWAP2 00000d02: POP 00000d03: SWAP2 00000d04: POP 00000d05: JUMPDEST 00000d06: SWAP3 00000d07: POP 00000d08: SWAP3 00000d09: SWAP1 00000d0a: POP 00000d0b: JUMP 00000d0c: JUMPDEST 00000d0d: PUSH1 0x60 00000d0f: PUSH1 0x00 00000d11: PUSH2 0x0d19 00000d14: DUP4 00000d15: PUSH2 0x1067 00000d18: JUMP 00000d19: JUMPDEST 00000d1a: SWAP1 00000d1b: POP 00000d1c: DUP1 00000d1d: MLOAD 00000d1e: PUSH8 0xffffffffffffffff 00000d27: DUP2 00000d28: GT 00000d29: ISZERO 00000d2a: PUSH2 0x0d35 00000d2d: JUMPI 00000d2e: PUSH2 0x0d35 00000d31: PUSH2 0x1c9d 00000d34: JUMP 00000d35: JUMPDEST 00000d36: PUSH1 0x40 00000d38: MLOAD 00000d39: SWAP1 00000d3a: DUP1 00000d3b: DUP3 00000d3c: MSTORE 00000d3d: DUP1 00000d3e: PUSH1 0x20 00000d40: MUL 00000d41: PUSH1 0x20 00000d43: ADD 00000d44: DUP3 00000d45: ADD 00000d46: PUSH1 0x40 00000d48: MSTORE 00000d49: DUP1 00000d4a: ISZERO 00000d4b: PUSH2 0x0d5e 00000d4e: JUMPI 00000d4f: DUP2 00000d50: PUSH1 0x20 00000d52: ADD 00000d53: PUSH1 0x20 00000d55: DUP3 00000d56: MUL 00000d57: DUP1 00000d58: CALLDATASIZE 00000d59: DUP4 00000d5a: CALLDATACOPY 00000d5b: ADD 00000d5c: SWAP1 00000d5d: POP 00000d5e: JUMPDEST 00000d5f: POP 00000d60: SWAP2 00000d61: POP 00000d62: PUSH1 0x00 00000d64: JUMPDEST 00000d65: DUP2 00000d66: MLOAD 00000d67: DUP2 00000d68: LT 00000d69: ISZERO 00000d6a: PUSH2 0x0dc0 00000d6d: JUMPI 00000d6e: DUP2 00000d6f: DUP2 00000d70: DUP2 00000d71: MLOAD 00000d72: DUP2 00000d73: LT 00000d74: PUSH2 0x0d7f 00000d77: JUMPI 00000d78: PUSH2 0x0d7f 00000d7b: PUSH2 0x1c19 00000d7e: JUMP 00000d7f: JUMPDEST 00000d80: PUSH1 0x20 00000d82: MUL 00000d83: PUSH1 0x20 00000d85: ADD 00000d86: ADD 00000d87: MLOAD 00000d88: DUP4 00000d89: DUP3 00000d8a: DUP2 00000d8b: MLOAD 00000d8c: DUP2 00000d8d: LT 00000d8e: PUSH2 0x0d99 00000d91: JUMPI 00000d92: PUSH2 0x0d99 00000d95: PUSH2 0x1c19 00000d98: JUMP 00000d99: JUMPDEST 00000d9a: PUSH4 0xffffffff 00000d9f: SWAP1 00000da0: SWAP3 00000da1: AND 00000da2: PUSH1 0x20 00000da4: SWAP3 00000da5: DUP4 00000da6: MUL 00000da7: SWAP2 00000da8: SWAP1 00000da9: SWAP2 00000daa: ADD 00000dab: SWAP1 00000dac: SWAP2 00000dad: ADD 00000dae: MSTORE 00000daf: DUP1 00000db0: PUSH2 0x0db8 00000db3: DUP2 00000db4: PUSH2 0x1ccc 00000db7: JUMP 00000db8: JUMPDEST 00000db9: SWAP2 00000dba: POP 00000dbb: POP 00000dbc: PUSH2 0x0d64 00000dbf: JUMP 00000dc0: JUMPDEST 00000dc1: POP 00000dc2: POP 00000dc3: SWAP2 00000dc4: SWAP1 00000dc5: POP 00000dc6: JUMP 00000dc7: JUMPDEST 00000dc8: PUSH1 0x00 00000dca: DUP1 00000dcb: DUP1 00000dcc: PUSH2 0x0ddf 00000dcf: PUSH1 0x97 00000dd1: PUSH4 0xffffffff 00000dd6: DUP1 00000dd7: DUP8 00000dd8: AND 00000dd9: SWAP1 00000dda: PUSH2 0x0cf3 00000ddd: AND 00000dde: JUMP 00000ddf: JUMPDEST 00000de0: SWAP2 00000de1: POP 00000de2: SWAP2 00000de3: POP 00000de4: DUP2 00000de5: PUSH2 0x0ded 00000de8: DUP6 00000de9: PUSH2 0x1102 00000dec: JUMP 00000ded: JUMPDEST 00000dee: SWAP1 00000def: PUSH2 0x0e25 00000df2: JUMPI 00000df3: PUSH1 0x40 00000df5: MLOAD 00000df6: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 00000e17: DUP2 00000e18: MSTORE 00000e19: PUSH1 0x04 00000e1b: ADD 00000e1c: PUSH2 0x05d7 00000e1f: SWAP2 00000e20: SWAP1 00000e21: PUSH2 0x1d72 00000e24: JUMP 00000e25: JUMPDEST 00000e26: POP 00000e27: SWAP4 00000e28: SWAP3 00000e29: POP 00000e2a: POP 00000e2b: POP 00000e2c: JUMP 00000e2d: JUMPDEST 00000e2e: PUSH1 0x00 00000e30: PUSH2 0x0e39 00000e33: DUP4 00000e34: DUP4 00000e35: PUSH2 0x1139 00000e38: JUMP 00000e39: JUMPDEST 00000e3a: SWAP1 00000e3b: POP 00000e3c: PUSH1 0x00 00000e3e: PUSH2 0x0e47 00000e41: DUP5 00000e42: DUP5 00000e43: PUSH2 0x1158 00000e46: JUMP 00000e47: JUMPDEST 00000e48: SWAP1 00000e49: POP 00000e4a: CALLDATASIZE 00000e4b: PUSH1 0x00 00000e4d: PUSH2 0x0e56 00000e50: DUP7 00000e51: DUP7 00000e52: PUSH2 0x1168 00000e55: JUMP 00000e56: JUMPDEST 00000e57: SWAP2 00000e58: POP 00000e59: SWAP2 00000e5a: POP 00000e5b: PUSH2 0x0e6c 00000e5e: PUSH2 0x0e64 00000e61: DUP6 00000e62: SWAP1 00000e63: JUMP 00000e64: JUMPDEST 00000e65: DUP5 00000e66: DUP5 00000e67: DUP5 00000e68: PUSH2 0x1178 00000e6b: JUMP 00000e6c: JUMPDEST 00000e6d: DUP4 00000e6e: DUP9 00000e6f: PUSH4 0xffffffff 00000e74: AND 00000e75: PUSH32 0xba20947a325f450d232530e5f5fce293e7963499d5309a07cee84a269f2f15a6 00000e96: DUP6 00000e97: PUSH1 0x40 00000e99: MLOAD 00000e9a: PUSH2 0x0ea5 00000e9d: SWAP2 00000e9e: DUP2 00000e9f: MSTORE 00000ea0: PUSH1 0x20 00000ea2: ADD 00000ea3: SWAP1 00000ea4: JUMP 00000ea5: JUMPDEST 00000ea6: PUSH1 0x40 00000ea8: MLOAD 00000ea9: DUP1 00000eaa: SWAP2 00000eab: SUB 00000eac: SWAP1 00000ead: LOG3 00000eae: POP 00000eaf: POP 00000eb0: POP 00000eb1: POP 00000eb2: POP 00000eb3: POP 00000eb4: POP 00000eb5: POP 00000eb6: JUMP 00000eb7: JUMPDEST 00000eb8: PUSH1 0x33 00000eba: DUP1 00000ebb: SLOAD 00000ebc: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000ed1: DUP4 00000ed2: DUP2 00000ed3: AND 00000ed4: PUSH32 0xffffffffffffffffffffffff0000000000000000000000000000000000000000 00000ef5: DUP4 00000ef6: AND 00000ef7: DUP2 00000ef8: OR 00000ef9: SWAP1 00000efa: SWAP4 00000efb: SSTORE 00000efc: PUSH1 0x40 00000efe: MLOAD 00000eff: SWAP2 00000f00: AND 00000f01: SWAP2 00000f02: SWAP1 00000f03: DUP3 00000f04: SWAP1 00000f05: PUSH32 0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0 00000f26: SWAP1 00000f27: PUSH1 0x00 00000f29: SWAP1 00000f2a: LOG3 00000f2b: POP 00000f2c: POP 00000f2d: JUMP 00000f2e: JUMPDEST 00000f2f: PUSH2 0x0f42 00000f32: PUSH1 0x97 00000f34: PUSH4 0xffffffff 00000f39: DUP1 00000f3a: DUP5 00000f3b: AND 00000f3c: SWAP1 00000f3d: PUSH2 0x1182 00000f40: AND 00000f41: JUMP 00000f42: JUMPDEST 00000f43: PUSH2 0x0f4b 00000f46: DUP3 00000f47: PUSH2 0x1102 00000f4a: JUMP 00000f4b: JUMPDEST 00000f4c: SWAP1 00000f4d: PUSH2 0x0798 00000f50: JUMPI 00000f51: PUSH1 0x40 00000f53: MLOAD 00000f54: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 00000f75: DUP2 00000f76: MSTORE 00000f77: PUSH1 0x04 00000f79: ADD 00000f7a: PUSH2 0x05d7 00000f7d: SWAP2 00000f7e: SWAP1 00000f7f: PUSH2 0x1d72 00000f82: JUMP 00000f83: JUMPDEST 00000f84: PUSH1 0x00 00000f86: DUP1 00000f87: PUSH2 0x0f9b 00000f8a: PUSH1 0x40 00000f8c: DUP1 00000f8d: MLOAD 00000f8e: PUSH1 0x20 00000f90: DUP2 00000f91: ADD 00000f92: SWAP1 00000f93: SWAP2 00000f94: MSTORE 00000f95: PUSH1 0x00 00000f97: DUP2 00000f98: MSTORE 00000f99: SWAP1 00000f9a: JUMP 00000f9b: JUMPDEST 00000f9c: SWAP1 00000f9d: POP 00000f9e: PUSH2 0x0fb2 00000fa1: DUP7 00000fa2: DUP5 00000fa3: PUSH2 0x0fad 00000fa6: DUP9 00000fa7: DUP9 00000fa8: DUP7 00000fa9: PUSH2 0x118e 00000fac: JUMP 00000fad: JUMPDEST 00000fae: PUSH2 0x11bd 00000fb1: JUMP 00000fb2: JUMPDEST 00000fb3: SWAP2 00000fb4: POP 00000fb5: DUP5 00000fb6: DUP7 00000fb7: PUSH4 0xffffffff 00000fbc: AND 00000fbd: PUSH32 0xd229aacb94204188fe8042965fa6b269c62dc5818b21238779ab64bdd17efeec 00000fde: DUP7 00000fdf: PUSH1 0x40 00000fe1: MLOAD 00000fe2: PUSH2 0x0fed 00000fe5: SWAP2 00000fe6: DUP2 00000fe7: MSTORE 00000fe8: PUSH1 0x20 00000fea: ADD 00000feb: SWAP1 00000fec: JUMP 00000fed: JUMPDEST 00000fee: PUSH1 0x40 00000ff0: MLOAD 00000ff1: DUP1 00000ff2: SWAP2 00000ff3: SUB 00000ff4: SWAP1 00000ff5: LOG3 00000ff6: POP 00000ff7: SWAP5 00000ff8: SWAP4 00000ff9: POP 00000ffa: POP 00000ffb: POP 00000ffc: POP 00000ffd: JUMP 00000ffe: JUMPDEST 00000fff: PUSH2 0x0798 00001002: PUSH1 0x97 00001004: PUSH4 0xffffffff 00001009: DUP1 0000100a: DUP6 0000100b: AND 0000100c: SWAP1 0000100d: DUP5 0000100e: SWAP1 0000100f: PUSH2 0x11d7 00001012: AND 00001013: JUMP 00001014: JUMPDEST 00001015: PUSH1 0x00 00001017: DUP1 00001018: PUSH2 0x1020 0000101b: DUP5 0000101c: PUSH2 0x0dc7 0000101f: JUMP 00001020: JUMPDEST 00001021: SWAP1 00001022: POP 00001023: PUSH2 0x0647 00001026: DUP5 00001027: DUP3 00001028: DUP6 00001029: PUSH2 0x11e2 0000102c: JUMP 0000102d: JUMPDEST 0000102e: PUSH1 0x00 00001030: DUP2 00001031: DUP2 00001032: MSTORE 00001033: PUSH1 0x02 00001035: DUP4 00001036: ADD 00001037: PUSH1 0x20 00001039: MSTORE 0000103a: PUSH1 0x40 0000103c: DUP2 0000103d: KECCAK256 0000103e: SLOAD 0000103f: DUP2 00001040: SWAP1 00001041: DUP1 00001042: PUSH2 0x105c 00001045: JUMPI 00001046: PUSH2 0x104f 00001049: DUP6 0000104a: DUP6 0000104b: PUSH2 0x12c3 0000104e: JUMP 0000104f: JUMPDEST 00001050: SWAP3 00001051: POP 00001052: PUSH1 0x00 00001054: SWAP2 00001055: POP 00001056: PUSH2 0x0d05 00001059: SWAP1 0000105a: POP 0000105b: JUMP 0000105c: JUMPDEST 0000105d: PUSH1 0x01 0000105f: SWAP3 00001060: POP 00001061: SWAP1 00001062: POP 00001063: PUSH2 0x0d05 00001066: JUMP 00001067: JUMPDEST 00001068: PUSH1 0x60 0000106a: PUSH1 0x00 0000106c: PUSH2 0x1074 0000106f: DUP4 00001070: PUSH2 0x12cf 00001073: JUMP 00001074: JUMPDEST 00001075: SWAP1 00001076: POP 00001077: DUP1 00001078: PUSH8 0xffffffffffffffff 00001081: DUP2 00001082: GT 00001083: ISZERO 00001084: PUSH2 0x108f 00001087: JUMPI 00001088: PUSH2 0x108f 0000108b: PUSH2 0x1c9d 0000108e: JUMP 0000108f: JUMPDEST 00001090: PUSH1 0x40 00001092: MLOAD 00001093: SWAP1 00001094: DUP1 00001095: DUP3 00001096: MSTORE 00001097: DUP1 00001098: PUSH1 0x20 0000109a: MUL 0000109b: PUSH1 0x20 0000109d: ADD 0000109e: DUP3 0000109f: ADD 000010a0: PUSH1 0x40 000010a2: MSTORE 000010a3: DUP1 000010a4: ISZERO 000010a5: PUSH2 0x10b8 000010a8: JUMPI 000010a9: DUP2 000010aa: PUSH1 0x20 000010ac: ADD 000010ad: PUSH1 0x20 000010af: DUP3 000010b0: MUL 000010b1: DUP1 000010b2: CALLDATASIZE 000010b3: DUP4 000010b4: CALLDATACOPY 000010b5: ADD 000010b6: SWAP1 000010b7: POP 000010b8: JUMPDEST 000010b9: POP 000010ba: SWAP2 000010bb: POP 000010bc: PUSH1 0x00 000010be: JUMPDEST 000010bf: DUP2 000010c0: DUP2 000010c1: LT 000010c2: ISZERO 000010c3: PUSH2 0x0dc0 000010c6: JUMPI 000010c7: PUSH2 0x10d0 000010ca: DUP5 000010cb: DUP3 000010cc: PUSH2 0x12da 000010cf: JUMP 000010d0: JUMPDEST 000010d1: PUSH1 0x00 000010d3: SHR 000010d4: DUP4 000010d5: DUP3 000010d6: DUP2 000010d7: MLOAD 000010d8: DUP2 000010d9: LT 000010da: PUSH2 0x10e5 000010dd: JUMPI 000010de: PUSH2 0x10e5 000010e1: PUSH2 0x1c19 000010e4: JUMP 000010e5: JUMPDEST 000010e6: PUSH1 0x20 000010e8: SWAP1 000010e9: DUP2 000010ea: MUL 000010eb: SWAP2 000010ec: SWAP1 000010ed: SWAP2 000010ee: ADD 000010ef: ADD 000010f0: MSTORE 000010f1: DUP1 000010f2: PUSH2 0x10fa 000010f5: DUP2 000010f6: PUSH2 0x1ccc 000010f9: JUMP 000010fa: JUMPDEST 000010fb: SWAP2 000010fc: POP 000010fd: POP 000010fe: PUSH2 0x10be 00001101: JUMP 00001102: JUMPDEST 00001103: PUSH1 0x60 00001105: PUSH2 0x1113 00001108: DUP3 00001109: PUSH4 0xffffffff 0000110e: AND 0000110f: PUSH2 0x12e6 00001112: JUMP 00001113: JUMPDEST 00001114: PUSH1 0x40 00001116: MLOAD 00001117: PUSH1 0x20 00001119: ADD 0000111a: PUSH2 0x1123 0000111d: SWAP2 0000111e: SWAP1 0000111f: PUSH2 0x1d85 00001122: JUMP 00001123: JUMPDEST 00001124: PUSH1 0x40 00001126: MLOAD 00001127: PUSH1 0x20 00001129: DUP2 0000112a: DUP4 0000112b: SUB 0000112c: SUB 0000112d: DUP2 0000112e: MSTORE 0000112f: SWAP1 00001130: PUSH1 0x40 00001132: MSTORE 00001133: SWAP1 00001134: POP 00001135: SWAP2 00001136: SWAP1 00001137: POP 00001138: JUMP 00001139: JUMPDEST 0000113a: PUSH1 0x00 0000113c: PUSH2 0x1148 0000113f: PUSH1 0x20 00001141: DUP3 00001142: DUP5 00001143: DUP7 00001144: PUSH2 0x1dca 00001147: JUMP 00001148: JUMPDEST 00001149: PUSH2 0x1151 0000114c: SWAP2 0000114d: PUSH2 0x1df4 00001150: JUMP 00001151: JUMPDEST 00001152: SWAP4 00001153: SWAP3 00001154: POP 00001155: POP 00001156: POP 00001157: JUMP 00001158: JUMPDEST 00001159: PUSH1 0x00 0000115b: PUSH2 0x1148 0000115e: PUSH1 0x40 00001160: PUSH1 0x20 00001162: DUP5 00001163: DUP7 00001164: PUSH2 0x1dca 00001167: JUMP 00001168: JUMPDEST 00001169: CALLDATASIZE 0000116a: PUSH1 0x00 0000116c: PUSH2 0x0d00 0000116f: DUP4 00001170: PUSH1 0x40 00001172: DUP2 00001173: DUP8 00001174: PUSH2 0x1dca 00001177: JUMP 00001178: JUMPDEST 00001179: PUSH2 0x0979 0000117c: DUP5 0000117d: DUP5 0000117e: PUSH2 0x13a4 00001181: JUMP 00001182: JUMPDEST 00001183: PUSH1 0x00 00001185: PUSH2 0x1151 00001188: DUP4 00001189: DUP4 0000118a: PUSH2 0x14fe 0000118d: JUMP 0000118e: JUMPDEST 0000118f: PUSH1 0x60 00001191: DUP4 00001192: DUP4 00001193: DUP4 00001194: PUSH1 0x40 00001196: MLOAD 00001197: PUSH1 0x20 00001199: ADD 0000119a: PUSH2 0x11a5 0000119d: SWAP4 0000119e: SWAP3 0000119f: SWAP2 000011a0: SWAP1 000011a1: PUSH2 0x1e30 000011a4: JUMP 000011a5: JUMPDEST 000011a6: PUSH1 0x40 000011a8: MLOAD 000011a9: PUSH1 0x20 000011ab: DUP2 000011ac: DUP4 000011ad: SUB 000011ae: SUB 000011af: DUP2 000011b0: MSTORE 000011b1: SWAP1 000011b2: PUSH1 0x40 000011b4: MSTORE 000011b5: SWAP1 000011b6: POP 000011b7: SWAP4 000011b8: SWAP3 000011b9: POP 000011ba: POP 000011bb: POP 000011bc: JUMP 000011bd: JUMPDEST 000011be: PUSH1 0x00 000011c0: DUP1 000011c1: PUSH2 0x11c9 000011c4: DUP6 000011c5: PUSH2 0x0dc7 000011c8: JUMP 000011c9: JUMPDEST 000011ca: SWAP1 000011cb: POP 000011cc: PUSH2 0x0a05 000011cf: DUP6 000011d0: DUP3 000011d1: DUP7 000011d2: DUP7 000011d3: PUSH2 0x151b 000011d6: JUMP 000011d7: JUMPDEST 000011d8: PUSH2 0x0979 000011db: DUP4 000011dc: DUP4 000011dd: DUP4 000011de: PUSH2 0x15ff 000011e1: JUMP 000011e2: JUMPDEST 000011e3: PUSH1 0x00 000011e5: PUSH32 0x000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239 00001206: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000121b: AND 0000121c: PUSH4 0x81d2ea95 00001221: DUP6 00001222: DUP6 00001223: DUP6 00001224: PUSH2 0x122c 00001227: DUP10 00001228: PUSH2 0x161c 0000122b: JUMP 0000122c: JUMPDEST 0000122d: PUSH1 0x65 0000122f: SLOAD 00001230: PUSH1 0x40 00001232: MLOAD 00001233: PUSH32 0xffffffff00000000000000000000000000000000000000000000000000000000 00001254: PUSH1 0xe0 00001256: DUP9 00001257: SWAP1 00001258: SHL 00001259: AND 0000125a: DUP2 0000125b: MSTORE 0000125c: PUSH2 0x1282 0000125f: SWAP6 00001260: SWAP5 00001261: SWAP4 00001262: SWAP3 00001263: SWAP2 00001264: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00001279: AND 0000127a: SWAP1 0000127b: PUSH1 0x04 0000127d: ADD 0000127e: PUSH2 0x1e5d 00001281: JUMP 00001282: JUMPDEST 00001283: PUSH1 0x20 00001285: PUSH1 0x40 00001287: MLOAD 00001288: DUP1 00001289: DUP4 0000128a: SUB 0000128b: DUP2 0000128c: DUP7 0000128d: GAS 0000128e: STATICCALL 0000128f: ISZERO 00001290: DUP1 00001291: ISZERO 00001292: PUSH2 0x129f 00001295: JUMPI 00001296: RETURNDATASIZE 00001297: PUSH1 0x00 00001299: DUP1 0000129a: RETURNDATACOPY 0000129b: RETURNDATASIZE 0000129c: PUSH1 0x00 0000129e: REVERT 0000129f: JUMPDEST 000012a0: POP 000012a1: POP 000012a2: POP 000012a3: POP 000012a4: PUSH1 0x40 000012a6: MLOAD 000012a7: RETURNDATASIZE 000012a8: PUSH1 0x1f 000012aa: NOT 000012ab: PUSH1 0x1f 000012ad: DUP3 000012ae: ADD 000012af: AND 000012b0: DUP3 000012b1: ADD 000012b2: DUP1 000012b3: PUSH1 0x40 000012b5: MSTORE 000012b6: POP 000012b7: DUP2 000012b8: ADD 000012b9: SWAP1 000012ba: PUSH2 0x0647 000012bd: SWAP2 000012be: SWAP1 000012bf: PUSH2 0x1ebd 000012c2: JUMP 000012c3: JUMPDEST 000012c4: PUSH1 0x00 000012c6: PUSH2 0x1151 000012c9: DUP4 000012ca: DUP4 000012cb: PUSH2 0x163e 000012ce: JUMP 000012cf: JUMPDEST 000012d0: PUSH1 0x00 000012d2: PUSH2 0x0bb8 000012d5: DUP3 000012d6: PUSH2 0x1656 000012d9: JUMP 000012da: JUMPDEST 000012db: PUSH1 0x00 000012dd: PUSH2 0x1151 000012e0: DUP4 000012e1: DUP4 000012e2: PUSH2 0x1660 000012e5: JUMP 000012e6: JUMPDEST 000012e7: PUSH1 0x60 000012e9: PUSH1 0x00 000012eb: PUSH2 0x12f3 000012ee: DUP4 000012ef: PUSH2 0x168a 000012f2: JUMP 000012f3: JUMPDEST 000012f4: PUSH1 0x01 000012f6: ADD 000012f7: SWAP1 000012f8: POP 000012f9: PUSH1 0x00 000012fb: DUP2 000012fc: PUSH8 0xffffffffffffffff 00001305: DUP2 00001306: GT 00001307: ISZERO 00001308: PUSH2 0x1313 0000130b: JUMPI 0000130c: PUSH2 0x1313 0000130f: PUSH2 0x1c9d 00001312: JUMP 00001313: JUMPDEST 00001314: PUSH1 0x40 00001316: MLOAD 00001317: SWAP1 00001318: DUP1 00001319: DUP3 0000131a: MSTORE 0000131b: DUP1 0000131c: PUSH1 0x1f 0000131e: ADD 0000131f: PUSH1 0x1f 00001321: NOT 00001322: AND 00001323: PUSH1 0x20 00001325: ADD 00001326: DUP3 00001327: ADD 00001328: PUSH1 0x40 0000132a: MSTORE 0000132b: DUP1 0000132c: ISZERO 0000132d: PUSH2 0x133d 00001330: JUMPI 00001331: PUSH1 0x20 00001333: DUP3 00001334: ADD 00001335: DUP2 00001336: DUP1 00001337: CALLDATASIZE 00001338: DUP4 00001339: CALLDATACOPY 0000133a: ADD 0000133b: SWAP1 0000133c: POP 0000133d: JUMPDEST 0000133e: POP 0000133f: SWAP1 00001340: POP 00001341: DUP2 00001342: DUP2 00001343: ADD 00001344: PUSH1 0x20 00001346: ADD 00001347: JUMPDEST 00001348: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 00001369: ADD 0000136a: PUSH32 0x3031323334353637383961626364656600000000000000000000000000000000 0000138b: PUSH1 0x0a 0000138d: DUP7 0000138e: MOD 0000138f: BYTE 00001390: DUP2 00001391: MSTORE8 00001392: PUSH1 0x0a 00001394: DUP6 00001395: DIV 00001396: SWAP5 00001397: POP 00001398: DUP5 00001399: PUSH2 0x1347 0000139c: JUMPI 0000139d: POP 0000139e: SWAP4 0000139f: SWAP3 000013a0: POP 000013a1: POP 000013a2: POP 000013a3: JUMP 000013a4: JUMPDEST 000013a5: DUP1 000013a6: SELFBALANCE 000013a7: LT 000013a8: ISZERO 000013a9: PUSH2 0x140e 000013ac: JUMPI 000013ad: PUSH1 0x40 000013af: MLOAD 000013b0: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 000013d1: DUP2 000013d2: MSTORE 000013d3: PUSH1 0x20 000013d5: PUSH1 0x04 000013d7: DUP3 000013d8: ADD 000013d9: MSTORE 000013da: PUSH1 0x1d 000013dc: PUSH1 0x24 000013de: DUP3 000013df: ADD 000013e0: MSTORE 000013e1: PUSH32 0x416464726573733a20696e73756666696369656e742062616c616e6365000000 00001402: PUSH1 0x44 00001404: DUP3 00001405: ADD 00001406: MSTORE 00001407: PUSH1 0x64 00001409: ADD 0000140a: PUSH2 0x05d7 0000140d: JUMP 0000140e: JUMPDEST 0000140f: PUSH1 0x00 00001411: DUP3 00001412: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00001427: AND 00001428: DUP3 00001429: PUSH1 0x40 0000142b: MLOAD 0000142c: PUSH1 0x00 0000142e: PUSH1 0x40 00001430: MLOAD 00001431: DUP1 00001432: DUP4 00001433: SUB 00001434: DUP2 00001435: DUP6 00001436: DUP8 00001437: GAS 00001438: CALL 00001439: SWAP3 0000143a: POP 0000143b: POP 0000143c: POP 0000143d: RETURNDATASIZE 0000143e: DUP1 0000143f: PUSH1 0x00 00001441: DUP2 00001442: EQ 00001443: PUSH2 0x1468 00001446: JUMPI 00001447: PUSH1 0x40 00001449: MLOAD 0000144a: SWAP2 0000144b: POP 0000144c: PUSH1 0x1f 0000144e: NOT 0000144f: PUSH1 0x3f 00001451: RETURNDATASIZE 00001452: ADD 00001453: AND 00001454: DUP3 00001455: ADD 00001456: PUSH1 0x40 00001458: MSTORE 00001459: RETURNDATASIZE 0000145a: DUP3 0000145b: MSTORE 0000145c: RETURNDATASIZE 0000145d: PUSH1 0x00 0000145f: PUSH1 0x20 00001461: DUP5 00001462: ADD 00001463: RETURNDATACOPY 00001464: PUSH2 0x146d 00001467: JUMP 00001468: JUMPDEST 00001469: PUSH1 0x60 0000146b: SWAP2 0000146c: POP 0000146d: JUMPDEST 0000146e: POP 0000146f: POP 00001470: SWAP1 00001471: POP 00001472: DUP1 00001473: PUSH2 0x0a93 00001476: JUMPI 00001477: PUSH1 0x40 00001479: MLOAD 0000147a: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 0000149b: DUP2 0000149c: MSTORE 0000149d: PUSH1 0x20 0000149f: PUSH1 0x04 000014a1: DUP3 000014a2: ADD 000014a3: MSTORE 000014a4: PUSH1 0x3a 000014a6: PUSH1 0x24 000014a8: DUP3 000014a9: ADD 000014aa: MSTORE 000014ab: PUSH32 0x416464726573733a20756e61626c6520746f2073656e642076616c75652c2072 000014cc: PUSH1 0x44 000014ce: DUP3 000014cf: ADD 000014d0: MSTORE 000014d1: PUSH32 0x6563697069656e74206d61792068617665207265766572746564000000000000 000014f2: PUSH1 0x64 000014f4: DUP3 000014f5: ADD 000014f6: MSTORE 000014f7: PUSH1 0x84 000014f9: ADD 000014fa: PUSH2 0x05d7 000014fd: JUMP 000014fe: JUMPDEST 000014ff: PUSH1 0x00 00001501: DUP2 00001502: DUP2 00001503: MSTORE 00001504: PUSH1 0x02 00001506: DUP4 00001507: ADD 00001508: PUSH1 0x20 0000150a: MSTORE 0000150b: PUSH1 0x40 0000150d: DUP2 0000150e: KECCAK256 0000150f: DUP2 00001510: SWAP1 00001511: SSTORE 00001512: PUSH2 0x1151 00001515: DUP4 00001516: DUP4 00001517: PUSH2 0x176c 0000151a: JUMP 0000151b: JUMPDEST 0000151c: PUSH1 0x00 0000151e: PUSH32 0x000000000000000000000000c005dc82818d67af737725bd4bf75435d065d239 0000153f: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00001554: AND 00001555: PUSH4 0x10b83dc0 0000155a: DUP5 0000155b: DUP8 0000155c: DUP8 0000155d: DUP7 0000155e: PUSH2 0x1566 00001561: DUP12 00001562: PUSH2 0x161c 00001565: JUMP 00001566: JUMPDEST 00001567: PUSH1 0x65 00001569: SLOAD 0000156a: PUSH1 0x40 0000156c: MLOAD 0000156d: PUSH32 0xffffffff00000000000000000000000000000000000000000000000000000000 0000158e: PUSH1 0xe0 00001590: DUP10 00001591: SWAP1 00001592: SHL 00001593: AND 00001594: DUP2 00001595: MSTORE 00001596: PUSH2 0x15bc 00001599: SWAP6 0000159a: SWAP5 0000159b: SWAP4 0000159c: SWAP3 0000159d: SWAP2 0000159e: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 000015b3: AND 000015b4: SWAP1 000015b5: PUSH1 0x04 000015b7: ADD 000015b8: PUSH2 0x1e5d 000015bb: JUMP 000015bc: JUMPDEST 000015bd: PUSH1 0x20 000015bf: PUSH1 0x40 000015c1: MLOAD 000015c2: DUP1 000015c3: DUP4 000015c4: SUB 000015c5: DUP2 000015c6: DUP6 000015c7: DUP9 000015c8: GAS 000015c9: CALL 000015ca: ISZERO 000015cb: DUP1 000015cc: ISZERO 000015cd: PUSH2 0x15da 000015d0: JUMPI 000015d1: RETURNDATASIZE 000015d2: PUSH1 0x00 000015d4: DUP1 000015d5: RETURNDATACOPY 000015d6: RETURNDATASIZE 000015d7: PUSH1 0x00 000015d9: REVERT 000015da: JUMPDEST 000015db: POP 000015dc: POP 000015dd: POP 000015de: POP 000015df: POP 000015e0: PUSH1 0x40 000015e2: MLOAD 000015e3: RETURNDATASIZE 000015e4: PUSH1 0x1f 000015e6: NOT 000015e7: PUSH1 0x1f 000015e9: DUP3 000015ea: ADD 000015eb: AND 000015ec: DUP3 000015ed: ADD 000015ee: DUP1 000015ef: PUSH1 0x40 000015f1: MSTORE 000015f2: POP 000015f3: DUP2 000015f4: ADD 000015f5: SWAP1 000015f6: PUSH2 0x0a05 000015f9: SWAP2 000015fa: SWAP1 000015fb: PUSH2 0x1ebd 000015fe: JUMP 000015ff: JUMPDEST 00001600: PUSH1 0x00 00001602: DUP3 00001603: DUP2 00001604: MSTORE 00001605: PUSH1 0x02 00001607: DUP5 00001608: ADD 00001609: PUSH1 0x20 0000160b: MSTORE 0000160c: PUSH1 0x40 0000160e: DUP2 0000160f: KECCAK256 00001610: DUP3 00001611: SWAP1 00001612: SSTORE 00001613: PUSH2 0x0647 00001616: DUP5 00001617: DUP5 00001618: PUSH2 0x1778 0000161b: JUMP 0000161c: JUMPDEST 0000161d: PUSH4 0xffffffff 00001622: DUP2 00001623: AND 00001624: PUSH1 0x00 00001626: SWAP1 00001627: DUP2 00001628: MSTORE 00001629: PUSH1 0xca 0000162b: PUSH1 0x20 0000162d: MSTORE 0000162e: PUSH1 0x40 00001630: SWAP1 00001631: KECCAK256 00001632: SLOAD 00001633: PUSH1 0x60 00001635: SWAP1 00001636: PUSH2 0x0bb8 00001639: SWAP1 0000163a: PUSH2 0x1784 0000163d: JUMP 0000163e: JUMPDEST 0000163f: PUSH1 0x00 00001641: DUP2 00001642: DUP2 00001643: MSTORE 00001644: PUSH1 0x01 00001646: DUP4 00001647: ADD 00001648: PUSH1 0x20 0000164a: MSTORE 0000164b: PUSH1 0x40 0000164d: DUP2 0000164e: KECCAK256 0000164f: SLOAD 00001650: ISZERO 00001651: ISZERO 00001652: PUSH2 0x1151 00001655: JUMP 00001656: JUMPDEST 00001657: PUSH1 0x00 00001659: PUSH2 0x0bb8 0000165c: DUP3 0000165d: SLOAD 0000165e: SWAP1 0000165f: JUMP 00001660: JUMPDEST 00001661: PUSH1 0x00 00001663: DUP3 00001664: PUSH1 0x00 00001666: ADD 00001667: DUP3 00001668: DUP2 00001669: SLOAD 0000166a: DUP2 0000166b: LT 0000166c: PUSH2 0x1677 0000166f: JUMPI 00001670: PUSH2 0x1677 00001673: PUSH2 0x1c19 00001676: JUMP 00001677: JUMPDEST 00001678: SWAP1 00001679: PUSH1 0x00 0000167b: MSTORE 0000167c: PUSH1 0x20 0000167e: PUSH1 0x00 00001680: KECCAK256 00001681: ADD 00001682: SLOAD 00001683: SWAP1 00001684: POP 00001685: SWAP3 00001686: SWAP2 00001687: POP 00001688: POP 00001689: JUMP 0000168a: JUMPDEST 0000168b: PUSH1 0x00 0000168d: DUP1 0000168e: PUSH27 0x184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000 000016aa: DUP4 000016ab: LT 000016ac: PUSH2 0x16d3 000016af: JUMPI 000016b0: PUSH27 0x184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000 000016cc: DUP4 000016cd: DIV 000016ce: SWAP3 000016cf: POP 000016d0: PUSH1 0x40 000016d2: ADD 000016d3: JUMPDEST 000016d4: PUSH14 0x04ee2d6d415b85acef8100000000 000016e3: DUP4 000016e4: LT 000016e5: PUSH2 0x16ff 000016e8: JUMPI 000016e9: PUSH14 0x04ee2d6d415b85acef8100000000 000016f8: DUP4 000016f9: DIV 000016fa: SWAP3 000016fb: POP 000016fc: PUSH1 0x20 000016fe: ADD 000016ff: JUMPDEST 00001700: PUSH7 0x2386f26fc10000 00001708: DUP4 00001709: LT 0000170a: PUSH2 0x171d 0000170d: JUMPI 0000170e: PUSH7 0x2386f26fc10000 00001716: DUP4 00001717: DIV 00001718: SWAP3 00001719: POP 0000171a: PUSH1 0x10 0000171c: ADD 0000171d: JUMPDEST 0000171e: PUSH4 0x05f5e100 00001723: DUP4 00001724: LT 00001725: PUSH2 0x1735 00001728: JUMPI 00001729: PUSH4 0x05f5e100 0000172e: DUP4 0000172f: DIV 00001730: SWAP3 00001731: POP 00001732: PUSH1 0x08 00001734: ADD 00001735: JUMPDEST 00001736: PUSH2 0x2710 00001739: DUP4 0000173a: LT 0000173b: PUSH2 0x1749 0000173e: JUMPI 0000173f: PUSH2 0x2710 00001742: DUP4 00001743: DIV 00001744: SWAP3 00001745: POP 00001746: PUSH1 0x04 00001748: ADD 00001749: JUMPDEST 0000174a: PUSH1 0x64 0000174c: DUP4 0000174d: LT 0000174e: PUSH2 0x175b 00001751: JUMPI 00001752: PUSH1 0x64 00001754: DUP4 00001755: DIV 00001756: SWAP3 00001757: POP 00001758: PUSH1 0x02 0000175a: ADD 0000175b: JUMPDEST 0000175c: PUSH1 0x0a 0000175e: DUP4 0000175f: LT 00001760: PUSH2 0x0bb8 00001763: JUMPI 00001764: PUSH1 0x01 00001766: ADD 00001767: SWAP3 00001768: SWAP2 00001769: POP 0000176a: POP 0000176b: JUMP 0000176c: JUMPDEST 0000176d: PUSH1 0x00 0000176f: PUSH2 0x1151 00001772: DUP4 00001773: DUP4 00001774: PUSH2 0x17a2 00001777: JUMP 00001778: JUMPDEST 00001779: PUSH1 0x00 0000177b: PUSH2 0x1151 0000177e: DUP4 0000177f: DUP4 00001780: PUSH2 0x1895 00001783: JUMP 00001784: JUMPDEST 00001785: PUSH1 0x60 00001787: PUSH2 0x0bb8 0000178a: PUSH1 0x00 0000178c: DUP4 0000178d: CALLER 0000178e: PUSH1 0x40 00001790: MLOAD 00001791: DUP1 00001792: PUSH1 0x20 00001794: ADD 00001795: PUSH1 0x40 00001797: MSTORE 00001798: DUP1 00001799: PUSH1 0x00 0000179b: DUP2 0000179c: MSTORE 0000179d: POP 0000179e: PUSH2 0x18e4 000017a1: JUMP 000017a2: JUMPDEST 000017a3: PUSH1 0x00 000017a5: DUP2 000017a6: DUP2 000017a7: MSTORE 000017a8: PUSH1 0x01 000017aa: DUP4 000017ab: ADD 000017ac: PUSH1 0x20 000017ae: MSTORE 000017af: PUSH1 0x40 000017b1: DUP2 000017b2: KECCAK256 000017b3: SLOAD 000017b4: DUP1 000017b5: ISZERO 000017b6: PUSH2 0x188b 000017b9: JUMPI 000017ba: PUSH1 0x00 000017bc: PUSH2 0x17c6 000017bf: PUSH1 0x01 000017c1: DUP4 000017c2: PUSH2 0x1c8a 000017c5: JUMP 000017c6: JUMPDEST 000017c7: DUP6 000017c8: SLOAD 000017c9: SWAP1 000017ca: SWAP2 000017cb: POP 000017cc: PUSH1 0x00 000017ce: SWAP1 000017cf: PUSH2 0x17da 000017d2: SWAP1 000017d3: PUSH1 0x01 000017d5: SWAP1 000017d6: PUSH2 0x1c8a 000017d9: JUMP 000017da: JUMPDEST 000017db: SWAP1 000017dc: POP 000017dd: DUP2 000017de: DUP2 000017df: EQ 000017e0: PUSH2 0x183f 000017e3: JUMPI 000017e4: PUSH1 0x00 000017e6: DUP7 000017e7: PUSH1 0x00 000017e9: ADD 000017ea: DUP3 000017eb: DUP2 000017ec: SLOAD 000017ed: DUP2 000017ee: LT 000017ef: PUSH2 0x17fa 000017f2: JUMPI 000017f3: PUSH2 0x17fa 000017f6: PUSH2 0x1c19 000017f9: JUMP 000017fa: JUMPDEST 000017fb: SWAP1 000017fc: PUSH1 0x00 000017fe: MSTORE 000017ff: PUSH1 0x20 00001801: PUSH1 0x00 00001803: KECCAK256 00001804: ADD 00001805: SLOAD 00001806: SWAP1 00001807: POP 00001808: DUP1 00001809: DUP8 0000180a: PUSH1 0x00 0000180c: ADD 0000180d: DUP5 0000180e: DUP2 0000180f: SLOAD 00001810: DUP2 00001811: LT 00001812: PUSH2 0x181d 00001815: JUMPI 00001816: PUSH2 0x181d 00001819: PUSH2 0x1c19 0000181c: JUMP 0000181d: JUMPDEST 0000181e: PUSH1 0x00 00001820: SWAP2 00001821: DUP3 00001822: MSTORE 00001823: PUSH1 0x20 00001825: DUP1 00001826: DUP4 00001827: KECCAK256 00001828: SWAP1 00001829: SWAP2 0000182a: ADD 0000182b: SWAP3 0000182c: SWAP1 0000182d: SWAP3 0000182e: SSTORE 0000182f: SWAP2 00001830: DUP3 00001831: MSTORE 00001832: PUSH1 0x01 00001834: DUP9 00001835: ADD 00001836: SWAP1 00001837: MSTORE 00001838: PUSH1 0x40 0000183a: SWAP1 0000183b: KECCAK256 0000183c: DUP4 0000183d: SWAP1 0000183e: SSTORE 0000183f: JUMPDEST 00001840: DUP6 00001841: SLOAD 00001842: DUP7 00001843: SWAP1 00001844: DUP1 00001845: PUSH2 0x1850 00001848: JUMPI 00001849: PUSH2 0x1850 0000184c: PUSH2 0x1ed6 0000184f: JUMP 00001850: JUMPDEST 00001851: PUSH1 0x01 00001853: SWAP1 00001854: SUB 00001855: DUP2 00001856: DUP2 00001857: SWAP1 00001858: PUSH1 0x00 0000185a: MSTORE 0000185b: PUSH1 0x20 0000185d: PUSH1 0x00 0000185f: KECCAK256 00001860: ADD 00001861: PUSH1 0x00 00001863: SWAP1 00001864: SSTORE 00001865: SWAP1 00001866: SSTORE 00001867: DUP6 00001868: PUSH1 0x01 0000186a: ADD 0000186b: PUSH1 0x00 0000186d: DUP7 0000186e: DUP2 0000186f: MSTORE 00001870: PUSH1 0x20 00001872: ADD 00001873: SWAP1 00001874: DUP2 00001875: MSTORE 00001876: PUSH1 0x20 00001878: ADD 00001879: PUSH1 0x00 0000187b: KECCAK256 0000187c: PUSH1 0x00 0000187e: SWAP1 0000187f: SSTORE 00001880: PUSH1 0x01 00001882: SWAP4 00001883: POP 00001884: POP 00001885: POP 00001886: POP 00001887: PUSH2 0x0bb8 0000188a: JUMP 0000188b: JUMPDEST 0000188c: PUSH1 0x00 0000188e: SWAP2 0000188f: POP 00001890: POP 00001891: PUSH2 0x0bb8 00001894: JUMP 00001895: JUMPDEST 00001896: PUSH1 0x00 00001898: DUP2 00001899: DUP2 0000189a: MSTORE 0000189b: PUSH1 0x01 0000189d: DUP4 0000189e: ADD 0000189f: PUSH1 0x20 000018a1: MSTORE 000018a2: PUSH1 0x40 000018a4: DUP2 000018a5: KECCAK256 000018a6: SLOAD 000018a7: PUSH2 0x18dc 000018aa: JUMPI 000018ab: POP 000018ac: DUP2 000018ad: SLOAD 000018ae: PUSH1 0x01 000018b0: DUP2 000018b1: DUP2 000018b2: ADD 000018b3: DUP5 000018b4: SSTORE 000018b5: PUSH1 0x00 000018b7: DUP5 000018b8: DUP2 000018b9: MSTORE 000018ba: PUSH1 0x20 000018bc: DUP1 000018bd: DUP3 000018be: KECCAK256 000018bf: SWAP1 000018c0: SWAP4 000018c1: ADD 000018c2: DUP5 000018c3: SWAP1 000018c4: SSTORE 000018c5: DUP5 000018c6: SLOAD 000018c7: DUP5 000018c8: DUP3 000018c9: MSTORE 000018ca: DUP3 000018cb: DUP7 000018cc: ADD 000018cd: SWAP1 000018ce: SWAP4 000018cf: MSTORE 000018d0: PUSH1 0x40 000018d2: SWAP1 000018d3: KECCAK256 000018d4: SWAP2 000018d5: SWAP1 000018d6: SWAP2 000018d7: SSTORE 000018d8: PUSH2 0x0bb8 000018db: JUMP 000018dc: JUMPDEST 000018dd: POP 000018de: PUSH1 0x00 000018e0: PUSH2 0x0bb8 000018e3: JUMP 000018e4: JUMPDEST 000018e5: PUSH1 0x60 000018e7: PUSH1 0x01 000018e9: DUP6 000018ea: DUP6 000018eb: DUP6 000018ec: DUP6 000018ed: PUSH1 0x40 000018ef: MLOAD 000018f0: PUSH1 0x20 000018f2: ADD 000018f3: PUSH2 0x1900 000018f6: SWAP6 000018f7: SWAP5 000018f8: SWAP4 000018f9: SWAP3 000018fa: SWAP2 000018fb: SWAP1 000018fc: PUSH2 0x1f05 000018ff: JUMP 00001900: JUMPDEST 00001901: PUSH1 0x40 00001903: MLOAD 00001904: PUSH1 0x20 00001906: DUP2 00001907: DUP4 00001908: SUB 00001909: SUB 0000190a: DUP2 0000190b: MSTORE 0000190c: SWAP1 0000190d: PUSH1 0x40 0000190f: MSTORE 00001910: SWAP1 00001911: POP 00001912: SWAP5 00001913: SWAP4 00001914: POP 00001915: POP 00001916: POP 00001917: POP 00001918: JUMP 00001919: JUMPDEST 0000191a: PUSH1 0x00 0000191c: PUSH1 0x20 0000191e: DUP3 0000191f: DUP5 00001920: SUB 00001921: SLT 00001922: ISZERO 00001923: PUSH2 0x192b 00001926: JUMPI 00001927: PUSH1 0x00 00001929: DUP1 0000192a: REVERT 0000192b: JUMPDEST 0000192c: DUP2 0000192d: CALLDATALOAD 0000192e: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00001943: DUP2 00001944: AND 00001945: DUP2 00001946: EQ 00001947: PUSH2 0x1151 0000194a: JUMPI 0000194b: PUSH1 0x00 0000194d: DUP1 0000194e: REVERT 0000194f: JUMPDEST 00001950: DUP1 00001951: CALLDATALOAD 00001952: PUSH4 0xffffffff 00001957: DUP2 00001958: AND 00001959: DUP2 0000195a: EQ 0000195b: PUSH2 0x1963 0000195e: JUMPI 0000195f: PUSH1 0x00 00001961: DUP1 00001962: REVERT 00001963: JUMPDEST 00001964: SWAP2 00001965: SWAP1 00001966: POP 00001967: JUMP 00001968: JUMPDEST 00001969: PUSH1 0x00 0000196b: PUSH1 0x20 0000196d: DUP3 0000196e: DUP5 0000196f: SUB 00001970: SLT 00001971: ISZERO 00001972: PUSH2 0x197a 00001975: JUMPI 00001976: PUSH1 0x00 00001978: DUP1 00001979: REVERT 0000197a: JUMPDEST 0000197b: PUSH2 0x1151 0000197e: DUP3 0000197f: PUSH2 0x194f 00001982: JUMP 00001983: JUMPDEST 00001984: PUSH1 0x20 00001986: DUP1 00001987: DUP3 00001988: MSTORE 00001989: DUP3 0000198a: MLOAD 0000198b: DUP3 0000198c: DUP3 0000198d: ADD 0000198e: DUP2 0000198f: SWAP1 00001990: MSTORE 00001991: PUSH1 0x00 00001993: SWAP2 00001994: SWAP1 00001995: DUP5 00001996: DUP3 00001997: ADD 00001998: SWAP1 00001999: PUSH1 0x40 0000199b: DUP6 0000199c: ADD 0000199d: SWAP1 0000199e: DUP5 0000199f: JUMPDEST 000019a0: DUP2 000019a1: DUP2 000019a2: LT 000019a3: ISZERO 000019a4: PUSH2 0x19c1 000019a7: JUMPI 000019a8: DUP4 000019a9: MLOAD 000019aa: PUSH4 0xffffffff 000019af: AND 000019b0: DUP4 000019b1: MSTORE 000019b2: SWAP3 000019b3: DUP5 000019b4: ADD 000019b5: SWAP3 000019b6: SWAP2 000019b7: DUP5 000019b8: ADD 000019b9: SWAP2 000019ba: PUSH1 0x01 000019bc: ADD 000019bd: PUSH2 0x199f 000019c0: JUMP 000019c1: JUMPDEST 000019c2: POP 000019c3: SWAP1 000019c4: SWAP7 000019c5: SWAP6 000019c6: POP 000019c7: POP 000019c8: POP 000019c9: POP 000019ca: POP 000019cb: POP 000019cc: JUMP 000019cd: JUMPDEST 000019ce: PUSH1 0x00 000019d0: DUP1 000019d1: PUSH1 0x40 000019d3: DUP4 000019d4: DUP6 000019d5: SUB 000019d6: SLT 000019d7: ISZERO 000019d8: PUSH2 0x19e0 000019db: JUMPI 000019dc: PUSH1 0x00 000019de: DUP1 000019df: REVERT 000019e0: JUMPDEST 000019e1: PUSH2 0x19e9 000019e4: DUP4 000019e5: PUSH2 0x194f 000019e8: JUMP 000019e9: JUMPDEST 000019ea: SWAP5 000019eb: PUSH1 0x20 000019ed: SWAP4 000019ee: SWAP1 000019ef: SWAP4 000019f0: ADD 000019f1: CALLDATALOAD 000019f2: SWAP4 000019f3: POP 000019f4: POP 000019f5: POP 000019f6: JUMP 000019f7: JUMPDEST 000019f8: PUSH1 0x00 000019fa: DUP1 000019fb: PUSH1 0x00 000019fd: DUP1 000019fe: PUSH1 0x60 00001a00: DUP6 00001a01: DUP8 00001a02: SUB 00001a03: SLT 00001a04: ISZERO 00001a05: PUSH2 0x1a0d 00001a08: JUMPI 00001a09: PUSH1 0x00 00001a0b: DUP1 00001a0c: REVERT 00001a0d: JUMPDEST 00001a0e: PUSH2 0x1a16 00001a11: DUP6 00001a12: PUSH2 0x194f 00001a15: JUMP 00001a16: JUMPDEST 00001a17: SWAP4 00001a18: POP 00001a19: PUSH1 0x20 00001a1b: DUP6 00001a1c: ADD 00001a1d: CALLDATALOAD 00001a1e: SWAP3 00001a1f: POP 00001a20: PUSH1 0x40 00001a22: DUP6 00001a23: ADD 00001a24: CALLDATALOAD 00001a25: PUSH8 0xffffffffffffffff 00001a2e: DUP1 00001a2f: DUP3 00001a30: GT 00001a31: ISZERO 00001a32: PUSH2 0x1a3a 00001a35: JUMPI 00001a36: PUSH1 0x00 00001a38: DUP1 00001a39: REVERT 00001a3a: JUMPDEST 00001a3b: DUP2 00001a3c: DUP8 00001a3d: ADD 00001a3e: SWAP2 00001a3f: POP 00001a40: DUP8 00001a41: PUSH1 0x1f 00001a43: DUP4 00001a44: ADD 00001a45: SLT 00001a46: PUSH2 0x1a4e 00001a49: JUMPI 00001a4a: PUSH1 0x00 00001a4c: DUP1 00001a4d: REVERT 00001a4e: JUMPDEST 00001a4f: DUP2 00001a50: CALLDATALOAD 00001a51: DUP2 00001a52: DUP2 00001a53: GT 00001a54: ISZERO 00001a55: PUSH2 0x1a5d 00001a58: JUMPI 00001a59: PUSH1 0x00 00001a5b: DUP1 00001a5c: REVERT 00001a5d: JUMPDEST 00001a5e: DUP9 00001a5f: PUSH1 0x20 00001a61: DUP3 00001a62: DUP6 00001a63: ADD 00001a64: ADD 00001a65: GT 00001a66: ISZERO 00001a67: PUSH2 0x1a6f 00001a6a: JUMPI 00001a6b: PUSH1 0x00 00001a6d: DUP1 00001a6e: REVERT 00001a6f: JUMPDEST 00001a70: SWAP6 00001a71: SWAP9 00001a72: SWAP5 00001a73: SWAP8 00001a74: POP 00001a75: POP 00001a76: PUSH1 0x20 00001a78: ADD 00001a79: SWAP5 00001a7a: POP 00001a7b: POP 00001a7c: POP 00001a7d: JUMP 00001a7e: JUMPDEST 00001a7f: PUSH1 0x00 00001a81: DUP1 00001a82: DUP4 00001a83: PUSH1 0x1f 00001a85: DUP5 00001a86: ADD 00001a87: SLT 00001a88: PUSH2 0x1a90 00001a8b: JUMPI 00001a8c: PUSH1 0x00 00001a8e: DUP1 00001a8f: REVERT 00001a90: JUMPDEST 00001a91: POP 00001a92: DUP2 00001a93: CALLDATALOAD 00001a94: PUSH8 0xffffffffffffffff 00001a9d: DUP2 00001a9e: GT 00001a9f: ISZERO 00001aa0: PUSH2 0x1aa8 00001aa3: JUMPI 00001aa4: PUSH1 0x00 00001aa6: DUP1 00001aa7: REVERT 00001aa8: JUMPDEST 00001aa9: PUSH1 0x20 00001aab: DUP4 00001aac: ADD 00001aad: SWAP2 00001aae: POP 00001aaf: DUP4 00001ab0: PUSH1 0x20 00001ab2: DUP3 00001ab3: PUSH1 0x05 00001ab5: SHL 00001ab6: DUP6 00001ab7: ADD 00001ab8: ADD 00001ab9: GT 00001aba: ISZERO 00001abb: PUSH2 0x0d05 00001abe: JUMPI 00001abf: PUSH1 0x00 00001ac1: DUP1 00001ac2: REVERT 00001ac3: JUMPDEST 00001ac4: PUSH1 0x00 00001ac6: DUP1 00001ac7: PUSH1 0x20 00001ac9: DUP4 00001aca: DUP6 00001acb: SUB 00001acc: SLT 00001acd: ISZERO 00001ace: PUSH2 0x1ad6 00001ad1: JUMPI 00001ad2: PUSH1 0x00 00001ad4: DUP1 00001ad5: REVERT 00001ad6: JUMPDEST 00001ad7: DUP3 00001ad8: CALLDATALOAD 00001ad9: PUSH8 0xffffffffffffffff 00001ae2: DUP2 00001ae3: GT 00001ae4: ISZERO 00001ae5: PUSH2 0x1aed 00001ae8: JUMPI 00001ae9: PUSH1 0x00 00001aeb: DUP1 00001aec: REVERT 00001aed: JUMPDEST 00001aee: PUSH2 0x1af9 00001af1: DUP6 00001af2: DUP3 00001af3: DUP7 00001af4: ADD 00001af5: PUSH2 0x1a7e 00001af8: JUMP 00001af9: JUMPDEST 00001afa: SWAP1 00001afb: SWAP7 00001afc: SWAP1 00001afd: SWAP6 00001afe: POP 00001aff: SWAP4 00001b00: POP 00001b01: POP 00001b02: POP 00001b03: POP 00001b04: JUMP 00001b05: JUMPDEST 00001b06: PUSH1 0x00 00001b08: DUP1 00001b09: PUSH1 0x00 00001b0b: PUSH1 0x60 00001b0d: DUP5 00001b0e: DUP7 00001b0f: SUB 00001b10: SLT 00001b11: ISZERO 00001b12: PUSH2 0x1b1a 00001b15: JUMPI 00001b16: PUSH1 0x00 00001b18: DUP1 00001b19: REVERT 00001b1a: JUMPDEST 00001b1b: PUSH2 0x1b23 00001b1e: DUP5 00001b1f: PUSH2 0x194f 00001b22: JUMP 00001b23: JUMPDEST 00001b24: SWAP6 00001b25: PUSH1 0x20 00001b27: DUP6 00001b28: ADD 00001b29: CALLDATALOAD 00001b2a: SWAP6 00001b2b: POP 00001b2c: PUSH1 0x40 00001b2e: SWAP1 00001b2f: SWAP5 00001b30: ADD 00001b31: CALLDATALOAD 00001b32: SWAP4 00001b33: SWAP3 00001b34: POP 00001b35: POP 00001b36: POP 00001b37: JUMP 00001b38: JUMPDEST 00001b39: PUSH1 0x00 00001b3b: DUP1 00001b3c: PUSH1 0x20 00001b3e: DUP4 00001b3f: DUP6 00001b40: SUB 00001b41: SLT 00001b42: ISZERO 00001b43: PUSH2 0x1b4b 00001b46: JUMPI 00001b47: PUSH1 0x00 00001b49: DUP1 00001b4a: REVERT 00001b4b: JUMPDEST 00001b4c: DUP3 00001b4d: CALLDATALOAD 00001b4e: PUSH8 0xffffffffffffffff 00001b57: DUP1 00001b58: DUP3 00001b59: GT 00001b5a: ISZERO 00001b5b: PUSH2 0x1b63 00001b5e: JUMPI 00001b5f: PUSH1 0x00 00001b61: DUP1 00001b62: REVERT 00001b63: JUMPDEST 00001b64: DUP2 00001b65: DUP6 00001b66: ADD 00001b67: SWAP2 00001b68: POP 00001b69: DUP6 00001b6a: PUSH1 0x1f 00001b6c: DUP4 00001b6d: ADD 00001b6e: SLT 00001b6f: PUSH2 0x1b77 00001b72: JUMPI 00001b73: PUSH1 0x00 00001b75: DUP1 00001b76: REVERT 00001b77: JUMPDEST 00001b78: DUP2 00001b79: CALLDATALOAD 00001b7a: DUP2 00001b7b: DUP2 00001b7c: GT 00001b7d: ISZERO 00001b7e: PUSH2 0x1b86 00001b81: JUMPI 00001b82: PUSH1 0x00 00001b84: DUP1 00001b85: REVERT 00001b86: JUMPDEST 00001b87: DUP7 00001b88: PUSH1 0x20 00001b8a: DUP3 00001b8b: PUSH1 0x06 00001b8d: SHL 00001b8e: DUP6 00001b8f: ADD 00001b90: ADD 00001b91: GT 00001b92: ISZERO 00001b93: PUSH2 0x1b9b 00001b96: JUMPI 00001b97: PUSH1 0x00 00001b99: DUP1 00001b9a: REVERT 00001b9b: JUMPDEST 00001b9c: PUSH1 0x20 00001b9e: SWAP3 00001b9f: SWAP1 00001ba0: SWAP3 00001ba1: ADD 00001ba2: SWAP7 00001ba3: SWAP2 00001ba4: SWAP6 00001ba5: POP 00001ba6: SWAP1 00001ba7: SWAP4 00001ba8: POP 00001ba9: POP 00001baa: POP 00001bab: POP 00001bac: JUMP 00001bad: JUMPDEST 00001bae: PUSH1 0x00 00001bb0: DUP1 00001bb1: PUSH1 0x00 00001bb3: DUP1 00001bb4: PUSH1 0x40 00001bb6: DUP6 00001bb7: DUP8 00001bb8: SUB 00001bb9: SLT 00001bba: ISZERO 00001bbb: PUSH2 0x1bc3 00001bbe: JUMPI 00001bbf: PUSH1 0x00 00001bc1: DUP1 00001bc2: REVERT 00001bc3: JUMPDEST 00001bc4: DUP5 00001bc5: CALLDATALOAD 00001bc6: PUSH8 0xffffffffffffffff 00001bcf: DUP1 00001bd0: DUP3 00001bd1: GT 00001bd2: ISZERO 00001bd3: PUSH2 0x1bdb 00001bd6: JUMPI 00001bd7: PUSH1 0x00 00001bd9: DUP1 00001bda: REVERT 00001bdb: JUMPDEST 00001bdc: PUSH2 0x1be7 00001bdf: DUP9 00001be0: DUP4 00001be1: DUP10 00001be2: ADD 00001be3: PUSH2 0x1a7e 00001be6: JUMP 00001be7: JUMPDEST 00001be8: SWAP1 00001be9: SWAP7 00001bea: POP 00001beb: SWAP5 00001bec: POP 00001bed: PUSH1 0x20 00001bef: DUP8 00001bf0: ADD 00001bf1: CALLDATALOAD 00001bf2: SWAP2 00001bf3: POP 00001bf4: DUP1 00001bf5: DUP3 00001bf6: GT 00001bf7: ISZERO 00001bf8: PUSH2 0x1c00 00001bfb: JUMPI 00001bfc: PUSH1 0x00 00001bfe: DUP1 00001bff: REVERT 00001c00: JUMPDEST 00001c01: POP 00001c02: PUSH2 0x1c0d 00001c05: DUP8 00001c06: DUP3 00001c07: DUP9 00001c08: ADD 00001c09: PUSH2 0x1a7e 00001c0c: JUMP 00001c0d: JUMPDEST 00001c0e: SWAP6 00001c0f: SWAP9 00001c10: SWAP5 00001c11: SWAP8 00001c12: POP 00001c13: SWAP6 00001c14: POP 00001c15: POP 00001c16: POP 00001c17: POP 00001c18: JUMP 00001c19: JUMPDEST 00001c1a: PUSH32 0x4e487b7100000000000000000000000000000000000000000000000000000000 00001c3b: PUSH1 0x00 00001c3d: MSTORE 00001c3e: PUSH1 0x32 00001c40: PUSH1 0x04 00001c42: MSTORE 00001c43: PUSH1 0x24 00001c45: PUSH1 0x00 00001c47: REVERT 00001c48: JUMPDEST 00001c49: PUSH32 0x4e487b7100000000000000000000000000000000000000000000000000000000 00001c6a: PUSH1 0x00 00001c6c: MSTORE 00001c6d: PUSH1 0x11 00001c6f: PUSH1 0x04 00001c71: MSTORE 00001c72: PUSH1 0x24 00001c74: PUSH1 0x00 00001c76: REVERT 00001c77: JUMPDEST 00001c78: DUP1 00001c79: DUP3 00001c7a: ADD 00001c7b: DUP1 00001c7c: DUP3 00001c7d: GT 00001c7e: ISZERO 00001c7f: PUSH2 0x0bb8 00001c82: JUMPI 00001c83: PUSH2 0x0bb8 00001c86: PUSH2 0x1c48 00001c89: JUMP 00001c8a: JUMPDEST 00001c8b: DUP2 00001c8c: DUP2 00001c8d: SUB 00001c8e: DUP2 00001c8f: DUP2 00001c90: GT 00001c91: ISZERO 00001c92: PUSH2 0x0bb8 00001c95: JUMPI 00001c96: PUSH2 0x0bb8 00001c99: PUSH2 0x1c48 00001c9c: JUMP 00001c9d: JUMPDEST 00001c9e: PUSH32 0x4e487b7100000000000000000000000000000000000000000000000000000000 00001cbf: PUSH1 0x00 00001cc1: MSTORE 00001cc2: PUSH1 0x41 00001cc4: PUSH1 0x04 00001cc6: MSTORE 00001cc7: PUSH1 0x24 00001cc9: PUSH1 0x00 00001ccb: REVERT 00001ccc: JUMPDEST 00001ccd: PUSH1 0x00 00001ccf: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 00001cf0: DUP3 00001cf1: SUB 00001cf2: PUSH2 0x1cfd 00001cf5: JUMPI 00001cf6: PUSH2 0x1cfd 00001cf9: PUSH2 0x1c48 00001cfc: JUMP 00001cfd: JUMPDEST 00001cfe: POP 00001cff: PUSH1 0x01 00001d01: ADD 00001d02: SWAP1 00001d03: JUMP 00001d04: JUMPDEST 00001d05: PUSH1 0x00 00001d07: JUMPDEST 00001d08: DUP4 00001d09: DUP2 00001d0a: LT 00001d0b: ISZERO 00001d0c: PUSH2 0x1d1f 00001d0f: JUMPI 00001d10: DUP2 00001d11: DUP2 00001d12: ADD 00001d13: MLOAD 00001d14: DUP4 00001d15: DUP3 00001d16: ADD 00001d17: MSTORE 00001d18: PUSH1 0x20 00001d1a: ADD 00001d1b: PUSH2 0x1d07 00001d1e: JUMP 00001d1f: JUMPDEST 00001d20: POP 00001d21: POP 00001d22: PUSH1 0x00 00001d24: SWAP2 00001d25: ADD 00001d26: MSTORE 00001d27: JUMP 00001d28: JUMPDEST 00001d29: PUSH1 0x00 00001d2b: DUP2 00001d2c: MLOAD 00001d2d: DUP1 00001d2e: DUP5 00001d2f: MSTORE 00001d30: PUSH2 0x1d40 00001d33: DUP2 00001d34: PUSH1 0x20 00001d36: DUP7 00001d37: ADD 00001d38: PUSH1 0x20 00001d3a: DUP7 00001d3b: ADD 00001d3c: PUSH2 0x1d04 00001d3f: JUMP 00001d40: JUMPDEST 00001d41: PUSH1 0x1f 00001d43: ADD 00001d44: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0 00001d65: AND 00001d66: SWAP3 00001d67: SWAP1 00001d68: SWAP3 00001d69: ADD 00001d6a: PUSH1 0x20 00001d6c: ADD 00001d6d: SWAP3 00001d6e: SWAP2 00001d6f: POP 00001d70: POP 00001d71: JUMP 00001d72: JUMPDEST 00001d73: PUSH1 0x20 00001d75: DUP2 00001d76: MSTORE 00001d77: PUSH1 0x00 00001d79: PUSH2 0x1151 00001d7c: PUSH1 0x20 00001d7e: DUP4 00001d7f: ADD 00001d80: DUP5 00001d81: PUSH2 0x1d28 00001d84: JUMP 00001d85: JUMPDEST 00001d86: PUSH32 0x4e6f20726f7574657220656e726f6c6c656420666f7220646f6d61696e3a2000 00001da7: DUP2 00001da8: MSTORE 00001da9: PUSH1 0x00 00001dab: DUP3 00001dac: MLOAD 00001dad: PUSH2 0x1dbd 00001db0: DUP2 00001db1: PUSH1 0x1f 00001db3: DUP6 00001db4: ADD 00001db5: PUSH1 0x20 00001db7: DUP8 00001db8: ADD 00001db9: PUSH2 0x1d04 00001dbc: JUMP 00001dbd: JUMPDEST 00001dbe: SWAP2 00001dbf: SWAP1 00001dc0: SWAP2 00001dc1: ADD 00001dc2: PUSH1 0x1f 00001dc4: ADD 00001dc5: SWAP3 00001dc6: SWAP2 00001dc7: POP 00001dc8: POP 00001dc9: JUMP 00001dca: JUMPDEST 00001dcb: PUSH1 0x00 00001dcd: DUP1 00001dce: DUP6 00001dcf: DUP6 00001dd0: GT 00001dd1: ISZERO 00001dd2: PUSH2 0x1dda 00001dd5: JUMPI 00001dd6: PUSH1 0x00 00001dd8: DUP1 00001dd9: REVERT 00001dda: JUMPDEST 00001ddb: DUP4 00001ddc: DUP7 00001ddd: GT 00001dde: ISZERO 00001ddf: PUSH2 0x1de7 00001de2: JUMPI 00001de3: PUSH1 0x00 00001de5: DUP1 00001de6: REVERT 00001de7: JUMPDEST 00001de8: POP 00001de9: POP 00001dea: DUP3 00001deb: ADD 00001dec: SWAP4 00001ded: SWAP2 00001dee: SWAP1 00001def: SWAP3 00001df0: SUB 00001df1: SWAP2 00001df2: POP 00001df3: JUMP 00001df4: JUMPDEST 00001df5: DUP1 00001df6: CALLDATALOAD 00001df7: PUSH1 0x20 00001df9: DUP4 00001dfa: LT 00001dfb: ISZERO 00001dfc: PUSH2 0x0bb8 00001dff: JUMPI 00001e00: PUSH32 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 00001e21: PUSH1 0x20 00001e23: DUP5 00001e24: SWAP1 00001e25: SUB 00001e26: PUSH1 0x03 00001e28: SHL 00001e29: SHL 00001e2a: AND 00001e2b: SWAP3 00001e2c: SWAP2 00001e2d: POP 00001e2e: POP 00001e2f: JUMP 00001e30: JUMPDEST 00001e31: DUP4 00001e32: DUP2 00001e33: MSTORE 00001e34: DUP3 00001e35: PUSH1 0x20 00001e37: DUP3 00001e38: ADD 00001e39: MSTORE 00001e3a: PUSH1 0x00 00001e3c: DUP3 00001e3d: MLOAD 00001e3e: PUSH2 0x1e4e 00001e41: DUP2 00001e42: PUSH1 0x40 00001e44: DUP6 00001e45: ADD 00001e46: PUSH1 0x20 00001e48: DUP8 00001e49: ADD 00001e4a: PUSH2 0x1d04 00001e4d: JUMP 00001e4e: JUMPDEST 00001e4f: SWAP2 00001e50: SWAP1 00001e51: SWAP2 00001e52: ADD 00001e53: PUSH1 0x40 00001e55: ADD 00001e56: SWAP5 00001e57: SWAP4 00001e58: POP 00001e59: POP 00001e5a: POP 00001e5b: POP 00001e5c: JUMP 00001e5d: JUMPDEST 00001e5e: PUSH4 0xffffffff 00001e63: DUP7 00001e64: AND 00001e65: DUP2 00001e66: MSTORE 00001e67: DUP5 00001e68: PUSH1 0x20 00001e6a: DUP3 00001e6b: ADD 00001e6c: MSTORE 00001e6d: PUSH1 0xa0 00001e6f: PUSH1 0x40 00001e71: DUP3 00001e72: ADD 00001e73: MSTORE 00001e74: PUSH1 0x00 00001e76: PUSH2 0x1e82 00001e79: PUSH1 0xa0 00001e7b: DUP4 00001e7c: ADD 00001e7d: DUP7 00001e7e: PUSH2 0x1d28 00001e81: JUMP 00001e82: JUMPDEST 00001e83: DUP3 00001e84: DUP2 00001e85: SUB 00001e86: PUSH1 0x60 00001e88: DUP5 00001e89: ADD 00001e8a: MSTORE 00001e8b: PUSH2 0x1e94 00001e8e: DUP2 00001e8f: DUP7 00001e90: PUSH2 0x1d28 00001e93: JUMP 00001e94: JUMPDEST 00001e95: SWAP2 00001e96: POP 00001e97: POP 00001e98: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00001ead: DUP4 00001eae: AND 00001eaf: PUSH1 0x80 00001eb1: DUP4 00001eb2: ADD 00001eb3: MSTORE 00001eb4: SWAP7 00001eb5: SWAP6 00001eb6: POP 00001eb7: POP 00001eb8: POP 00001eb9: POP 00001eba: POP 00001ebb: POP 00001ebc: JUMP 00001ebd: JUMPDEST 00001ebe: PUSH1 0x00 00001ec0: PUSH1 0x20 00001ec2: DUP3 00001ec3: DUP5 00001ec4: SUB 00001ec5: SLT 00001ec6: ISZERO 00001ec7: PUSH2 0x1ecf 00001eca: JUMPI 00001ecb: PUSH1 0x00 00001ecd: DUP1 00001ece: REVERT 00001ecf: JUMPDEST 00001ed0: POP 00001ed1: MLOAD 00001ed2: SWAP2 00001ed3: SWAP1 00001ed4: POP 00001ed5: JUMP 00001ed6: JUMPDEST 00001ed7: PUSH32 0x4e487b7100000000000000000000000000000000000000000000000000000000 00001ef8: PUSH1 0x00 00001efa: MSTORE 00001efb: PUSH1 0x31 00001efd: PUSH1 0x04 00001eff: MSTORE 00001f00: PUSH1 0x24 00001f02: PUSH1 0x00 00001f04: REVERT 00001f05: JUMPDEST 00001f06: PUSH32 0xffff000000000000000000000000000000000000000000000000000000000000 00001f27: DUP7 00001f28: PUSH1 0xf0 00001f2a: SHL 00001f2b: AND 00001f2c: DUP2 00001f2d: MSTORE 00001f2e: DUP5 00001f2f: PUSH1 0x02 00001f31: DUP3 00001f32: ADD 00001f33: MSTORE 00001f34: DUP4 00001f35: PUSH1 0x22 00001f37: DUP3 00001f38: ADD 00001f39: MSTORE 00001f3a: PUSH32 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000 00001f5b: DUP4 00001f5c: PUSH1 0x60 00001f5e: SHL 00001f5f: AND 00001f60: PUSH1 0x42 00001f62: DUP3 00001f63: ADD 00001f64: MSTORE 00001f65: PUSH1 0x00 00001f67: DUP3 00001f68: MLOAD 00001f69: PUSH2 0x1f79 00001f6c: DUP2 00001f6d: PUSH1 0x56 00001f6f: DUP6 00001f70: ADD 00001f71: PUSH1 0x20 00001f73: DUP8 00001f74: ADD 00001f75: PUSH2 0x1d04 00001f78: JUMP 00001f79: JUMPDEST 00001f7a: SWAP2 00001f7b: SWAP1 00001f7c: SWAP2 00001f7d: ADD 00001f7e: PUSH1 0x56 00001f80: ADD 00001f81: SWAP7 00001f82: SWAP6 00001f83: POP 00001f84: POP 00001f85: POP 00001f86: POP 00001f87: POP 00001f88: POP 00001f89: JUMP 00001f8a: INVALID 00001f8b: LOG2 00001f8c: PUSH5 0x6970667358 00001f92: UNKNOWN(0x22) 00001f93: SLT 00001f94: KECCAK256 00001f95: UNKNOWN(0xF6) 00001f96: UNKNOWN(0x23) 00001f97: MCOPY 00001f98: UNKNOWN(0xBA) 00001f99: TIMESTAMP 00001f9a: UNKNOWN(0xCD) 00001f9b: SWAP7 00001f9c: MCOPY 00001f9d: UNKNOWN(0xC8) 00001f9e: PUSH26 0x7320b70fa3f71041a56ca31e8577e21f58323505f76e64736f6c 00001fb9: PUSH4 0x43000813 00001fbe: STOP 00001fbf: CALLER
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| high | auth | codex | Permissionless initializers let the first caller seize control and mint supply | 85 | no | — |
viewevidence_json{
"evidence": "`initialize`/`setAuthorizedHook` are externally callable with only the OZ `initializer` modifier, which enforces one-time execution but does not authenticate the caller. Some of these initializers also mint to `msg.sender` or rewrite owner/configuration.",
"locations": [
"AUDIT_BUNDLE.txt:9360 (contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol)",
"AUDIT_BUNDLE.txt:10150 (contracts/hooks/igp/InterchainGasPaymaster.sol)",
"AUDIT_BUNDLE.txt:12612 (contracts/isms/routing/DomainRoutingIsm.sol)",
"AUDIT_BUNDLE.txt:12623 (contracts/isms/routing/DomainRoutingIsm.sol)",
"AUDIT_BUNDLE.txt:13188 (contracts/Mailbox.sol)",
"AUDIT_BUNDLE.txt:13664 (contracts/middleware/InterchainAccountRouter.sol)",
"AUDIT_BUNDLE.txt:14255 (contracts/middleware/InterchainQueryRouter.sol)",
"AUDIT_BUNDLE.txt:14545 (contracts/middleware/liquidity-layer/adapters/CircleBridgeAdapter.sol)",
"AUDIT_BUNDLE.txt:14862 (contracts/middleware/liquidity-layer/adapters/PortalAdapter.sol)",
"AUDIT_BUNDLE.txt:15074 (contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol)",
"AUDIT_BUNDLE.txt:16441 (contracts/token/HypERC20.sol)",
"AUDIT_BUNDLE.txt:16992 (contracts/token/HypERC721.sol)"
],
"recommendation": "Do not expose public initialization for constructor-deployed instances. Move critical setup into the constructor or gate initialization to the constructor-established owner with a separate one-time guard. If these are meant to be proxy implementations, disable initializers on the implementation and initialize atomically via proxy constructor calldata.",
"exploit_scenario": "Several production contracts are deployed with constructors but defer critical setup to unrestricted `initializer` functions. If deployment and initialization are not atomic, an attacker can front-run the intended initializer call, become owner, install malicious hooks/ISMs/bridge endpoints, or mint the full initial token/NFT supply to themselves. For example, hijacking `Mailbox.initialize` gives the attacker control over `defaultIsm/defaultHook/requiredHook`, while hijacking `HypERC20.initialize` or `HypERC721.initialize` mints the initial supply to the attacker."
}
|
| high | auth | codex | Anyone can call post-dispatch hooks directly for the current latest message | 85 | no | — |
viewevidence_json{
"evidence": "`AbstractPostDispatchHook.postDispatch` has no caller check. `MerkleTreeHook` and `AbstractMessageIdAuthHook` only verify that the supplied message matches `mailbox.latestDispatchedId()`, which any external caller can satisfy by replaying the just-emitted message bytes.",
"locations": [
"AUDIT_BUNDLE.txt:9226 (contracts/hooks/libs/AbstractPostDispatchHook.sol)",
"AUDIT_BUNDLE.txt:9489 (contracts/hooks/libs/AbstractMessageIdAuthHook.sol)",
"AUDIT_BUNDLE.txt:10753 (contracts/hooks/MerkleTreeHook.sol)"
],
"recommendation": "Require `msg.sender == address(mailbox)` for hooks whose security depends on mailbox state, or override `postDispatch` in those hooks to enforce mailbox-only access. Add single-use guards per message ID where duplicate execution would be unsafe.",
"exploit_scenario": "After any legitimate dispatch, the full message bytes are public in the `Dispatch` event and `latestDispatchedId` stays set until the next dispatch. Because `AbstractPostDispatchHook.postDispatch` is permissionless, an attacker can call `MerkleTreeHook.postDispatch` directly and insert the same message ID into the Merkle tree again, corrupting checkpoint state. The same pattern also lets an attacker directly trigger message-id authorization hooks (`ERC5164Hook`/`OPStackHook`) for the current latest message, even if that message did not actually opt into those hooks through mailbox dispatch."
}
|
| medium | other | detector | ETH value transfer possible | 40 | no | no |
viewtrace_id: 0xb2a2ca4291571cc73421f27f59107c08b13c8c18696ba76e1ccc0531cc23ade9
call_targets:
0x15b5d6b614242b118aa404528a7f3e2ad241e4a4
target_varies:
no
classification:
constant_target
validation_json{
"sink": "CALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0xb2a2ca4291571cc73421f27f59107c08b13c8c18696ba76e1ccc0531cc23ade9",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0x15b5d6b614242b118aa404528a7f3e2ad241e4a4"
],
"matched_probe": null,
"target_varies": false,
"classification": "constant_target"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0xefae508a",
"selector": "0xefae508a",
"calldata_variants": [
"0xefae508a",
"0xefae508a0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xf2ed8c53",
"selector": "0xf2ed8c53",
"calldata_variants": [
"0xf2ed8c53",
"0xf2ed8c530000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xf2fde38b",
"selector": "0xf2fde38b",
"calldata_variants": [
"0xf2fde38b",
"0xf2fde38b0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xd5438eae",
"selector": "0xd5438eae",
"calldata_variants": [
"0xd5438eae",
"0xd5438eae0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xde523cf3",
"selector": "0xde523cf3",
"calldata_variants": [
"0xde523cf3",
"0xde523cf30000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xe9198bf9",
"selector": "0xe9198bf9",
"calldata_variants": [
"0xe9198bf9",
"0xe9198bf90000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0x8da5cb5b",
"selector": "0x8da5cb5b",
"calldata_variants": [
"0x8da5cb5b",
"0x8da5cb5b0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xb1bd6436",
"selector": "0xb1bd6436",
"calldata_variants": [
"0xb1bd6436",
"0xb1bd64360000000000000000000000000000000000000000000000000000000000000000"
]
}
]
}
evidence_json{
"examples": [
{
"pc": 5176,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
},
{
"pc": 5577,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
}
],
"eth_value_calls": 2,
"guards_detected": [
{
"pc": 1949,
"type": "msg_sender_eq_const",
"value": "0xffffffffffffffffffffffffffffffffffffffff"
}
]
}
|
| medium | other | detector | Untrusted CALL target/value reachable | 45 | no | no |
viewtrace_id: 0xb2a2ca4291571cc73421f27f59107c08b13c8c18696ba76e1ccc0531cc23ade9
call_targets:
0x15b5d6b614242b118aa404528a7f3e2ad241e4a4
target_varies:
no
classification:
constant_target
validation_json{
"sink": "CALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0xb2a2ca4291571cc73421f27f59107c08b13c8c18696ba76e1ccc0531cc23ade9",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0x15b5d6b614242b118aa404528a7f3e2ad241e4a4"
],
"matched_probe": null,
"target_varies": false,
"classification": "constant_target"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0xefae508a",
"selector": "0xefae508a",
"calldata_variants": [
"0xefae508a",
"0xefae508a0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xf2ed8c53",
"selector": "0xf2ed8c53",
"calldata_variants": [
"0xf2ed8c53",
"0xf2ed8c530000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xf2fde38b",
"selector": "0xf2fde38b",
"calldata_variants": [
"0xf2fde38b",
"0xf2fde38b0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xd5438eae",
"selector": "0xd5438eae",
"calldata_variants": [
"0xd5438eae",
"0xd5438eae0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xde523cf3",
"selector": "0xde523cf3",
"calldata_variants": [
"0xde523cf3",
"0xde523cf30000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xe9198bf9",
"selector": "0xe9198bf9",
"calldata_variants": [
"0xe9198bf9",
"0xe9198bf90000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0x8da5cb5b",
"selector": "0x8da5cb5b",
"calldata_variants": [
"0x8da5cb5b",
"0x8da5cb5b0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xb1bd6436",
"selector": "0xb1bd6436",
"calldata_variants": [
"0xb1bd6436",
"0xb1bd64360000000000000000000000000000000000000000000000000000000000000000"
]
}
]
}
evidence_json{
"examples": [
{
"pc": 5176,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
},
{
"pc": 5577,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
}
],
"call_count": 2,
"guards_detected": [
{
"pc": 1949,
"type": "msg_sender_eq_const",
"value": "0xffffffffffffffffffffffffffffffffffffffff"
}
],
"untrusted_call_count": 2
}
|
| low | other | codex | DestinationRecipientRoutingHook quotes the wrong hook for recipient-specific routes | 65 | no | — |
viewevidence_json{
"evidence": "`DestinationRecipientRoutingHook._postDispatch` consults `customHooks[destination][recipient]`, but inherited `DomainRoutingHook._quoteDispatch` only calls `_getConfiguredHook(message)`, which is destination-only and never checks the recipient override map.",
"locations": [
"AUDIT_BUNDLE.txt:11186 (contracts/hooks/routing/DomainRoutingHook.sol)",
"AUDIT_BUNDLE.txt:11238 (contracts/hooks/routing/DestinationRecipientRoutingHook.sol)"
],
"recommendation": "Override `_quoteDispatch` in `DestinationRecipientRoutingHook` so it resolves the same hook that `_postDispatch` will actually call, including recipient-specific overrides.",
"exploit_scenario": "If the owner configures a `customHooks[destination][recipient]`, dispatch execution will route to that custom hook, but fee quoting still uses the destination-level hook from `DomainRoutingHook`. Users relying on `quoteDispatch` can underpay and have dispatches revert, making recipient-specific routes unusable unless callers manually overpay."
}
|