v3 Router SDK changes (#2752)

### Description

- Reorganize solidity directory structure for clarity
- Updates router apps (token and helloworld) with changes from
https://github.com/hyperlane-xyz/hyperlane-monorepo/pull/2749

### Drive-by changes

- Merges the hyperlane-token package to solidity and sdk

### Backward compatibility

No (removed token package)

### Testing

- Unit Tests
- Instrumented coverage
merkle-vs-mapping
Yorke Rhodes 1 year ago committed by GitHub
parent fcfecdf250
commit 1ecfc46ce2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      .github/workflows/node.yml
  2. 3
      .gitmodules
  3. 1
      Dockerfile
  4. 31
      codecov.yml
  5. 2
      solidity/.solcover.js
  6. 2
      solidity/contracts/Mailbox.sol
  7. 4
      solidity/contracts/client/GasRouter.sol
  8. 14
      solidity/contracts/client/MailboxClient.sol
  9. 25
      solidity/contracts/client/Router.sol
  10. 6
      solidity/contracts/hooks/MerkleTreeHook.sol
  11. 4
      solidity/contracts/hooks/OPStackHook.sol
  12. 4
      solidity/contracts/hooks/PausableHook.sol
  13. 5
      solidity/contracts/hooks/StaticProtocolFee.sol
  14. 8
      solidity/contracts/hooks/aggregation/ERC5164Hook.sol
  15. 4
      solidity/contracts/hooks/aggregation/StaticAggregationHook.sol
  16. 16
      solidity/contracts/hooks/igp/InterchainGasPaymaster.sol
  17. 3
      solidity/contracts/hooks/igp/OverheadIgp.sol
  18. 1
      solidity/contracts/hooks/igp/StorageGasOracle.sol
  19. 15
      solidity/contracts/hooks/libs/AbstractMessageIdAuthHook.sol
  20. 4
      solidity/contracts/hooks/libs/AbstractPostDispatchHook.sol
  21. 0
      solidity/contracts/hooks/libs/StandardHookMetadata.sol
  22. 4
      solidity/contracts/hooks/routing/DestinationRecipientRoutingHook.sol
  23. 10
      solidity/contracts/hooks/routing/DomainRoutingHook.sol
  24. 6
      solidity/contracts/hooks/routing/FallbackDomainRoutingHook.sol
  25. 6
      solidity/contracts/interfaces/IValidatorAnnounce.sol
  26. 50
      solidity/contracts/interfaces/middleware/IInterchainAccountRouter.sol
  27. 18
      solidity/contracts/interfaces/middleware/IInterchainQueryRouter.sol
  28. 2
      solidity/contracts/isms/aggregation/AbstractAggregationIsm.sol
  29. 2
      solidity/contracts/isms/aggregation/StaticAggregationIsm.sol
  30. 0
      solidity/contracts/isms/hook/ERC5164Ism.sol
  31. 2
      solidity/contracts/isms/hook/OPStackIsm.sol
  32. 57
      solidity/contracts/isms/hook/crossChainEnabled/CrossChainEnabled.sol
  33. 7
      solidity/contracts/isms/hook/crossChainEnabled/errors.sol
  34. 50
      solidity/contracts/isms/hook/crossChainEnabled/optimism/CrossChainEnabledOptimism.sol
  35. 40
      solidity/contracts/isms/hook/crossChainEnabled/optimism/LibOptimism.sol
  36. 0
      solidity/contracts/isms/libs/AggregationIsmMetadata.sol
  37. 0
      solidity/contracts/isms/libs/MerkleRootMultisigIsmMetadata.sol
  38. 0
      solidity/contracts/isms/libs/MessageIdMultisigIsmMetadata.sol
  39. 2
      solidity/contracts/isms/multisig/AbstractMerkleRootMultisigIsm.sol
  40. 2
      solidity/contracts/isms/multisig/AbstractMessageIdMultisigIsm.sol
  41. 56
      solidity/contracts/isms/multisig/ValidatorAnnounce.sol
  42. 2
      solidity/contracts/isms/routing/InterchainAccountIsm.sol
  43. 0
      solidity/contracts/libs/Indexed.sol
  44. 35
      solidity/contracts/libs/ValidatorAnnouncements.sol
  45. 12
      solidity/contracts/middleware/InterchainAccountRouter.sol
  46. 9
      solidity/contracts/middleware/InterchainQueryRouter.sol
  47. 2
      solidity/contracts/middleware/libs/Call.sol
  48. 4
      solidity/contracts/middleware/libs/InterchainAccountMessage.sol
  49. 2
      solidity/contracts/middleware/libs/InterchainQueryMessage.sol
  50. 2
      solidity/contracts/middleware/libs/OwnableMulticall.sol
  51. 2
      solidity/contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol
  52. 2
      solidity/contracts/middleware/liquidity-layer/adapters/CircleBridgeAdapter.sol
  53. 2
      solidity/contracts/middleware/liquidity-layer/adapters/PortalAdapter.sol
  54. 0
      solidity/contracts/test/ERC20Test.sol
  55. 0
      solidity/contracts/test/ERC721Test.sol
  56. 22
      solidity/contracts/test/TestGasRouter.sol
  57. 2
      solidity/contracts/test/TestInterchainGasPaymaster.sol
  58. 2
      solidity/contracts/test/TestPostDispatchHook.sol
  59. 2
      solidity/contracts/test/TestQuery.sol
  60. 8
      solidity/contracts/test/TestQuerySender.sol
  61. 9
      solidity/contracts/test/TestRouter.sol
  62. 16
      solidity/contracts/test/bad-recipient/BadRecipient1.sol
  63. 6
      solidity/contracts/test/bad-recipient/BadRecipient2.sol
  64. 17
      solidity/contracts/test/bad-recipient/BadRecipient3.sol
  65. 14
      solidity/contracts/test/bad-recipient/BadRecipient5.sol
  66. 14
      solidity/contracts/test/bad-recipient/BadRecipient6.sol
  67. 7
      solidity/contracts/token/HypERC20.sol
  68. 13
      solidity/contracts/token/HypERC20Collateral.sol
  69. 10
      solidity/contracts/token/HypERC721.sol
  70. 12
      solidity/contracts/token/HypERC721Collateral.sol
  71. 11
      solidity/contracts/token/HypNative.sol
  72. 0
      solidity/contracts/token/README.md
  73. 4
      solidity/contracts/token/extensions/HypERC721URICollateral.sol
  74. 2
      solidity/contracts/token/extensions/HypERC721URIStorage.sol
  75. 2
      solidity/contracts/token/extensions/HypNativeScaled.sol
  76. 2
      solidity/contracts/token/libs/TokenMessage.sol
  77. 16
      solidity/contracts/token/libs/TokenRouter.sol
  78. 21
      solidity/coverage.sh
  79. 4
      solidity/package.json
  80. 4
      solidity/test/InterchainAccountRouter.t.sol
  81. 4
      solidity/test/InterchainQueryRouter.t.sol
  82. 2
      solidity/test/Mailbox.t.sol
  83. 25
      solidity/test/ValidatorAnnounce.t.sol
  84. 4
      solidity/test/hooks/DomainRoutingHook.t.sol
  85. 2
      solidity/test/hooks/StaticProtocolFee.t.sol
  86. 6
      solidity/test/igps/InterchainGasPaymaster.t.sol
  87. 2
      solidity/test/igps/OverheadIgp.t.sol
  88. 2
      solidity/test/igps/gas-oracles/StorageGasOracle.t.sol
  89. 2
      solidity/test/isms/AggregationIsm.t.sol
  90. 2
      solidity/test/isms/ERC5164ISM.t.sol
  91. 2
      solidity/test/isms/MultisigIsm.t.sol
  92. 7
      solidity/test/isms/OPStackIsm.t.sol
  93. 114
      solidity/test/router.test.ts
  94. 73
      solidity/test/token/HypERC20.t.sol
  95. 75
      solidity/test/token/HypERC721.t.sol
  96. 24
      solidity/test/token/HypNativeScaled.t.sol
  97. 24
      typescript/helloworld/contracts/HelloWorld.sol
  98. 1
      typescript/helloworld/package.json
  99. 4
      typescript/helloworld/src/deploy/deploy.ts
  100. 9
      typescript/helloworld/src/scripts/check.ts
  101. Some files were not shown because too many files have changed in this diff Show More

@ -170,13 +170,10 @@ jobs:
#- name: gas
# run: yarn workspace @hyperlane-xyz/core run gas-ci
- name: Core unit tests
- name: Unit tests
run: yarn workspace @hyperlane-xyz/core run test
- name: Token unit tests
run: yarn workspace @hyperlane-xyz/hyperlane-token run test
- name: Run Slither
- name: Static analysis
uses: crytic/slither-action@v0.3.0
id: slither
with:

3
.gitmodules vendored

@ -1,6 +1,3 @@
[submodule "solidity/lib/forge-std"]
path = solidity/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "typescript/token/lib/forge-std"]
path = typescript/token/lib/forge-std
url = https://github.com/foundry-rs/forge-std

@ -14,7 +14,6 @@ COPY .yarn/releases ./.yarn/releases
COPY typescript/utils/package.json ./typescript/utils/
COPY typescript/sdk/package.json ./typescript/sdk/
COPY typescript/helloworld/package.json ./typescript/helloworld/
COPY typescript/token/package.json ./typescript/token/
COPY typescript/infra/package.json ./typescript/infra/
COPY solidity/package.json ./solidity/

@ -0,0 +1,31 @@
comment:
layout: "header, diff, flags, components" # show component info in the PR comment
component_management:
default_rules: # default rules that will be inherited by all components
statuses:
- type: project # in this case every component that doens't have a status defined will have a project type one
target: auto
branches:
- "!main"
individual_components:
- component_id: module_core
name: core
paths:
- solidity/contracts/Mailbox.sol
- component_id: module_hooks
name: hooks
paths:
- solidity/contracts/hooks/**
- component_id: module_isms
name: isms
paths:
- solidity/contracts/isms/**
- component_id: module_token
name: token
paths:
- solidity/contracts/token/**
- component_id: module_middlewares
name: middlewares
paths:
- solidity/contracts/middleware/**

@ -1,5 +1,5 @@
module.exports = {
skipFiles: ['test', 'mock'],
skipFiles: ['test', 'mock', 'upgrade', 'interfaces'],
istanbulReporter: ['lcov'],
mocha: {
enableTimeouts: false,

@ -3,7 +3,7 @@ pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {Versioned} from "./upgrade/Versioned.sol";
import {Indexed} from "./Indexed.sol";
import {Indexed} from "./libs/Indexed.sol";
import {Message} from "./libs/Message.sol";
import {TypeCasts} from "./libs/TypeCasts.sol";
import {IInterchainSecurityModule, ISpecifiesInterchainSecurityModule} from "./interfaces/IInterchainSecurityModule.sol";

@ -2,7 +2,7 @@
pragma solidity >=0.6.11;
import {Router} from "./Router.sol";
import {StandardHookMetadata} from "./libs/hooks/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol";
abstract contract GasRouter is Router {
// ============ Mutable Storage ============
@ -13,6 +13,8 @@ abstract contract GasRouter is Router {
uint256 gas;
}
constructor(address _mailbox) Router(_mailbox) {}
/**
* @notice Sets the gas amount dispatched for each configured domain.
* @param gasConfigs The array of GasRouterConfig structs

@ -98,19 +98,23 @@ abstract contract MailboxClient is OwnableUpgradeable {
return "";
}
function _msgValue(
uint32 /*_destinationDomain*/
) internal view virtual returns (uint256) {
return msg.value;
function _dispatch(
uint32 _destinationDomain,
bytes32 _recipient,
bytes memory _messageBody
) internal virtual returns (bytes32) {
return
_dispatch(_destinationDomain, _recipient, msg.value, _messageBody);
}
function _dispatch(
uint32 _destinationDomain,
bytes32 _recipient,
uint256 _value,
bytes memory _messageBody
) internal virtual returns (bytes32) {
return
mailbox.dispatch{value: _msgValue(_destinationDomain)}(
mailbox.dispatch{value: _value}(
_destinationDomain,
_recipient,
_messageBody,

@ -2,12 +2,12 @@
pragma solidity >=0.6.11;
// ============ Internal Imports ============
import {IMessageRecipient} from "./interfaces/IMessageRecipient.sol";
import {IPostDispatchHook} from "./interfaces/hooks/IPostDispatchHook.sol";
import {IInterchainSecurityModule} from "./interfaces/IInterchainSecurityModule.sol";
import {EnumerableMapExtended} from "./libs/EnumerableMapExtended.sol";
import {MailboxClient} from "./client/MailboxClient.sol";
import {StandardHookMetadata} from "./libs/hooks/StandardHookMetadata.sol";
import {IMessageRecipient} from "../interfaces/IMessageRecipient.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {IInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol";
import {MailboxClient} from "./MailboxClient.sol";
import {EnumerableMapExtended} from "../libs/EnumerableMapExtended.sol";
import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol";
// ============ External Imports ============
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
@ -131,7 +131,7 @@ abstract contract Router is MailboxClient, IMessageRecipient {
require(
contained,
string.concat(
"No router enrolled for domain",
"No router enrolled for domain: ",
Strings.toString(_domain)
)
);
@ -143,8 +143,17 @@ abstract contract Router is MailboxClient, IMessageRecipient {
virtual
returns (bytes32)
{
return _dispatch(_destinationDomain, msg.value, _messageBody);
}
function _dispatch(
uint32 _destinationDomain,
uint256 _value,
bytes memory _messageBody
) internal virtual returns (bytes32) {
bytes32 _router = _mustHaveRemoteRouter(_destinationDomain);
return super._dispatch(_destinationDomain, _router, _messageBody);
return
super._dispatch(_destinationDomain, _router, _value, _messageBody);
}
function _quoteDispatch(

@ -16,9 +16,9 @@ pragma solidity >=0.8.0;
import {MerkleLib} from "../libs/Merkle.sol";
import {Message} from "../libs/Message.sol";
import {MailboxClient} from "../client/MailboxClient.sol";
import {Indexed} from "../Indexed.sol";
import {AbstractPostDispatchHook} from "./AbstractPostDispatchHook.sol";
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {Indexed} from "../libs/Indexed.sol";
import {AbstractPostDispatchHook} from "./libs/AbstractPostDispatchHook.sol";
import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol";
contract MerkleTreeHook is AbstractPostDispatchHook, MailboxClient, Indexed {
using Message for bytes;

@ -14,10 +14,10 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {AbstractMessageIdAuthHook} from "./AbstractMessageIdAuthHook.sol";
import {AbstractMessageIdAuthHook} from "./libs/AbstractMessageIdAuthHook.sol";
import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol";
import {TypeCasts} from "../libs/TypeCasts.sol";
import {Message} from "../libs/Message.sol";
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
// ============ External Imports ============

@ -13,8 +13,8 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {AbstractPostDispatchHook} from "./AbstractPostDispatchHook.sol";
import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol";
import {AbstractPostDispatchHook} from "./libs/AbstractPostDispatchHook.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";

@ -15,8 +15,9 @@ pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {Message} from "../libs/Message.sol";
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {AbstractPostDispatchHook} from "./AbstractPostDispatchHook.sol";
import {StandardHookMetadata} from "./libs/StandardHookMetadata.sol";
import {AbstractPostDispatchHook} from "./libs/AbstractPostDispatchHook.sol";
// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

@ -14,10 +14,10 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {TypeCasts} from "../libs/TypeCasts.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {IMessageDispatcher} from "../interfaces/hooks/IMessageDispatcher.sol";
import {AbstractMessageIdAuthHook} from "./AbstractMessageIdAuthHook.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
import {IMessageDispatcher} from "../../interfaces/hooks/IMessageDispatcher.sol";
import {AbstractMessageIdAuthHook} from "../libs/AbstractMessageIdAuthHook.sol";
// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

@ -13,9 +13,9 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
import {StandardHookMetadata} from "../../libs/hooks/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../libs/StandardHookMetadata.sol";
import {AbstractPostDispatchHook} from "../libs/AbstractPostDispatchHook.sol";
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
import {AbstractPostDispatchHook} from "../AbstractPostDispatchHook.sol";
import {MetaProxy} from "../../libs/MetaProxy.sol";
contract StaticAggregationHook is AbstractPostDispatchHook {

@ -14,14 +14,13 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {Message} from "../libs/Message.sol";
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {IGasOracle} from "../interfaces/IGasOracle.sol";
import {IInterchainGasPaymaster} from "../interfaces/IInterchainGasPaymaster.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {AbstractPostDispatchHook} from "../hooks/AbstractPostDispatchHook.sol";
import {Indexed} from "../Indexed.sol";
import {Message} from "../../libs/Message.sol";
import {StandardHookMetadata} from "../libs/StandardHookMetadata.sol";
import {IGasOracle} from "../../interfaces/IGasOracle.sol";
import {IInterchainGasPaymaster} from "../../interfaces/IInterchainGasPaymaster.sol";
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
import {AbstractPostDispatchHook} from "../libs/AbstractPostDispatchHook.sol";
import {Indexed} from "../../libs/Indexed.sol";
// ============ External Imports ============
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
@ -153,6 +152,7 @@ contract InterchainGasPaymaster is
);
uint256 _overpayment = msg.value - _requiredPayment;
if (_overpayment > 0) {
require(_refundAddress != address(0), "no refund address");
payable(_refundAddress).sendValue(_overpayment);
}

@ -2,7 +2,8 @@
pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {IInterchainGasPaymaster} from "../interfaces/IInterchainGasPaymaster.sol";
import {IInterchainGasPaymaster} from "../../interfaces/IInterchainGasPaymaster.sol";
// ============ External Imports ============
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

@ -3,6 +3,7 @@ pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {IGasOracle} from "../../interfaces/IGasOracle.sol";
// ============ External Imports ============
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

@ -14,13 +14,12 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {AbstractMessageIdAuthorizedIsm} from "../isms/hook/AbstractMessageIdAuthorizedIsm.sol";
import {AbstractPostDispatchHook} from "./AbstractPostDispatchHook.sol";
import {TypeCasts} from "../libs/TypeCasts.sol";
import {Message} from "../libs/Message.sol";
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {MailboxClient} from "../client/MailboxClient.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {AbstractMessageIdAuthorizedIsm} from "../../isms/hook/AbstractMessageIdAuthorizedIsm.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {Message} from "../../libs/Message.sol";
import {StandardHookMetadata} from "./StandardHookMetadata.sol";
import {MailboxClient} from "../../client/MailboxClient.sol";
/**
* @title AbstractMessageIdAuthHook
@ -44,10 +43,10 @@ abstract contract AbstractMessageIdAuthHook is
// ============ Constructor ============
constructor(
address mailbox,
address _mailbox,
uint32 _destinationDomain,
address _ism
) MailboxClient(mailbox) {
) MailboxClient(_mailbox) {
require(_ism != address(0), "AbstractMessageIdAuthHook: invalid ISM");
require(
_destinationDomain != 0,

@ -14,8 +14,8 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {StandardHookMetadata} from "./StandardHookMetadata.sol";
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
/**
* @title AbstractPostDispatch

@ -13,8 +13,8 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
import {Message} from "../libs/Message.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {Message} from "../../libs/Message.sol";
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
import {DomainRoutingHook} from "./DomainRoutingHook.sol";
contract DestinationRecipientRoutingHook is DomainRoutingHook {

@ -14,11 +14,11 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {Message} from "../libs/Message.sol";
import {StandardHookMetadata} from "../libs/hooks/StandardHookMetadata.sol";
import {MailboxClient} from "../client/MailboxClient.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {AbstractPostDispatchHook} from "./AbstractPostDispatchHook.sol";
import {StandardHookMetadata} from "../libs/StandardHookMetadata.sol";
import {AbstractPostDispatchHook} from "../libs/AbstractPostDispatchHook.sol";
import {MailboxClient} from "../../client/MailboxClient.sol";
import {Message} from "../../libs/Message.sol";
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
// ============ External Imports ============
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";

@ -13,10 +13,10 @@ pragma solidity >=0.8.0;
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {IMailbox} from "../interfaces/IMailbox.sol";
import {IPostDispatchHook} from "../../interfaces/hooks/IPostDispatchHook.sol";
import {IMailbox} from "../../interfaces/IMailbox.sol";
import {DomainRoutingHook} from "./DomainRoutingHook.sol";
import {Message} from "../libs/Message.sol";
import {Message} from "../../libs/Message.sol";
/**
* @title FallbackDomainRoutingHook

@ -2,12 +2,6 @@
pragma solidity >=0.6.11;
interface IValidatorAnnounce {
/// @notice Returns the local domain for validator announcements
function localDomain() external view returns (uint32);
/// @notice Returns the mailbox contract for validator announcements
function mailbox() external view returns (address);
/// @notice Returns a list of validators that have made announcements
function getAnnouncedValidators() external view returns (address[] memory);

@ -1,50 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
import {OwnableMulticall} from "../../OwnableMulticall.sol";
import {CallLib} from "../../libs/Call.sol";
interface IInterchainAccountRouter {
function callRemote(
uint32 _destination,
address _to,
uint256 _value,
bytes calldata _data
) external returns (bytes32);
function callRemote(uint32 _destination, CallLib.Call[] calldata calls)
external
returns (bytes32);
function callRemoteWithOverrides(
uint32 _destination,
bytes32 _router,
bytes32 _ism,
CallLib.Call[] calldata calls
) external returns (bytes32);
function getLocalInterchainAccount(
uint32 _origin,
bytes32 _router,
bytes32 _owner,
address _ism
) external view returns (OwnableMulticall);
function getLocalInterchainAccount(
uint32 _origin,
address _router,
address _owner,
address _ism
) external view returns (OwnableMulticall);
function getRemoteInterchainAccount(
address _router,
address _owner,
address _ism
) external view returns (address);
function getRemoteInterchainAccount(uint32 _destination, address _owner)
external
view
returns (address);
}

@ -1,18 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
import {CallLib} from "../../libs/Call.sol";
interface IInterchainQueryRouter {
function query(
uint32 _destination,
address _to,
bytes memory _data,
bytes memory _callback
) external returns (bytes32);
function query(
uint32 _destination,
CallLib.StaticCallWithCallback[] calldata calls
) external returns (bytes32);
}

@ -7,7 +7,7 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
// ============ Internal Imports ============
import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol";
import {IAggregationIsm} from "../../interfaces/isms/IAggregationIsm.sol";
import {AggregationIsmMetadata} from "../../libs/isms/AggregationIsmMetadata.sol";
import {AggregationIsmMetadata} from "../../isms/libs/AggregationIsmMetadata.sol";
/**
* @title AggregationIsm

@ -3,7 +3,7 @@ pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {AbstractAggregationIsm} from "./AbstractAggregationIsm.sol";
import {AggregationIsmMetadata} from "../../libs/isms/AggregationIsmMetadata.sol";
import {AggregationIsmMetadata} from "../../isms/libs/AggregationIsmMetadata.sol";
import {MetaProxy} from "../../libs/MetaProxy.sol";
/**

@ -19,9 +19,9 @@ import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityMod
import {Message} from "../../libs/Message.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {AbstractMessageIdAuthorizedIsm} from "./AbstractMessageIdAuthorizedIsm.sol";
import {CrossChainEnabledOptimism} from "./crossChainEnabled/optimism/CrossChainEnabledOptimism.sol";
// ============ External Imports ============
import {CrossChainEnabledOptimism} from "@openzeppelin/contracts/crosschain/optimism/CrossChainEnabledOptimism.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
/**

@ -1,57 +0,0 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (crosschain/CrossChainEnabled.sol)
pragma solidity ^0.8.4;
import "./errors.sol";
/**
* @author OpenZeppelin
* @dev LibOptimism was removed from the OpenZeppelin Contracts library as of v4.7.0
* @dev Provides information for building cross-chain aware contracts. This
* abstract contract provides accessors and modifiers to control the execution
* flow when receiving cross-chain messages.
*
* Actual implementations of cross-chain aware contracts, which are based on
* this abstraction, will have to inherit from a bridge-specific
* specialization. Such specializations are provided under
* `crosschain/<chain>/CrossChainEnabled<chain>.sol`.
*
* _Available since v4.6._
*/
abstract contract CrossChainEnabled {
/**
* @dev Throws if the current function call is not the result of a
* cross-chain execution.
*/
modifier onlyCrossChain() {
if (!_isCrossChain()) revert NotCrossChainCall();
_;
}
/**
* @dev Throws if the current function call is not the result of a
* cross-chain execution initiated by `account`.
*/
modifier onlyCrossChainSender(address expected) {
address actual = _crossChainSender();
if (expected != actual)
revert InvalidCrossChainSender(actual, expected);
_;
}
/**
* @dev Returns whether the current function call is the result of a
* cross-chain message.
*/
function _isCrossChain() internal view virtual returns (bool);
/**
* @dev Returns the address of the sender of the cross-chain message that
* triggered the current function call.
*
* IMPORTANT: Should revert with `NotCrossChainCall` if the current function
* call is not the result of a cross-chain message.
*/
function _crossChainSender() internal view virtual returns (address);
}

@ -1,7 +0,0 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (crosschain/errors.sol)
pragma solidity ^0.8.4;
error NotCrossChainCall();
error InvalidCrossChainSender(address actual, address expected);

@ -1,50 +0,0 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/optimism/CrossChainEnabledOptimism.sol)
pragma solidity ^0.8.4;
import {CrossChainEnabled} from "../CrossChainEnabled.sol";
import {LibOptimism} from "./LibOptimism.sol";
/**
* @author OpenZeppelin
* @dev CrossChainEnabledOptimism was removed from the OpenZeppelin Contracts library as of v4.7.0
* @dev https://www.optimism.io/[Optimism] specialization or the
* {CrossChainEnabled} abstraction.
*
* The messenger (`CrossDomainMessenger`) contract is provided and maintained by
* the optimism team. You can find the address of this contract on mainnet and
* kovan in the https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts/deployments[deployments section of Optimism monorepo].
*
* _Available since v4.6._
*/
abstract contract CrossChainEnabledOptimism is CrossChainEnabled {
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
address private immutable _messenger;
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(address messenger) {
_messenger = messenger;
}
/**
* @dev see {CrossChainEnabled-_isCrossChain}
*/
function _isCrossChain() internal view virtual override returns (bool) {
return LibOptimism.isCrossChain(_messenger);
}
/**
* @dev see {CrossChainEnabled-_crossChainSender}
*/
function _crossChainSender()
internal
view
virtual
override
onlyCrossChain
returns (address)
{
return LibOptimism.crossChainSender(_messenger);
}
}

@ -1,40 +0,0 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (crosschain/optimism/LibOptimism.sol)
pragma solidity ^0.8.4;
import {ICrossDomainMessenger} from "../../../../interfaces/optimism/ICrossDomainMessenger.sol";
import {NotCrossChainCall} from "../errors.sol";
/**
* @dev Primitives for cross-chain aware contracts for https://www.optimism.io/[Optimism].
* See the https://community.optimism.io/docs/developers/bridge/messaging/#accessing-msg-sender[documentation]
* for the functionality used here.
*/
library LibOptimism {
/**
* @dev Returns whether the current function call is the result of a
* cross-chain message relayed by `messenger`.
*/
function isCrossChain(address messenger) internal view returns (bool) {
return msg.sender == messenger;
}
/**
* @dev Returns the address of the sender that triggered the current
* cross-chain message through `messenger`.
*
* NOTE: {isCrossChain} should be checked before trying to recover the
* sender, as it will revert with `NotCrossChainCall` if the current
* function call is not the result of a cross-chain message.
*/
function crossChainSender(address messenger)
internal
view
returns (address)
{
if (!isCrossChain(messenger)) revert NotCrossChainCall();
return ICrossDomainMessenger(messenger).xDomainMessageSender();
}
}

@ -4,7 +4,7 @@ pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol";
import {AbstractMultisigIsm} from "./AbstractMultisigIsm.sol";
import {MerkleRootMultisigIsmMetadata} from "../../libs/isms/MerkleRootMultisigIsmMetadata.sol";
import {MerkleRootMultisigIsmMetadata} from "../../isms/libs/MerkleRootMultisigIsmMetadata.sol";
import {Message} from "../../libs/Message.sol";
import {MerkleLib} from "../../libs/Merkle.sol";
import {CheckpointLib} from "../../libs/CheckpointLib.sol";

@ -4,7 +4,7 @@ pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol";
import {AbstractMultisigIsm} from "./AbstractMultisigIsm.sol";
import {MessageIdMultisigIsmMetadata} from "../../libs/isms/MessageIdMultisigIsmMetadata.sol";
import {MessageIdMultisigIsmMetadata} from "../libs/MessageIdMultisigIsmMetadata.sol";
import {Message} from "../../libs/Message.sol";
import {CheckpointLib} from "../../libs/CheckpointLib.sol";

@ -2,10 +2,11 @@
pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {IValidatorAnnounce} from "./interfaces/IValidatorAnnounce.sol";
import {IMailbox} from "./interfaces/IMailbox.sol";
import {TypeCasts} from "./libs/TypeCasts.sol";
import {ValidatorAnnouncements} from "./libs/ValidatorAnnouncements.sol";
import {IValidatorAnnounce} from "../../interfaces/IValidatorAnnounce.sol";
import {IMailbox} from "../../interfaces/IMailbox.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {MailboxClient} from "../../client/MailboxClient.sol";
// ============ External Imports ============
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
@ -14,19 +15,12 @@ import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
* @title ValidatorAnnounce
* @notice Stores the location(s) of validator signed checkpoints
*/
contract ValidatorAnnounce is IValidatorAnnounce {
contract ValidatorAnnounce is MailboxClient, IValidatorAnnounce {
// ============ Libraries ============
using EnumerableSet for EnumerableSet.AddressSet;
using TypeCasts for address;
// ============ Constants ============
// Address of the mailbox being validated
address public immutable mailbox;
// Domain of chain on which the contract is deployed
uint32 public immutable localDomain;
// ============ Public Storage ============
// The set of validators that have announced
@ -51,10 +45,7 @@ contract ValidatorAnnounce is IValidatorAnnounce {
// ============ Constructor ============
constructor(address _mailbox) {
mailbox = _mailbox;
localDomain = IMailbox(mailbox).localDomain();
}
constructor(address _mailbox) MailboxClient(_mailbox) {}
// ============ External Functions ============
@ -79,8 +70,7 @@ contract ValidatorAnnounce is IValidatorAnnounce {
replayProtection[_replayId] = true;
// Verify that the signature matches the declared validator
bytes32 _announcementDigest = ValidatorAnnouncements
.getAnnouncementDigest(mailbox, localDomain, _storageLocation);
bytes32 _announcementDigest = getAnnouncementDigest(_storageLocation);
address _signer = ECDSA.recover(_announcementDigest, _signature);
require(_signer == _validator, "!signature");
@ -114,4 +104,34 @@ contract ValidatorAnnounce is IValidatorAnnounce {
function getAnnouncedValidators() external view returns (address[] memory) {
return validators.values();
}
/**
* @notice Returns the digest validators are expected to sign when signing announcements.
* @param _storageLocation Storage location string.
* @return The digest of the announcement.
*/
function getAnnouncementDigest(string memory _storageLocation)
public
view
returns (bytes32)
{
return
ECDSA.toEthSignedMessageHash(
keccak256(abi.encodePacked(_domainHash(), _storageLocation))
);
}
/**
* @notice Returns the domain separator used in validator announcements.
*/
function _domainHash() internal view returns (bytes32) {
return
keccak256(
abi.encodePacked(
localDomain,
address(mailbox).addressToBytes32(),
"HYPERLANE_ANNOUNCEMENT"
)
);
}
}

@ -5,7 +5,7 @@ import {AbstractRoutingIsm} from "./AbstractRoutingIsm.sol";
import {IMailbox} from "../../interfaces/IMailbox.sol";
import {IInterchainSecurityModule} from "../../interfaces/IInterchainSecurityModule.sol";
import {Message} from "../../libs/Message.sol";
import {InterchainAccountMessage} from "../../libs/middleware/InterchainAccountMessage.sol";
import {InterchainAccountMessage} from "../../middleware/libs/InterchainAccountMessage.sol";
/**
* @title InterchainAccountIsm

@ -1,35 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {TypeCasts} from "./TypeCasts.sol";
// ============ External Imports ============
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
library ValidatorAnnouncements {
using TypeCasts for address;
/**
* @notice Returns the digest validators are expected to sign when signing announcements.
* @param _mailbox Address of the mailbox being validated
* @param _localDomain Domain of chain on which the contract is deployed
* @param _storageLocation Storage location string.
* @return The digest of the announcement.
*/
function getAnnouncementDigest(
address _mailbox,
uint32 _localDomain,
string memory _storageLocation
) internal pure returns (bytes32) {
bytes32 _domainHash = keccak256(
abi.encodePacked(
_localDomain,
_mailbox.addressToBytes32(),
"HYPERLANE_ANNOUNCEMENT"
)
);
return
ECDSA.toEthSignedMessageHash(
keccak256(abi.encodePacked(_domainHash, _storageLocation))
);
}
}

@ -2,15 +2,13 @@
pragma solidity ^0.8.13;
// ============ Internal Imports ============
import {OwnableMulticall} from "../OwnableMulticall.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {IInterchainAccountRouter} from "../interfaces/middleware/IInterchainAccountRouter.sol";
import {InterchainAccountMessage} from "../libs/middleware/InterchainAccountMessage.sol";
import {OwnableMulticall} from "./libs/OwnableMulticall.sol";
import {InterchainAccountMessage} from "./libs/InterchainAccountMessage.sol";
import {CallLib} from "./libs/Call.sol";
import {MinimalProxy} from "../libs/MinimalProxy.sol";
import {CallLib} from "../libs/Call.sol";
import {TypeCasts} from "../libs/TypeCasts.sol";
import {EnumerableMapExtended} from "../libs/EnumerableMapExtended.sol";
import {Router, MailboxClient} from "../Router.sol";
import {Router} from "../client/Router.sol";
// ============ External Imports ============
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
@ -21,7 +19,7 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini
* @title A contract that allows accounts on chain A to call contracts via a
* proxy contract on chain B.
*/
contract InterchainAccountRouter is Router, IInterchainAccountRouter {
contract InterchainAccountRouter is Router {
// ============ Libraries ============
using TypeCasts for address;

@ -2,10 +2,9 @@
pragma solidity ^0.8.13;
// ============ Internal Imports ============
import {CallLib} from "../libs/Call.sol";
import {Router} from "../Router.sol";
import {IInterchainQueryRouter} from "../interfaces/middleware/IInterchainQueryRouter.sol";
import {InterchainQueryMessage} from "../libs/middleware/InterchainQueryMessage.sol";
import {Router} from "../client/Router.sol";
import {CallLib} from "./libs/Call.sol";
import {InterchainQueryMessage} from "./libs/InterchainQueryMessage.sol";
import {TypeCasts} from "../libs/TypeCasts.sol";
// ============ External Imports ============
@ -17,7 +16,7 @@ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Ini
* @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, IInterchainQueryRouter {
contract InterchainQueryRouter is Router {
using TypeCasts for address;
using TypeCasts for bytes32;
using InterchainQueryMessage for bytes;

@ -3,7 +3,7 @@ pragma solidity ^0.8.13;
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {TypeCasts} from "./TypeCasts.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
library CallLib {
struct StaticCall {

@ -1,8 +1,8 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {CallLib} from "../Call.sol";
import {TypeCasts} from "../TypeCasts.sol";
import {CallLib} from "./Call.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
/**
* Format of message:

@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import {CallLib} from "../Call.sol";
import {CallLib} from "./Call.sol";
/**
* Format of message:

@ -2,7 +2,7 @@
pragma solidity ^0.8.13;
// ============ Internal Imports ============
import {CallLib} from "./libs/Call.sol";
import {CallLib} from "./Call.sol";
/*
* @title OwnableMulticall

@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import {Router} from "../../Router.sol";
import {Router} from "../../client/Router.sol";
import {ILiquidityLayerRouter} from "../../interfaces/ILiquidityLayerRouter.sol";
import {ICircleMessageTransmitter} from "./interfaces/circle/ICircleMessageTransmitter.sol";

@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import {Router} from "../../../Router.sol";
import {Router} from "../../../client/Router.sol";
import {ITokenMessenger} from "../interfaces/circle/ITokenMessenger.sol";
import {ICircleMessageTransmitter} from "../interfaces/circle/ICircleMessageTransmitter.sol";

@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import {Router} from "../../../Router.sol";
import {Router} from "../../../client/Router.sol";
import {IPortalTokenBridge} from "../interfaces/portal/IPortalTokenBridge.sol";
import {ILiquidityLayerAdapter} from "../interfaces/ILiquidityLayerAdapter.sol";

@ -1,18 +1,18 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
import "./TestRouter.sol";
import "../GasRouter.sol";
import "../client/GasRouter.sol";
contract TestGasRouter is TestRouter, GasRouter {
constructor(address _mailbox) TestRouter(_mailbox) {}
contract TestGasRouter is GasRouter {
constructor(address _mailbox) GasRouter(_mailbox) {}
function _metadata(uint32 _destination)
internal
view
override(GasRouter, MailboxClient)
returns (bytes memory)
{
return GasRouter._metadata(_destination);
function dispatch(uint32 _destination, bytes memory _msg) external payable {
_dispatch(_destination, _msg);
}
function _handle(
uint32,
bytes32,
bytes calldata
) internal pure override {}
}

@ -2,7 +2,7 @@
pragma solidity >=0.8.0;
// ============ Internal Imports ============
import {InterchainGasPaymaster} from "../igps/InterchainGasPaymaster.sol";
import {InterchainGasPaymaster} from "../hooks/igp/InterchainGasPaymaster.sol";
contract TestInterchainGasPaymaster is InterchainGasPaymaster {
uint256 public constant gasPrice = 10;

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {AbstractPostDispatchHook} from "../hooks/AbstractPostDispatchHook.sol";
import {AbstractPostDispatchHook} from "../hooks/libs/AbstractPostDispatchHook.sol";
contract TestPostDispatchHook is AbstractPostDispatchHook {
// ============ Public Storage ============

@ -3,7 +3,7 @@ pragma solidity ^0.8.13;
import {InterchainQueryRouter} from "../middleware/InterchainQueryRouter.sol";
import {TypeCasts} from "../libs/TypeCasts.sol";
import {CallLib} from "../libs/Call.sol";
import {CallLib} from "../middleware/libs/Call.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

@ -1,11 +1,11 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IInterchainQueryRouter} from "../interfaces/middleware/IInterchainQueryRouter.sol";
import {CallLib} from "../libs/Call.sol";
import {InterchainQueryRouter} from "../middleware/InterchainQueryRouter.sol";
import {CallLib} from "../middleware/libs/Call.sol";
contract TestQuerySender {
IInterchainQueryRouter queryRouter;
InterchainQueryRouter queryRouter;
address public lastAddressResult;
uint256 public lastUint256Result;
@ -16,7 +16,7 @@ contract TestQuerySender {
event ReceivedBytes32Result(bytes32 result);
function initialize(address _queryRouterAddress) external {
queryRouter = IInterchainQueryRouter(_queryRouterAddress);
queryRouter = InterchainQueryRouter(_queryRouterAddress);
}
function queryAddress(

@ -1,13 +1,20 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;
import "../Router.sol";
import "../client/Router.sol";
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,

@ -1,16 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IMessageRecipient} from "../../interfaces/IMessageRecipient.sol";
contract BadRecipient1 is IMessageRecipient {
function handle(
uint32,
bytes32,
bytes calldata
) external payable override {
assembly {
revert(0, 0)
}
}
}

@ -1,6 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
contract BadRecipient2 {
function handle(uint32, bytes32) external pure {} // solhint-disable-line no-empty-blocks
}

@ -1,17 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IMessageRecipient} from "../../interfaces/IMessageRecipient.sol";
contract BadRecipient3 is IMessageRecipient {
function handle(
uint32,
bytes32,
bytes calldata
) external payable override {
assembly {
mstore(0, 0xabcdef)
revert(0, 32)
}
}
}

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IMessageRecipient} from "../../interfaces/IMessageRecipient.sol";
contract BadRecipient5 is IMessageRecipient {
function handle(
uint32,
bytes32,
bytes calldata
) external payable override {
require(false, "no can do");
}
}

@ -1,14 +0,0 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
import {IMessageRecipient} from "../../interfaces/IMessageRecipient.sol";
contract BadRecipient6 is IMessageRecipient {
function handle(
uint32,
bytes32,
bytes calldata
) external payable override {
require(false); // solhint-disable-line reason-string
}
}

@ -13,26 +13,21 @@ import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/
contract HypERC20 is ERC20Upgradeable, TokenRouter {
uint8 private immutable _decimals;
constructor(uint8 __decimals) {
constructor(uint8 __decimals, address _mailbox) TokenRouter(_mailbox) {
_decimals = __decimals;
}
/**
* @notice Initializes the Hyperlane router, ERC20 metadata, and mints initial supply to deployer.
* @param _mailbox The address of the mailbox contract.
* @param _totalSupply The initial supply of the token.
* @param _name The name of the token.
* @param _symbol The symbol of the token.
*/
function initialize(
address _mailbox,
uint256 _totalSupply,
string memory _name,
string memory _symbol
) external initializer {
// initialize router
__Router_initialize(_mailbox);
// Initialize ERC20 metadata
__ERC20_init(_name, _symbol);
_mint(msg.sender, _totalSupply);

@ -2,7 +2,8 @@
pragma solidity >=0.8.0;
import {TokenRouter} from "./libs/TokenRouter.sol";
import {Message} from "./libs/Message.sol";
import {TokenMessage} from "./libs/TokenMessage.sol";
import {MailboxClient} from "../client/MailboxClient.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
@ -20,18 +21,10 @@ contract HypERC20Collateral is TokenRouter {
* @notice Constructor
* @param erc20 Address of the token to keep as collateral
*/
constructor(address erc20) {
constructor(address erc20, address _mailbox) TokenRouter(_mailbox) {
wrappedToken = IERC20(erc20);
}
/**
* @notice Initializes the Hyperlane router.
* @param _mailbox The address of the mailbox contract.
*/
function initialize(address _mailbox) external initializer {
__Router_initialize(_mailbox);
}
function balanceOf(address _account)
external
view

@ -12,25 +12,25 @@ import {ERC721EnumerableUpgradeable} from "@openzeppelin/contracts-upgradeable/t
* @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 _mailbox The address of the mailbox contract.
* @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(
address _mailbox,
uint256 _mintAmount,
string memory _name,
string memory _symbol
) external initializer {
// transfers ownership to `msg.sender`
__Router_initialize(_mailbox);
address owner = msg.sender;
_transferOwnership(owner);
__ERC721_init(_name, _symbol);
for (uint256 i = 0; i < _mintAmount; i++) {
_safeMint(msg.sender, i);
_safeMint(owner, i);
}
}

@ -2,7 +2,7 @@
pragma solidity >=0.8.0;
import {TokenRouter} from "./libs/TokenRouter.sol";
import {Message} from "./libs/Message.sol";
import {TokenMessage} from "./libs/TokenMessage.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
@ -17,7 +17,7 @@ contract HypERC721Collateral is TokenRouter {
* @notice Constructor
* @param erc721 Address of the token to keep as collateral
*/
constructor(address erc721) {
constructor(address erc721, address _mailbox) TokenRouter(_mailbox) {
wrappedToken = IERC721(erc721);
}
@ -25,14 +25,6 @@ contract HypERC721Collateral is TokenRouter {
return IERC721(wrappedToken).ownerOf(_tokenId);
}
/**
* @notice Initializes the Hyperlane router.
* @param _mailbox The address of the mailbox contract.
*/
function initialize(address _mailbox) external initializer {
__Router_initialize(_mailbox);
}
/**
* @dev Returns the balance of `_account` for `wrappedToken`.
* @inheritdoc TokenRouter

@ -2,7 +2,7 @@
pragma solidity >=0.8.0;
import {TokenRouter} from "./libs/TokenRouter.sol";
import {Message} from "./libs/Message.sol";
import {TokenMessage} from "./libs/TokenMessage.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
/**
@ -18,14 +18,7 @@ contract HypNative is TokenRouter {
*/
event Donation(address indexed sender, uint256 amount);
/**
* @notice Initializes the Hyperlane router, ERC20 metadata, and mints initial supply to deployer.
* @param _mailbox The address of the mailbox contract.
*/
function initialize(address _mailbox) external initializer {
// transfers ownership to `msg.sender`
__Router_initialize(_mailbox);
}
constructor(address _mailbox) TokenRouter(_mailbox) {}
/**
* @inheritdoc TokenRouter

@ -11,7 +11,9 @@ import {IERC721MetadataUpgradeable} from "@openzeppelin/contracts-upgradeable/to
*/
contract HypERC721URICollateral is HypERC721Collateral {
// solhint-disable-next-line no-empty-blocks
constructor(address erc721) HypERC721Collateral(erc721) {}
constructor(address erc721, address _mailbox)
HypERC721Collateral(erc721, _mailbox)
{}
/**
* @dev Transfers `_tokenId` of `wrappedToken` from `msg.sender` to this contract.

@ -12,6 +12,8 @@ import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC72
* @author Abacus Works
*/
contract HypERC721URIStorage is HypERC721, ERC721URIStorageUpgradeable {
constructor(address _mailbox) HypERC721(_mailbox) {}
function balanceOf(address account)
public
view

@ -13,7 +13,7 @@ import {TokenRouter} from "../libs/TokenRouter.sol";
contract HypNativeScaled is HypNative {
uint256 public immutable scale;
constructor(uint256 _scale) {
constructor(uint256 _scale, address _mailbox) HypNative(_mailbox) {
scale = _scale;
}

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;
library Message {
library TokenMessage {
function format(
bytes32 _recipient,
uint256 _amount,

@ -1,9 +1,10 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;
import {GasRouter} from "@hyperlane-xyz/core/contracts/GasRouter.sol";
import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol";
import {Message} from "./Message.sol";
import {GasRouter} from "../../client/GasRouter.sol";
import {MailboxClient} from "../../client/MailboxClient.sol";
import {TypeCasts} from "../../libs/TypeCasts.sol";
import {TokenMessage} from "./TokenMessage.sol";
/**
* @title Hyperlane Token Router that extends Router with abstract token (ERC20/ERC721) remote transfer functionality.
@ -12,7 +13,7 @@ import {Message} from "./Message.sol";
abstract contract TokenRouter is GasRouter {
using TypeCasts for bytes32;
using TypeCasts for address;
using Message for bytes;
using TokenMessage for bytes;
/**
* @dev Emitted on `transferRemote` when a transfer message is dispatched.
@ -38,6 +39,8 @@ abstract contract TokenRouter is GasRouter {
uint256 amount
);
constructor(address _mailbox) GasRouter(_mailbox) {}
/**
* @notice Transfers `_amountOrId` token to `_recipient` on `_destination` domain.
* @dev Delegates transfer logic to `_transferFromSender` implementation.
@ -73,11 +76,10 @@ abstract contract TokenRouter is GasRouter {
uint256 _gasPayment
) internal returns (bytes32 messageId) {
bytes memory metadata = _transferFromSender(_amountOrId);
messageId = _dispatchWithGas(
messageId = _dispatch(
_destination,
Message.format(_recipient, _amountOrId, metadata),
_gasPayment,
msg.sender // refund address
TokenMessage.format(_recipient, _amountOrId, metadata)
);
emit SentTransferRemote(_destination, _recipient, _amountOrId);
}

@ -0,0 +1,21 @@
# generates lcov.info
forge coverage --report lcov
if ! command -v lcov &>/dev/null; then
echo "lcov is not installed. Installing..."
sudo apt-get install lcov
fi
lcov --version
# forge does not instrument libraries https://github.com/foundry-rs/foundry/issues/4854
EXCLUDE="*test* *mock* *node_modules* $(grep -r 'library' contracts -l)"
lcov --rc lcov_branch_coverage=1 \
--output-file forge-pruned-lcov.info \
--remove lcov.info $EXCLUDE
if [ "$CI" != "true" ]; then
genhtml --rc lcov_branch_coverage=1 \
--output-directory coverage forge-pruned-lcov.info \
&& open coverage/index.html
fi

@ -47,8 +47,8 @@
"scripts": {
"build": "hardhat compile && tsc",
"lint": "solhint contracts/**/*.sol",
"clean": "hardhat clean && rm -rf ./dist ./cache ./types",
"coverage": "hardhat coverage && forge coverage --report lcov",
"clean": "hardhat clean && rm -rf ./dist ./cache ./types ./coverage",
"coverage": "./coverage.sh",
"docs": "forge doc",
"prettier": "prettier --write ./contracts ./test",
"test": "hardhat test && forge test -vvv",

@ -8,10 +8,8 @@ import "../contracts/mock/MockHyperlaneEnvironment.sol";
import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
import {IInterchainSecurityModule} from "../contracts/interfaces/IInterchainSecurityModule.sol";
import {IInterchainGasPaymaster} from "../contracts/interfaces/IInterchainGasPaymaster.sol";
import {InterchainAccountRouter} from "../contracts/middleware/InterchainAccountRouter.sol";
import {CallLib, OwnableMulticall, InterchainAccountRouter} from "../contracts/middleware/InterchainAccountRouter.sol";
import {InterchainAccountIsm} from "../contracts/isms/routing/InterchainAccountIsm.sol";
import {OwnableMulticall} from "../contracts/OwnableMulticall.sol";
import {CallLib} from "../contracts/libs/Call.sol";
contract Callable {
mapping(address => bytes32) public data;

@ -2,8 +2,7 @@
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import {InterchainQueryRouter} from "../contracts/middleware/InterchainQueryRouter.sol";
import {IInterchainQueryRouter} from "../contracts/interfaces/middleware/IInterchainQueryRouter.sol";
import {CallLib, InterchainQueryRouter} from "../contracts/middleware/InterchainQueryRouter.sol";
import {MockHyperlaneEnvironment} from "../contracts/mock/MockHyperlaneEnvironment.sol";
import {MockToken} from "../contracts/mock/MockToken.sol";
@ -11,7 +10,6 @@ import {PausableHook} from "../contracts/hooks/PausableHook.sol";
import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
import "../contracts/test/TestRecipient.sol";
import {CallLib} from "../contracts/libs/Call.sol";
contract InterchainQueryRouterTest is Test {
using TypeCasts for address;

@ -10,7 +10,7 @@ import "../contracts/test/TestIsm.sol";
import "../contracts/test/TestRecipient.sol";
import "../contracts/hooks/MerkleTreeHook.sol";
import {StandardHookMetadata} from "../contracts/libs/hooks/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../contracts/hooks/libs/StandardHookMetadata.sol";
import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
contract MailboxTest is Test, Versioned {

@ -3,9 +3,8 @@ pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../contracts/mock/MockMailbox.sol";
import "../contracts/ValidatorAnnounce.sol";
import "../contracts/isms/multisig/ValidatorAnnounce.sol";
import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
import {ValidatorAnnouncements} from "../contracts/libs/ValidatorAnnouncements.sol";
contract ValidatorAnnounceTest is Test {
using TypeCasts for address;
@ -25,18 +24,17 @@ contract ValidatorAnnounceTest is Test {
valAnnounce = new ValidatorAnnounce(address(mailbox));
}
function announce(uint256 privateKey, string memory storageLocation)
internal
{
bytes32 digest = ValidatorAnnouncements.getAnnouncementDigest(
address(mailbox),
localDomain,
storageLocation
);
function announce(
uint256 privateKey,
string memory storageLocation,
bool shouldRevert
) internal {
bytes32 digest = valAnnounce.getAnnouncementDigest(storageLocation);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
bytes memory signature = abi.encodePacked(r, s, v);
address validator = vm.addr(privateKey);
if (shouldRevert) vm.expectRevert("replay");
valAnnounce.announce(validator, storageLocation, signature);
}
@ -61,7 +59,7 @@ contract ValidatorAnnounceTest is Test {
string memory storageLocation1 = "s3://test-bucket/us-east-1";
vm.expectEmit(true, false, false, true, address(valAnnounce));
emit ValidatorAnnouncement(validator, storageLocation1);
announce(privateKey, storageLocation1);
announce(privateKey, storageLocation1, false);
address[] memory expectedValidators = new address[](1);
expectedValidators[0] = validator;
@ -80,14 +78,13 @@ contract ValidatorAnnounceTest is Test {
);
// Shouldn't be able to announce the same location twice
vm.expectRevert("replay");
announce(privateKey, storageLocation1);
announce(privateKey, storageLocation1, true);
// Announce a second location
string memory storageLocation2 = "s3://test-bucket-2/us-east-1";
vm.expectEmit(true, false, false, true, address(valAnnounce));
emit ValidatorAnnouncement(validator, storageLocation2);
announce(privateKey, storageLocation2);
announce(privateKey, storageLocation2, false);
assertEqAddrArr(
valAnnounce.getAnnouncedValidators(),
expectedValidators

@ -4,8 +4,8 @@ pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {DomainRoutingHook} from "../../contracts/hooks/DomainRoutingHook.sol";
import {FallbackDomainRoutingHook} from "../../contracts/hooks/FallbackDomainRoutingHook.sol";
import {DomainRoutingHook} from "../../contracts/hooks/routing/DomainRoutingHook.sol";
import {FallbackDomainRoutingHook} from "../../contracts/hooks/routing/FallbackDomainRoutingHook.sol";
import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";

@ -4,7 +4,7 @@ pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {MessageUtils} from "../isms/IsmTestUtils.sol";
import {StandardHookMetadata} from "../../contracts/libs/hooks/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol";
import {StaticProtocolFee} from "../../contracts/hooks/StaticProtocolFee.sol";

@ -3,12 +3,12 @@ pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {StandardHookMetadata} from "../../contracts/libs/hooks/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol";
import {Message} from "../../contracts/libs/Message.sol";
import {MessageUtils} from "../isms/IsmTestUtils.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {InterchainGasPaymaster} from "../../contracts/igps/InterchainGasPaymaster.sol";
import {StorageGasOracle} from "../../contracts/igps/gas-oracles/StorageGasOracle.sol";
import {InterchainGasPaymaster} from "../../contracts/hooks/igp/InterchainGasPaymaster.sol";
import {StorageGasOracle} from "../../contracts/hooks/igp/StorageGasOracle.sol";
import {IGasOracle} from "../../contracts/interfaces/IGasOracle.sol";
contract InterchainGasPaymasterTest is Test {

@ -2,7 +2,7 @@
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {OverheadIgp} from "../../contracts/igps/OverheadIgp.sol";
import {OverheadIgp} from "../../contracts/hooks/igp/OverheadIgp.sol";
import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol";
contract OverheadIgpTest is Test {

@ -2,7 +2,7 @@
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {StorageGasOracle} from "../../../contracts/igps/gas-oracles/StorageGasOracle.sol";
import {StorageGasOracle} from "../../../contracts/hooks/igp/StorageGasOracle.sol";
import {IGasOracle} from "../../../contracts/interfaces/IGasOracle.sol";
contract StorageGasOracleTest is Test {

@ -5,7 +5,7 @@ import "forge-std/Test.sol";
import {IAggregationIsm} from "../../contracts/interfaces/isms/IAggregationIsm.sol";
import {StaticAggregationIsmFactory} from "../../contracts/isms/aggregation/StaticAggregationIsmFactory.sol";
import {AggregationIsmMetadata} from "../../contracts/libs/isms/AggregationIsmMetadata.sol";
import {AggregationIsmMetadata} from "../../contracts/isms/libs/AggregationIsmMetadata.sol";
import {TestIsm, MOfNTestUtils} from "./IsmTestUtils.sol";
contract AggregationIsmTest is Test {

@ -9,7 +9,7 @@ import {MessageUtils} from "./IsmTestUtils.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {IMessageDispatcher} from "../../contracts/interfaces/hooks/IMessageDispatcher.sol";
import {ERC5164Hook} from "../../contracts/hooks/ERC5164Hook.sol";
import {ERC5164Hook} from "../../contracts/hooks/aggregation/ERC5164Hook.sol";
import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol";
import {ERC5164Ism} from "../../contracts/isms/hook/ERC5164Ism.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";

@ -6,7 +6,7 @@ import "forge-std/Test.sol";
import {IMultisigIsm} from "../../contracts/interfaces/isms/IMultisigIsm.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";
import {StaticMerkleRootMultisigIsmFactory, StaticMessageIdMultisigIsmFactory} from "../../contracts/isms/multisig/StaticMultisigIsm.sol";
import {MerkleRootMultisigIsmMetadata} from "../../contracts/libs/isms/MerkleRootMultisigIsmMetadata.sol";
import {MerkleRootMultisigIsmMetadata} from "../../contracts/isms/libs/MerkleRootMultisigIsmMetadata.sol";
import {CheckpointLib} from "../../contracts/libs/CheckpointLib.sol";
import {StaticMOfNAddressSetFactory} from "../../contracts/libs/StaticMOfNAddressSetFactory.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";

@ -5,8 +5,8 @@ import {Test} from "forge-std/Test.sol";
import {LibBit} from "../../contracts/libs/LibBit.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {StandardHookMetadata} from "../../contracts/libs/hooks/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../../contracts/libs/hooks/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol";
import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol";
import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";
import {Message} from "../../contracts/libs/Message.sol";
@ -15,7 +15,8 @@ import {TestMultisigIsm} from "../../contracts/test/TestMultisigIsm.sol";
import {OPStackIsm} from "../../contracts/isms/hook/OPStackIsm.sol";
import {OPStackHook} from "../../contracts/hooks/OPStackHook.sol";
import {TestRecipient} from "../../contracts/test/TestRecipient.sol";
import {NotCrossChainCall} from "../../contracts/isms/hook/crossChainEnabled/errors.sol";
import {NotCrossChainCall} from "@openzeppelin/contracts/crosschain/errors.sol";
import {AddressAliasHelper} from "@eth-optimism/contracts/standards/AddressAliasHelper.sol";
import {ICrossDomainMessenger, IL2CrossDomainMessenger} from "../../contracts/interfaces/optimism/ICrossDomainMessenger.sol";

@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { expect } from 'chai';
import { BigNumberish, ContractTransaction } from 'ethers';
import { BigNumberish } from 'ethers';
import { ethers } from 'hardhat';
import { addressToBytes32 } from '@hyperlane-xyz/utils';
@ -9,6 +9,7 @@ import { addressToBytes32 } from '@hyperlane-xyz/utils';
import {
TestInterchainGasPaymaster,
TestInterchainGasPaymaster__factory,
TestIsm,
TestIsm__factory,
TestMailbox,
TestMailbox__factory,
@ -23,19 +24,11 @@ const destination = 2;
const destinationWithoutRouter = 3;
const body = '0xdeadbeef';
interface GasPaymentParams {
// The amount of destination gas being paid for
gasAmount: BigNumberish;
// The amount of native tokens paid
payment: BigNumberish;
refundAddress: string;
}
// TODO: update for v3
describe.skip('Router', async () => {
describe('Router', async () => {
let router: TestRouter,
mailbox: TestMailbox,
igp: TestInterchainGasPaymaster,
defaultIsm: TestIsm,
signer: SignerWithAddress,
nonOwner: SignerWithAddress;
@ -46,46 +39,43 @@ describe.skip('Router', async () => {
beforeEach(async () => {
const mailboxFactory = new TestMailbox__factory(signer);
mailbox = await mailboxFactory.deploy(origin);
igp = await new TestInterchainGasPaymaster__factory(signer).deploy(
nonOwner.address,
);
igp = await new TestInterchainGasPaymaster__factory(signer).deploy();
const requiredHook = await new TestMerkleTreeHook__factory(signer).deploy(
mailbox.address,
);
const defaultIsm = await new TestIsm__factory(signer).deploy();
defaultIsm = await new TestIsm__factory(signer).deploy();
await mailbox.initialize(
signer.address,
defaultIsm.address,
igp.address,
requiredHook.address,
);
router = await new TestRouter__factory(signer).deploy();
router = await new TestRouter__factory(signer).deploy(mailbox.address);
await router.initialize(igp.address, defaultIsm.address);
});
describe('#initialize', () => {
it('should set the mailbox', async () => {
await router.initialize(mailbox.address);
expect(await router.mailbox()).to.equal(mailbox.address);
it('should set the hook', async () => {
expect(await router.hook()).to.equal(igp.address);
});
it('should set the ism', async () => {
expect(await router.interchainSecurityModule()).to.equal(
defaultIsm.address,
);
});
it('should transfer owner to deployer', async () => {
await router.initialize(mailbox.address);
expect(await router.owner()).to.equal(signer.address);
});
it('cannot be initialized twice', async () => {
await router.initialize(mailbox.address);
await expect(router.initialize(mailbox.address)).to.be.revertedWith(
'Initializable: contract is already initialized',
);
await expect(
router.initialize(mailbox.address, defaultIsm.address),
).to.be.revertedWith('Initializable: contract is already initialized');
});
});
describe('when initialized', () => {
beforeEach(async () => {
await router.initialize(mailbox.address);
});
it('accepts message from enrolled mailbox and router', async () => {
const sender = addressToBytes32(nonOwner.address);
await router.enrollRemoteRouter(origin, sender);
@ -97,7 +87,7 @@ describe.skip('Router', async () => {
it('rejects message from unenrolled mailbox', async () => {
await expect(
router.handle(origin, addressToBytes32(nonOwner.address), body),
).to.be.revertedWith('!mailbox');
).to.be.revertedWith('MailboxClient: sender not mailbox');
});
it('rejects message from unenrolled router', async () => {
@ -105,9 +95,7 @@ describe.skip('Router', async () => {
const recipient = addressToBytes32(router.address);
await expect(
mailbox.testHandle(origin, sender, recipient, body),
).to.be.revertedWith(
`No router enrolled for domain. Did you specify the right domain ID?`,
);
).to.be.revertedWith(`No router enrolled for domain: ${origin}`);
});
it('owner can enroll remote router', async () => {
@ -115,7 +103,7 @@ describe.skip('Router', async () => {
const remoteBytes = addressToBytes32(nonOwner.address);
expect(await router.isRemoteRouter(origin, remoteBytes)).to.equal(false);
await expect(router.mustHaveRemoteRouter(origin)).to.be.revertedWith(
`No router enrolled for domain. Did you specify the right domain ID?`,
`No router enrolled for domain: ${origin}`,
);
await router.enrollRemoteRouter(origin, addressToBytes32(remote));
expect(await router.isRemoteRouter(origin, remoteBytes)).to.equal(true);
@ -127,7 +115,7 @@ describe.skip('Router', async () => {
const remoteBytes = addressToBytes32(nonOwner.address);
expect(await router.isRemoteRouter(origin, remoteBytes)).to.equal(false);
await expect(router.mustHaveRemoteRouter(origin)).to.be.revertedWith(
`No router enrolled for domain. Did you specify the right domain ID?`,
`No router enrolled for domain: ${origin}`,
);
await router.enrollRemoteRouters([origin], [addressToBytes32(remote)]);
expect(await router.isRemoteRouter(origin, remoteBytes)).to.equal(true);
@ -155,7 +143,7 @@ describe.skip('Router', async () => {
).to.be.revertedWith(ONLY_OWNER_REVERT_MSG);
});
describe('dispatch functions', () => {
describe('#dispatch', () => {
let payment: BigNumberish;
beforeEach(async () => {
@ -165,11 +153,14 @@ describe.skip('Router', async () => {
destination,
addressToBytes32(nonOwner.address),
);
const recipient = utils.addressToBytes32(router.address);
payment = await mailbox.quoteDispatch(destination, recipient, body);
const recipient = addressToBytes32(router.address);
payment = await mailbox['quoteDispatch(uint32,bytes32,bytes)'](
destination,
recipient,
body,
);
});
describe('#dispatch', () => {
it('dispatches a message', async () => {
await expect(
router.dispatch(destination, body, { value: payment }),
@ -186,51 +177,8 @@ describe.skip('Router', async () => {
await expect(
router.dispatch(destinationWithoutRouter, body),
).to.be.revertedWith(
`No router enrolled for domain. Did you specify the right domain ID?`,
`No router enrolled for domain: ${destinationWithoutRouter}`,
);
});
});
describe('#dispatchWithGas', () => {
const testGasPaymentParams = {
gasAmount: 4321,
payment: 43210,
refundAddress: '0xc0ffee0000000000000000000000000000000000',
};
it('dispatches a message', async () => {
await expect(
router.dispatchWithGas(
destination,
body,
testGasPaymentParams.gasAmount,
testGasPaymentParams.payment,
testGasPaymentParams.refundAddress,
{ value: testGasPaymentParams.payment },
),
).to.emit(mailbox, 'Dispatch');
});
it('uses custom igp metadata', async () => {
const tx = await router.dispatchWithGas(
destination,
body,
testGasPaymentParams.gasAmount,
testGasPaymentParams.payment,
testGasPaymentParams.refundAddress,
{ value: testGasPaymentParams.payment },
);
const messageId = await mailbox.latestDispatchedId();
const required = await igp.quoteGasPayment(
destination,
testGasPaymentParams.gasAmount,
);
expect(tx)
.to.emit(igp, 'GasPayment')
.withArgs(messageId, testGasPaymentParams.gasAmount, required);
});
});
});
});
});

@ -15,17 +15,17 @@ pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol";
import {TestMailbox} from "@hyperlane-xyz/core/contracts/test/TestMailbox.sol";
import {TestPostDispatchHook} from "@hyperlane-xyz/core/contracts/test/TestPostDispatchHook.sol";
import {TestInterchainGasPaymaster} from "@hyperlane-xyz/core/contracts/test/TestInterchainGasPaymaster.sol";
import {GasRouter} from "@hyperlane-xyz/core/contracts/GasRouter.sol";
import {ERC20Test} from "../contracts/test/ERC20Test.sol";
import {HypERC20} from "../contracts/HypERC20.sol";
import {HypERC20Collateral} from "../contracts/HypERC20Collateral.sol";
import {HypNative} from "../contracts/HypNative.sol";
import {TokenRouter} from "../contracts/libs/TokenRouter.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";
import {ERC20Test} from "../../contracts/test/ERC20Test.sol";
import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol";
import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol";
import {GasRouter} from "../../contracts/client/GasRouter.sol";
import {HypERC20} from "../../contracts/token/HypERC20.sol";
import {HypERC20Collateral} from "../../contracts/token/HypERC20Collateral.sol";
import {HypNative} from "../../contracts/token/HypNative.sol";
import {TokenRouter} from "../../contracts/token/libs/TokenRouter.sol";
abstract contract HypTokenTest is Test {
using TypeCasts for address;
@ -35,7 +35,6 @@ abstract contract HypTokenTest is Test {
uint256 internal constant TOTAL_SUPPLY = 1_000_000e18;
uint256 internal REQUIRED_VALUE; // initialized in setUp
uint256 internal constant GAS_LIMIT = 10_000;
uint256 internal IGP_GAS_PRICE; // initialized in test
uint256 internal constant TRANSFER_AMT = 100e18;
string internal constant NAME = "HyperlaneInu";
string internal constant SYMBOL = "HYP";
@ -74,13 +73,8 @@ abstract contract HypTokenTest is Test {
REQUIRED_VALUE = noopHook.quoteDispatch("", "");
remoteToken = new HypERC20(DECIMALS);
remoteToken.initialize(
address(remoteMailbox),
TOTAL_SUPPLY,
NAME,
SYMBOL
);
remoteToken = new HypERC20(DECIMALS, address(remoteMailbox));
remoteToken.initialize(TOTAL_SUPPLY, NAME, SYMBOL);
remoteToken.enrollRemoteRouter(
ORIGIN,
address(localToken).addressToBytes32()
@ -110,8 +104,7 @@ abstract contract HypTokenTest is Test {
}
function _setCustomGasConfig() internal {
localMailbox.setDefaultHook(address(igp));
IGP_GAS_PRICE = igp.gasPrice();
localToken.setHook(address(igp));
TokenRouter.GasRouterConfig[]
memory config = new TokenRouter.GasRouterConfig[](1);
@ -180,15 +173,10 @@ contract HypERC20Test is HypTokenTest {
function setUp() public override {
super.setUp();
localToken = new HypERC20(DECIMALS);
localToken = new HypERC20(DECIMALS, address(localMailbox));
erc20Token = HypERC20(address(localToken));
erc20Token.initialize(
address(localMailbox),
TOTAL_SUPPLY,
NAME,
SYMBOL
);
erc20Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL);
erc20Token.enrollRemoteRouter(
DESTINATION,
@ -201,7 +189,7 @@ contract HypERC20Test is HypTokenTest {
function testInitialize_revert_ifAlreadyInitialized() public {
vm.expectRevert("Initializable: contract is already initialized");
erc20Token.initialize(ALICE, TOTAL_SUPPLY, NAME, SYMBOL);
erc20Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL);
}
function testTotalSupply() public {
@ -245,7 +233,7 @@ contract HypERC20Test is HypTokenTest {
_performRemoteTransferAndGas(
REQUIRED_VALUE,
TRANSFER_AMT,
GAS_LIMIT * IGP_GAS_PRICE
GAS_LIMIT * igp.gasPrice()
);
assertEq(erc20Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT);
}
@ -258,12 +246,11 @@ contract HypERC20CollateralTest is HypTokenTest {
function setUp() public override {
super.setUp();
localToken = new HypERC20Collateral(address(primaryToken));
erc20Collateral = HypERC20Collateral(address(localToken));
HypERC20Collateral(address(localToken)).initialize(
localToken = new HypERC20Collateral(
address(primaryToken),
address(localMailbox)
);
erc20Collateral = HypERC20Collateral(address(localToken));
erc20Collateral.enrollRemoteRouter(
DESTINATION,
@ -276,10 +263,7 @@ contract HypERC20CollateralTest is HypTokenTest {
_enrollRemoteTokenRouter();
}
function testInitialize_revert_ifAlreadyInitialized() public {
vm.expectRevert("Initializable: contract is already initialized");
erc20Collateral.initialize(ALICE);
}
function testInitialize_revert_ifAlreadyInitialized() public {}
function testRemoteTransfer() public {
uint256 balanceBefore = localToken.balanceOf(ALICE);
@ -306,7 +290,7 @@ contract HypERC20CollateralTest is HypTokenTest {
_performRemoteTransferAndGas(
REQUIRED_VALUE,
TRANSFER_AMT,
GAS_LIMIT * IGP_GAS_PRICE
GAS_LIMIT * igp.gasPrice()
);
assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT);
}
@ -319,11 +303,9 @@ contract HypNativeTest is HypTokenTest {
function setUp() public override {
super.setUp();
localToken = new HypNative();
localToken = new HypNative(address(localMailbox));
nativeToken = HypNative(payable(address(localToken)));
nativeToken.initialize(address(localMailbox));
nativeToken.enrollRemoteRouter(
DESTINATION,
address(remoteToken).addressToBytes32()
@ -335,10 +317,7 @@ contract HypNativeTest is HypTokenTest {
_enrollRemoteTokenRouter();
}
function testInitialize_revert_ifAlreadyInitialized() public {
vm.expectRevert("Initializable: contract is already initialized");
nativeToken.initialize(ALICE);
}
function testInitialize_revert_ifAlreadyInitialized() public {}
function testRemoteTransfer() public {
_performRemoteTransferWithEmit(
@ -363,7 +342,7 @@ contract HypNativeTest is HypTokenTest {
_performRemoteTransferAndGas(
REQUIRED_VALUE,
TRANSFER_AMT,
TRANSFER_AMT + GAS_LIMIT * IGP_GAS_PRICE
TRANSFER_AMT + GAS_LIMIT * igp.gasPrice()
);
}
}

@ -15,19 +15,18 @@ pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {TestMailbox} from "@hyperlane-xyz/core/contracts/test/TestMailbox.sol";
import {TestPostDispatchHook} from "@hyperlane-xyz/core/contracts/test/TestPostDispatchHook.sol";
import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol";
import {TestMailbox} from "../../contracts/test/TestMailbox.sol";
import {TestPostDispatchHook} from "../../contracts/test/TestPostDispatchHook.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {ERC721Test} from "../contracts/test/ERC721Test.sol";
import {TokenRouter} from "../contracts/libs/TokenRouter.sol";
import {HypERC721} from "../contracts/HypERC721.sol";
import {HypERC721Collateral} from "../contracts/HypERC721Collateral.sol";
import {HypERC721URIStorage} from "../contracts/extensions/HypERC721URIStorage.sol";
import {HypERC721URICollateral} from "../contracts/extensions/HypERC721URICollateral.sol";
import {ERC721Test} from "../../contracts/test/ERC721Test.sol";
import {TokenRouter} from "../../contracts/token/libs/TokenRouter.sol";
import {HypERC721} from "../../contracts/token/HypERC721.sol";
import {HypERC721Collateral} from "../../contracts/token/HypERC721Collateral.sol";
import {HypERC721URIStorage} from "../../contracts/token/extensions/HypERC721URIStorage.sol";
import {HypERC721URICollateral} from "../../contracts/token/extensions/HypERC721URICollateral.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";
import {ERC721URIStorageUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721URIStorageUpgradeable.sol";
abstract contract HypTokenTest is Test, IERC721Receiver {
@ -70,13 +69,16 @@ abstract contract HypTokenTest is Test, IERC721Receiver {
remoteMailbox = new TestMailbox(DESTINATION);
remoteToken = new HypERC721Collateral(address(remotePrimaryToken));
remoteToken = new HypERC721Collateral(
address(remotePrimaryToken),
address(remoteMailbox)
);
}
function _deployRemoteToken(bool isCollateral) internal {
if (isCollateral) {
remoteToken = new HypERC721Collateral(address(remotePrimaryToken));
HypERC721Collateral(address(remoteToken)).initialize(
remoteToken = new HypERC721Collateral(
address(remotePrimaryToken),
address(remoteMailbox)
);
remotePrimaryToken.transferFrom(
@ -85,13 +87,9 @@ abstract contract HypTokenTest is Test, IERC721Receiver {
0
); // need for processing messages
} else {
remoteToken = new HypERC721();
HypERC721(address(remoteToken)).initialize(
address(remoteMailbox),
0,
NAME,
SYMBOL
);
HypERC721 erc721 = new HypERC721(address(remoteMailbox));
erc721.initialize(0, NAME, SYMBOL);
remoteToken = TokenRouter(address(erc721));
}
remoteToken.enrollRemoteRouter(
ORIGIN,
@ -152,10 +150,10 @@ contract HypERC721Test is HypTokenTest {
function setUp() public virtual override {
super.setUp();
localToken = new HypERC721();
localToken = new HypERC721(address(localMailbox));
hyp721 = HypERC721(address(localToken));
hyp721.initialize(address(localMailbox), INITIAL_SUPPLY, NAME, SYMBOL);
hyp721.initialize(INITIAL_SUPPLY, NAME, SYMBOL);
hyp721.enrollRemoteRouter(
DESTINATION,
@ -165,7 +163,7 @@ contract HypERC721Test is HypTokenTest {
function testInitialize_revert_ifAlreadyInitialized() public {
vm.expectRevert("Initializable: contract is already initialized");
hyp721.initialize(address(localMailbox), INITIAL_SUPPLY, NAME, SYMBOL);
hyp721.initialize(INITIAL_SUPPLY, NAME, SYMBOL);
}
function testTotalSupply() public {
@ -211,6 +209,8 @@ contract HypERC721Test is HypTokenTest {
}
contract MockHypERC721URIStorage is HypERC721URIStorage {
constructor(address mailbox) HypERC721URIStorage(mailbox) {}
function setTokenURI(uint256 tokenId, string memory uri) public {
_setTokenURI(tokenId, uri);
}
@ -224,15 +224,10 @@ contract HypERC721URIStorageTest is HypTokenTest {
function setUp() public override {
super.setUp();
localToken = new MockHypERC721URIStorage();
localToken = new MockHypERC721URIStorage(address(localMailbox));
hyp721Storage = MockHypERC721URIStorage(address(localToken));
hyp721Storage.initialize(
address(localMailbox),
INITIAL_SUPPLY,
NAME,
SYMBOL
);
hyp721Storage.initialize(INITIAL_SUPPLY, NAME, SYMBOL);
hyp721Storage.setTokenURI(0, URI);
hyp721Storage.enrollRemoteRouter(
DESTINATION,
@ -258,11 +253,12 @@ contract HypERC721CollateralTest is HypTokenTest {
function setUp() public override {
super.setUp();
localToken = new HypERC721Collateral(address(localPrimaryToken));
localToken = new HypERC721Collateral(
address(localPrimaryToken),
address(localMailbox)
);
hyp721Collateral = HypERC721Collateral(address(localToken));
hyp721Collateral.initialize(address(localMailbox));
hyp721Collateral.enrollRemoteRouter(
DESTINATION,
address(remoteToken).addressToBytes32()
@ -275,10 +271,7 @@ contract HypERC721CollateralTest is HypTokenTest {
);
}
function testInitialize_revert_ifAlreadyInitialized() public {
vm.expectRevert("Initializable: contract is already initialized");
hyp721Collateral.initialize(ALICE);
}
function testInitialize_revert_ifAlreadyInitialized() public {}
function testRemoteTransfer(bool isCollateral) public {
localPrimaryToken.approve(address(hyp721Collateral), 0);
@ -321,10 +314,12 @@ contract HypERC721CollateralURIStorageTest is HypTokenTest {
function setUp() public override {
super.setUp();
localToken = new HypERC721URICollateral(address(localPrimaryToken));
localToken = new HypERC721URICollateral(
address(localPrimaryToken),
address(localMailbox)
);
hyp721URICollateral = HypERC721URICollateral(address(localToken));
hyp721URICollateral.initialize(address(localMailbox));
hyp721URICollateral.enrollRemoteRouter(
DESTINATION,
address(remoteToken).addressToBytes32()

@ -3,10 +3,10 @@ pragma solidity >=0.8.0;
import "forge-std/Test.sol";
import {HypNativeScaled} from "../contracts/extensions/HypNativeScaled.sol";
import {HypERC20} from "../contracts/HypERC20.sol";
import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol";
import {MockHyperlaneEnvironment} from "@hyperlane-xyz/core/contracts/mock/MockHyperlaneEnvironment.sol";
import {HypNativeScaled} from "../../contracts/token/extensions/HypNativeScaled.sol";
import {HypERC20} from "../../contracts/token/HypERC20.sol";
import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {MockHyperlaneEnvironment} from "../../contracts/mock/MockHyperlaneEnvironment.sol";
contract HypNativeScaledTest is Test {
uint32 nativeDomain = 1;
@ -37,16 +37,16 @@ contract HypNativeScaledTest is Test {
function setUp() public {
environment = new MockHyperlaneEnvironment(synthDomain, nativeDomain);
synth = new HypERC20(decimals);
synth.initialize(
address(environment.mailboxes(synthDomain)),
mintAmount * (10**decimals),
"Zebec BSC Token",
"ZBC"
synth = new HypERC20(
decimals,
address(environment.mailboxes(synthDomain))
);
synth.initialize(mintAmount * (10**decimals), "Zebec BSC Token", "ZBC");
native = new HypNativeScaled(scale);
native.initialize(address(environment.mailboxes(nativeDomain)));
native = new HypNativeScaled(
scale,
address(environment.mailboxes(nativeDomain))
);
native.enrollRemoteRouter(
synthDomain,

@ -2,7 +2,8 @@
pragma solidity ^0.8.13;
// ============ External Imports ============
import {Router} from "@hyperlane-xyz/core/contracts/Router.sol";
import {Router} from "@hyperlane-xyz/core/contracts/client/Router.sol";
import {StandardHookMetadata} from "@hyperlane-xyz/core/contracts/hooks/libs/StandardHookMetadata.sol";
/*
* @title The Hello World App
@ -42,13 +43,10 @@ contract HelloWorld is Router {
uint256 handleGasAmount
);
constructor(address _mailbox, address _interchainGasPaymaster) {
constructor(address _mailbox, address _hook) Router(_mailbox) {
// Transfer ownership of the contract to deployer
_transferOwnership(msg.sender);
// Set the addresses for the Mailbox and IGP
// Alternatively, this could be done later in an initialize method
_setMailbox(_mailbox);
_setInterchainGasPaymaster(_interchainGasPaymaster);
setHook(_hook);
}
// ============ External functions ============
@ -65,13 +63,7 @@ contract HelloWorld is Router {
{
sent += 1;
sentTo[_destinationDomain] += 1;
_dispatchWithGas(
_destinationDomain,
bytes(_message),
HANDLE_GAS_AMOUNT,
msg.value,
msg.sender
);
_dispatch(_destinationDomain, bytes(_message));
emit SentHelloWorld(
mailbox.localDomain(),
_destinationDomain,
@ -80,6 +72,12 @@ contract HelloWorld is Router {
}
// ============ Internal functions ============
function _metadata(
uint32 /*_destinationDomain*/
) internal view override returns (bytes memory) {
return
StandardHookMetadata.formatMetadata(HANDLE_GAS_AMOUNT, msg.sender);
}
/**
* @notice Handles a message from a remote router.

@ -3,6 +3,7 @@
"description": "A basic skeleton of an Hyperlane app",
"version": "1.5.0",
"dependencies": {
"@hyperlane-xyz/core": "1.5.0",
"@hyperlane-xyz/sdk": "1.5.0",
"@openzeppelin/contracts-upgradeable": "^4.8.0",
"ethers": "^5.7.2"

@ -1,3 +1,5 @@
import { ethers } from 'ethers';
import {
ChainName,
HyperlaneContracts,
@ -31,7 +33,7 @@ export class HelloWorldDeployer extends HyperlaneRouterDeployer<
async deployContracts(chain: ChainName, config: HelloWorldConfig) {
const router = await this.deployContract(chain, 'router', [
config.mailbox,
config.interchainGasPaymaster,
config.hook ?? ethers.constants.AddressZero,
]);
return {
router,

@ -1,9 +1,7 @@
import {
HyperlaneCore,
HyperlaneIgp,
MultiProvider,
attachContractsMap,
createRouterConfigMap,
} from '@hyperlane-xyz/sdk';
import { HelloWorldApp } from '../app/app';
@ -27,13 +25,8 @@ async function check() {
);
const core = HyperlaneCore.fromEnvironment('testnet', multiProvider);
const igp = HyperlaneIgp.fromEnvironment('testnet', multiProvider);
const app = new HelloWorldApp(core, contractsMap, multiProvider);
const config = createRouterConfigMap(
ownerAddress,
core.contractsMap,
igp.contractsMap,
);
const config = core.getRouterConfig(ownerAddress);
console.info('Starting check');
const helloWorldChecker = new HelloWorldChecker(multiProvider, app, config);

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save