@hyperlane-xyz/core 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/contracts/AbacusConnectionClient.sol +141 -0
  2. package/contracts/OwnableMulticall.sol +60 -0
  3. package/contracts/Router.sol +194 -0
  4. package/contracts/middleware/InterchainAccountRouter.sol +93 -0
  5. package/contracts/middleware/InterchainQueryRouter.sol +110 -0
  6. package/contracts/middleware/README.md +13 -0
  7. package/contracts/test/TestAbacusConnectionClient.sol +23 -0
  8. package/contracts/test/TestQuery.sol +39 -0
  9. package/contracts/test/TestRouter.sol +47 -0
  10. package/dist/contracts/AbacusConnectionClient.d.ts +169 -0
  11. package/dist/contracts/AbacusConnectionClient.d.ts.map +1 -0
  12. package/dist/contracts/AbacusConnectionClient.js +4 -0
  13. package/dist/contracts/AbacusConnectionClient.js.map +1 -0
  14. package/dist/contracts/OwnableMulticall.d.ts +123 -0
  15. package/dist/contracts/OwnableMulticall.d.ts.map +1 -0
  16. package/dist/contracts/OwnableMulticall.js +4 -0
  17. package/dist/contracts/OwnableMulticall.js.map +1 -0
  18. package/dist/contracts/Router.d.ts +222 -0
  19. package/dist/contracts/Router.d.ts.map +1 -0
  20. package/dist/contracts/Router.js +4 -0
  21. package/dist/contracts/Router.js.map +1 -0
  22. package/dist/contracts/index.d.ts +5 -0
  23. package/dist/contracts/index.d.ts.map +1 -1
  24. package/dist/contracts/middleware/InterchainAccountRouter.d.ts +301 -0
  25. package/dist/contracts/middleware/InterchainAccountRouter.d.ts.map +1 -0
  26. package/dist/contracts/middleware/InterchainAccountRouter.js +4 -0
  27. package/dist/contracts/middleware/InterchainAccountRouter.js.map +1 -0
  28. package/dist/contracts/middleware/InterchainQueryRouter.d.ts +333 -0
  29. package/dist/contracts/middleware/InterchainQueryRouter.d.ts.map +1 -0
  30. package/dist/contracts/middleware/InterchainQueryRouter.js +4 -0
  31. package/dist/contracts/middleware/InterchainQueryRouter.js.map +1 -0
  32. package/dist/contracts/middleware/index.d.ts +3 -0
  33. package/dist/contracts/middleware/index.d.ts.map +1 -0
  34. package/dist/contracts/middleware/index.js +3 -0
  35. package/dist/contracts/middleware/index.js.map +1 -0
  36. package/dist/contracts/test/TestAbacusConnectionClient.d.ts +209 -0
  37. package/dist/contracts/test/TestAbacusConnectionClient.d.ts.map +1 -0
  38. package/dist/contracts/test/TestAbacusConnectionClient.js +4 -0
  39. package/dist/contracts/test/TestAbacusConnectionClient.js.map +1 -0
  40. package/dist/contracts/test/TestQuery.d.ts +87 -0
  41. package/dist/contracts/test/TestQuery.d.ts.map +1 -0
  42. package/dist/contracts/test/TestQuery.js +4 -0
  43. package/dist/contracts/test/TestQuery.js.map +1 -0
  44. package/dist/contracts/test/TestRouter.d.ts +295 -0
  45. package/dist/contracts/test/TestRouter.d.ts.map +1 -0
  46. package/dist/contracts/test/TestRouter.js +4 -0
  47. package/dist/contracts/test/TestRouter.js.map +1 -0
  48. package/dist/contracts/test/index.d.ts +3 -0
  49. package/dist/contracts/test/index.d.ts.map +1 -1
  50. package/dist/factories/contracts/AbacusConnectionClient__factory.d.ts +43 -0
  51. package/dist/factories/contracts/AbacusConnectionClient__factory.d.ts.map +1 -0
  52. package/dist/factories/contracts/AbacusConnectionClient__factory.js +161 -0
  53. package/dist/factories/contracts/AbacusConnectionClient__factory.js.map +1 -0
  54. package/dist/factories/contracts/OwnableMulticall__factory.d.ts +78 -0
  55. package/dist/factories/contracts/OwnableMulticall__factory.d.ts.map +1 -0
  56. package/dist/factories/contracts/OwnableMulticall__factory.js +136 -0
  57. package/dist/factories/contracts/OwnableMulticall__factory.js.map +1 -0
  58. package/dist/factories/contracts/Router__factory.d.ts +36 -0
  59. package/dist/factories/contracts/Router__factory.d.ts.map +1 -0
  60. package/dist/factories/contracts/Router__factory.js +240 -0
  61. package/dist/factories/contracts/Router__factory.js.map +1 -0
  62. package/dist/factories/contracts/index.d.ts +4 -0
  63. package/dist/factories/contracts/index.d.ts.map +1 -1
  64. package/dist/factories/contracts/index.js +8 -1
  65. package/dist/factories/contracts/index.js.map +1 -1
  66. package/dist/factories/contracts/middleware/InterchainAccountRouter__factory.d.ts +69 -0
  67. package/dist/factories/contracts/middleware/InterchainAccountRouter__factory.d.ts.map +1 -0
  68. package/dist/factories/contracts/middleware/InterchainAccountRouter__factory.js +389 -0
  69. package/dist/factories/contracts/middleware/InterchainAccountRouter__factory.js.map +1 -0
  70. package/dist/factories/contracts/middleware/InterchainQueryRouter__factory.d.ts +69 -0
  71. package/dist/factories/contracts/middleware/InterchainQueryRouter__factory.d.ts.map +1 -0
  72. package/dist/factories/contracts/middleware/InterchainQueryRouter__factory.js +438 -0
  73. package/dist/factories/contracts/middleware/InterchainQueryRouter__factory.js.map +1 -0
  74. package/dist/factories/contracts/middleware/index.d.ts +3 -0
  75. package/dist/factories/contracts/middleware/index.d.ts.map +1 -0
  76. package/dist/factories/contracts/middleware/index.js +11 -0
  77. package/dist/factories/contracts/middleware/index.js.map +1 -0
  78. package/dist/factories/contracts/test/TestAbacusConnectionClient__factory.d.ts +48 -0
  79. package/dist/factories/contracts/test/TestAbacusConnectionClient__factory.d.ts.map +1 -0
  80. package/dist/factories/contracts/test/TestAbacusConnectionClient__factory.js +242 -0
  81. package/dist/factories/contracts/test/TestAbacusConnectionClient__factory.js.map +1 -0
  82. package/dist/factories/contracts/test/TestQuery__factory.d.ts +66 -0
  83. package/dist/factories/contracts/test/TestQuery__factory.d.ts.map +1 -0
  84. package/dist/factories/contracts/test/TestQuery__factory.js +120 -0
  85. package/dist/factories/contracts/test/TestQuery__factory.js.map +1 -0
  86. package/dist/factories/contracts/test/TestRouter__factory.d.ts +48 -0
  87. package/dist/factories/contracts/test/TestRouter__factory.d.ts.map +1 -0
  88. package/dist/factories/contracts/test/TestRouter__factory.js +366 -0
  89. package/dist/factories/contracts/test/TestRouter__factory.js.map +1 -0
  90. package/dist/factories/contracts/test/index.d.ts +3 -0
  91. package/dist/factories/contracts/test/index.d.ts.map +1 -1
  92. package/dist/factories/contracts/test/index.js +7 -1
  93. package/dist/factories/contracts/test/index.js.map +1 -1
  94. package/dist/index.d.ts +16 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +18 -1
  97. package/dist/index.js.map +1 -1
  98. package/package.json +2 -2
@@ -0,0 +1,141 @@
1
+ // SPDX-License-Identifier: MIT OR Apache-2.0
2
+ pragma solidity >=0.6.11;
3
+
4
+ // ============ Internal Imports ============
5
+ import {IInterchainGasPaymaster} from "../interfaces/IInterchainGasPaymaster.sol";
6
+ import {IOutbox} from "../interfaces/IOutbox.sol";
7
+ import {IAbacusConnectionManager} from "../interfaces/IAbacusConnectionManager.sol";
8
+
9
+ // ============ External Imports ============
10
+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
11
+
12
+ abstract contract AbacusConnectionClient is OwnableUpgradeable {
13
+ // ============ Mutable Storage ============
14
+
15
+ IAbacusConnectionManager public abacusConnectionManager;
16
+ // Interchain Gas Paymaster contract. The relayer associated with this contract
17
+ // must be willing to relay messages dispatched from the current Outbox contract,
18
+ // otherwise payments made to the paymaster will not result in relayed messages.
19
+ IInterchainGasPaymaster public interchainGasPaymaster;
20
+
21
+ uint256[48] private __GAP; // gap for upgrade safety
22
+
23
+ // ============ Events ============
24
+
25
+ /**
26
+ * @notice Emitted when a new abacusConnectionManager is set.
27
+ * @param abacusConnectionManager The address of the abacusConnectionManager contract
28
+ */
29
+ event AbacusConnectionManagerSet(address indexed abacusConnectionManager);
30
+
31
+ /**
32
+ * @notice Emitted when a new Interchain Gas Paymaster is set.
33
+ * @param interchainGasPaymaster The address of the Interchain Gas Paymaster.
34
+ */
35
+ event InterchainGasPaymasterSet(address indexed interchainGasPaymaster);
36
+
37
+ // ============ Modifiers ============
38
+
39
+ /**
40
+ * @notice Only accept messages from an Abacus Inbox contract
41
+ */
42
+ modifier onlyInbox() {
43
+ require(_isInbox(msg.sender), "!inbox");
44
+ _;
45
+ }
46
+
47
+ // ======== Initializer =========
48
+
49
+ function __AbacusConnectionClient_initialize(
50
+ address _abacusConnectionManager
51
+ ) internal onlyInitializing {
52
+ _setAbacusConnectionManager(_abacusConnectionManager);
53
+ __Ownable_init();
54
+ }
55
+
56
+ function __AbacusConnectionClient_initialize(
57
+ address _abacusConnectionManager,
58
+ address _interchainGasPaymaster
59
+ ) internal onlyInitializing {
60
+ _setInterchainGasPaymaster(_interchainGasPaymaster);
61
+ __AbacusConnectionClient_initialize(_abacusConnectionManager);
62
+ }
63
+
64
+ // ============ External functions ============
65
+
66
+ /**
67
+ * @notice Sets the address of the application's AbacusConnectionManager.
68
+ * @param _abacusConnectionManager The address of the AbacusConnectionManager contract.
69
+ */
70
+ function setAbacusConnectionManager(address _abacusConnectionManager)
71
+ external
72
+ virtual
73
+ onlyOwner
74
+ {
75
+ _setAbacusConnectionManager(_abacusConnectionManager);
76
+ }
77
+
78
+ /**
79
+ * @notice Sets the address of the application's InterchainGasPaymaster.
80
+ * @param _interchainGasPaymaster The address of the InterchainGasPaymaster contract.
81
+ */
82
+ function setInterchainGasPaymaster(address _interchainGasPaymaster)
83
+ external
84
+ virtual
85
+ onlyOwner
86
+ {
87
+ _setInterchainGasPaymaster(_interchainGasPaymaster);
88
+ }
89
+
90
+ // ============ Internal functions ============
91
+
92
+ /**
93
+ * @notice Sets the address of the application's InterchainGasPaymaster.
94
+ * @param _interchainGasPaymaster The address of the InterchainGasPaymaster contract.
95
+ */
96
+ function _setInterchainGasPaymaster(address _interchainGasPaymaster)
97
+ internal
98
+ {
99
+ interchainGasPaymaster = IInterchainGasPaymaster(
100
+ _interchainGasPaymaster
101
+ );
102
+ emit InterchainGasPaymasterSet(_interchainGasPaymaster);
103
+ }
104
+
105
+ /**
106
+ * @notice Modify the contract the Application uses to validate Inbox contracts
107
+ * @param _abacusConnectionManager The address of the abacusConnectionManager contract
108
+ */
109
+ function _setAbacusConnectionManager(address _abacusConnectionManager)
110
+ internal
111
+ {
112
+ abacusConnectionManager = IAbacusConnectionManager(
113
+ _abacusConnectionManager
114
+ );
115
+ emit AbacusConnectionManagerSet(_abacusConnectionManager);
116
+ }
117
+
118
+ /**
119
+ * @notice Get the local Outbox contract from the abacusConnectionManager
120
+ * @return The local Outbox contract
121
+ */
122
+ function _outbox() internal view returns (IOutbox) {
123
+ return abacusConnectionManager.outbox();
124
+ }
125
+
126
+ /**
127
+ * @notice Determine whether _potentialInbox is an enrolled Inbox from the abacusConnectionManager
128
+ * @return True if _potentialInbox is an enrolled Inbox
129
+ */
130
+ function _isInbox(address _potentialInbox) internal view returns (bool) {
131
+ return abacusConnectionManager.isInbox(_potentialInbox);
132
+ }
133
+
134
+ /**
135
+ * @notice Get the local domain from the abacusConnectionManager
136
+ * @return The local domain
137
+ */
138
+ function _localDomain() internal view virtual returns (uint32) {
139
+ return abacusConnectionManager.localDomain();
140
+ }
141
+ }
@@ -0,0 +1,60 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.13;
3
+
4
+ // ============ External Imports ============
5
+
6
+ import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
7
+
8
+ struct Call {
9
+ address to;
10
+ bytes data;
11
+ }
12
+
13
+ /*
14
+ * @title OwnableMulticall
15
+ * @dev Allows only only address to execute calls to other contracts
16
+ */
17
+ contract OwnableMulticall is OwnableUpgradeable {
18
+ constructor() {
19
+ _transferOwnership(msg.sender);
20
+ }
21
+
22
+ function proxyCalls(Call[] calldata calls) external onlyOwner {
23
+ for (uint256 i = 0; i < calls.length; i += 1) {
24
+ (bool success, bytes memory returnData) = calls[i].to.call(
25
+ calls[i].data
26
+ );
27
+ if (!success) {
28
+ assembly {
29
+ revert(add(returnData, 32), returnData)
30
+ }
31
+ }
32
+ }
33
+ }
34
+
35
+ function _call(Call[] memory calls, bytes[] memory callbacks)
36
+ internal
37
+ returns (bytes[] memory resolveCalls)
38
+ {
39
+ resolveCalls = new bytes[](callbacks.length);
40
+ for (uint256 i = 0; i < calls.length; i++) {
41
+ (bool success, bytes memory returnData) = calls[i].to.call(
42
+ calls[i].data
43
+ );
44
+ require(success, "Multicall: call failed");
45
+ resolveCalls[i] = bytes.concat(callbacks[i], returnData);
46
+ }
47
+ }
48
+
49
+ // TODO: deduplicate
50
+ function proxyCallBatch(address to, bytes[] memory calls) internal {
51
+ for (uint256 i = 0; i < calls.length; i += 1) {
52
+ (bool success, bytes memory returnData) = to.call(calls[i]);
53
+ if (!success) {
54
+ assembly {
55
+ revert(add(returnData, 32), returnData)
56
+ }
57
+ }
58
+ }
59
+ }
60
+ }
@@ -0,0 +1,194 @@
1
+ // SPDX-License-Identifier: MIT OR Apache-2.0
2
+ pragma solidity >=0.6.11;
3
+
4
+ // ============ Internal Imports ============
5
+ import {AbacusConnectionClient} from "./AbacusConnectionClient.sol";
6
+ import {IAbacusConnectionManager} from "../interfaces/IAbacusConnectionManager.sol";
7
+ import {IInterchainGasPaymaster} from "../interfaces/IInterchainGasPaymaster.sol";
8
+ import {IMessageRecipient} from "../interfaces/IMessageRecipient.sol";
9
+ import {IOutbox} from "../interfaces/IOutbox.sol";
10
+
11
+ abstract contract Router is AbacusConnectionClient, IMessageRecipient {
12
+ string constant NO_ROUTER_ENROLLED_REVERT_MESSAGE =
13
+ "No router enrolled for domain. Did you specify the right domain ID?";
14
+
15
+ // ============ Mutable Storage ============
16
+
17
+ mapping(uint32 => bytes32) public routers;
18
+ uint256[49] private __GAP; // gap for upgrade safety
19
+
20
+ // ============ Events ============
21
+
22
+ /**
23
+ * @notice Emitted when a router is set.
24
+ * @param domain The domain of the new router
25
+ * @param router The address of the new router
26
+ */
27
+ event RemoteRouterEnrolled(uint32 indexed domain, bytes32 indexed router);
28
+
29
+ // ============ Modifiers ============
30
+ /**
31
+ * @notice Only accept messages from a remote Router contract
32
+ * @param _origin The domain the message is coming from
33
+ * @param _router The address the message is coming from
34
+ */
35
+ modifier onlyRemoteRouter(uint32 _origin, bytes32 _router) {
36
+ require(
37
+ _isRemoteRouter(_origin, _router),
38
+ NO_ROUTER_ENROLLED_REVERT_MESSAGE
39
+ );
40
+ _;
41
+ }
42
+
43
+ // ======== Initializer =========
44
+ function __Router_initialize(address _abacusConnectionManager)
45
+ internal
46
+ onlyInitializing
47
+ {
48
+ __AbacusConnectionClient_initialize(_abacusConnectionManager);
49
+ }
50
+
51
+ function __Router_initialize(
52
+ address _abacusConnectionManager,
53
+ address _interchainGasPaymaster
54
+ ) internal onlyInitializing {
55
+ __AbacusConnectionClient_initialize(
56
+ _abacusConnectionManager,
57
+ _interchainGasPaymaster
58
+ );
59
+ }
60
+
61
+ // ============ External functions ============
62
+
63
+ /**
64
+ * @notice Register the address of a Router contract for the same Application on a remote chain
65
+ * @param _domain The domain of the remote Application Router
66
+ * @param _router The address of the remote Application Router
67
+ */
68
+ function enrollRemoteRouter(uint32 _domain, bytes32 _router)
69
+ external
70
+ virtual
71
+ onlyOwner
72
+ {
73
+ _enrollRemoteRouter(_domain, _router);
74
+ }
75
+
76
+ /**
77
+ * @notice Handles an incoming message
78
+ * @param _origin The origin domain
79
+ * @param _sender The sender address
80
+ * @param _message The message
81
+ */
82
+ function handle(
83
+ uint32 _origin,
84
+ bytes32 _sender,
85
+ bytes calldata _message
86
+ ) external virtual override onlyInbox onlyRemoteRouter(_origin, _sender) {
87
+ // TODO: callbacks on success/failure
88
+ _handle(_origin, _sender, _message);
89
+ }
90
+
91
+ // ============ Virtual functions ============
92
+ function _handle(
93
+ uint32 _origin,
94
+ bytes32 _sender,
95
+ bytes calldata _message
96
+ ) internal virtual;
97
+
98
+ // ============ Internal functions ============
99
+
100
+ /**
101
+ * @notice Set the router for a given domain
102
+ * @param _domain The domain
103
+ * @param _router The new router
104
+ */
105
+ function _enrollRemoteRouter(uint32 _domain, bytes32 _router) internal {
106
+ routers[_domain] = _router;
107
+ emit RemoteRouterEnrolled(_domain, _router);
108
+ }
109
+
110
+ /**
111
+ * @notice Return true if the given domain / router is the address of a remote Application Router
112
+ * @param _domain The domain of the potential remote Application Router
113
+ * @param _router The address of the potential remote Application Router
114
+ */
115
+ function _isRemoteRouter(uint32 _domain, bytes32 _router)
116
+ internal
117
+ view
118
+ returns (bool)
119
+ {
120
+ return routers[_domain] == _router;
121
+ }
122
+
123
+ /**
124
+ * @notice Assert that the given domain has a Application Router registered and return its address
125
+ * @param _domain The domain of the chain for which to get the Application Router
126
+ * @return _router The address of the remote Application Router on _domain
127
+ */
128
+ function _mustHaveRemoteRouter(uint32 _domain)
129
+ internal
130
+ view
131
+ returns (bytes32 _router)
132
+ {
133
+ _router = routers[_domain];
134
+ require(_router != bytes32(0), NO_ROUTER_ENROLLED_REVERT_MESSAGE);
135
+ }
136
+
137
+ /**
138
+ * @notice Dispatches a message to an enrolled router via the local router's Outbox.
139
+ * @notice Does not pay interchain gas.
140
+ * @dev Reverts if there is no enrolled router for _destinationDomain.
141
+ * @param _destinationDomain The domain of the chain to which to send the message.
142
+ * @param _msg The message to dispatch.
143
+ */
144
+ function _dispatch(uint32 _destinationDomain, bytes memory _msg)
145
+ internal
146
+ returns (uint256)
147
+ {
148
+ return _dispatch(_outbox(), _destinationDomain, _msg);
149
+ }
150
+
151
+ /**
152
+ * @notice Dispatches a message to an enrolled router via the local router's Outbox
153
+ * and pays for it to be relayed to the destination.
154
+ * @dev Reverts if there is no enrolled router for _destinationDomain.
155
+ * @param _destinationDomain The domain of the chain to which to send the message.
156
+ * @param _msg The message to dispatch.
157
+ * @param _gasPayment The amount of native tokens to pay for the message to be relayed.
158
+ */
159
+ function _dispatchWithGas(
160
+ uint32 _destinationDomain,
161
+ bytes memory _msg,
162
+ uint256 _gasPayment
163
+ ) internal {
164
+ IOutbox _outbox = _outbox();
165
+ uint256 _leafIndex = _dispatch(_outbox, _destinationDomain, _msg);
166
+ if (_gasPayment > 0) {
167
+ interchainGasPaymaster.payGasFor{value: _gasPayment}(
168
+ address(_outbox),
169
+ _leafIndex,
170
+ _destinationDomain
171
+ );
172
+ }
173
+ }
174
+
175
+ // ============ Private functions ============
176
+
177
+ /**
178
+ * @notice Dispatches a message to an enrolled router via the provided Outbox.
179
+ * @dev Does not pay interchain gas.
180
+ * @dev Reverts if there is no enrolled router for _destinationDomain.
181
+ * @param _outbox The outbox contract to dispatch the message through.
182
+ * @param _destinationDomain The domain of the chain to which to send the message.
183
+ * @param _msg The message to dispatch.
184
+ */
185
+ function _dispatch(
186
+ IOutbox _outbox,
187
+ uint32 _destinationDomain,
188
+ bytes memory _msg
189
+ ) private returns (uint256) {
190
+ // Ensure that destination chain has an enrolled router.
191
+ bytes32 _router = _mustHaveRemoteRouter(_destinationDomain);
192
+ return _outbox.dispatch(_destinationDomain, _router, _msg);
193
+ }
194
+ }
@@ -0,0 +1,93 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {OwnableMulticall, Call} from "../OwnableMulticall.sol";
5
+
6
+ // ============ External Imports ============
7
+ import {Router} from "../Router.sol";
8
+ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
9
+ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
10
+ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
11
+
12
+ /*
13
+ * @title The Hello World App
14
+ * @dev You can use this simple app as a starting point for your own application.
15
+ */
16
+ contract InterchainAccountRouter is Router {
17
+ bytes constant bytecode = type(OwnableMulticall).creationCode;
18
+ bytes32 constant bytecodeHash = bytes32(keccak256(bytecode));
19
+
20
+ event InterchainAccountCreated(
21
+ uint32 indexed origin,
22
+ address sender,
23
+ address account
24
+ );
25
+
26
+ function initialize(
27
+ address _owner,
28
+ address _abacusConnectionManager,
29
+ address _interchainGasPaymaster
30
+ ) public initializer {
31
+ // Transfer ownership of the contract to deployer
32
+ _transferOwnership(_owner);
33
+ // Set the addresses for the ACM and IGP
34
+ // Alternatively, this could be done later in an initialize method
35
+ _setAbacusConnectionManager(_abacusConnectionManager);
36
+ _setInterchainGasPaymaster(_interchainGasPaymaster);
37
+ }
38
+
39
+ function dispatch(uint32 _destinationDomain, Call[] calldata calls)
40
+ external
41
+ {
42
+ _dispatch(_destinationDomain, abi.encode(msg.sender, calls));
43
+ }
44
+
45
+ function getInterchainAccount(uint32 _origin, address _sender)
46
+ public
47
+ view
48
+ returns (address)
49
+ {
50
+ return _getInterchainAccount(_salt(_origin, _sender));
51
+ }
52
+
53
+ function getDeployedInterchainAccount(uint32 _origin, address _sender)
54
+ public
55
+ returns (OwnableMulticall)
56
+ {
57
+ bytes32 salt = _salt(_origin, _sender);
58
+ address interchainAccount = _getInterchainAccount(salt);
59
+ if (!Address.isContract(interchainAccount)) {
60
+ interchainAccount = Create2.deploy(0, salt, bytecode);
61
+ emit InterchainAccountCreated(_origin, _sender, interchainAccount);
62
+ }
63
+ return OwnableMulticall(interchainAccount);
64
+ }
65
+
66
+ function _salt(uint32 _origin, address _sender)
67
+ internal
68
+ pure
69
+ returns (bytes32)
70
+ {
71
+ return bytes32(abi.encodePacked(_origin, _sender));
72
+ }
73
+
74
+ function _getInterchainAccount(bytes32 salt)
75
+ internal
76
+ view
77
+ returns (address)
78
+ {
79
+ return Create2.computeAddress(salt, bytecodeHash);
80
+ }
81
+
82
+ function _handle(
83
+ uint32 _origin,
84
+ bytes32, // router sender
85
+ bytes calldata _message
86
+ ) internal override {
87
+ (address sender, Call[] memory calls) = abi.decode(
88
+ _message,
89
+ (address, Call[])
90
+ );
91
+ getDeployedInterchainAccount(_origin, sender).proxyCalls(calls);
92
+ }
93
+ }
@@ -0,0 +1,110 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {OwnableMulticall, Call} from "../OwnableMulticall.sol";
5
+
6
+ // ============ External Imports ============
7
+ import {Router} from "../Router.sol";
8
+ import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
9
+ import {Address} from "@openzeppelin/contracts/utils/Address.sol";
10
+ import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
11
+
12
+ contract InterchainQueryRouter is Router, OwnableMulticall {
13
+ enum Action {
14
+ DISPATCH,
15
+ RESOLVE
16
+ }
17
+
18
+ event QueryDispatched(
19
+ uint32 indexed destinationDomain,
20
+ address indexed sender
21
+ );
22
+ event QueryReturned(uint32 indexed originDomain, address indexed sender);
23
+ event QueryResolved(
24
+ uint32 indexed destinationDomain,
25
+ address indexed sender
26
+ );
27
+
28
+ function initialize(
29
+ address _owner,
30
+ address _abacusConnectionManager,
31
+ address _interchainGasPaymaster
32
+ ) public initializer {
33
+ // Transfer ownership of the contract to deployer
34
+ _transferOwnership(_owner);
35
+ // Set the addresses for the ACM and IGP
36
+ // Alternatively, this could be done later in an initialize method
37
+ _setAbacusConnectionManager(_abacusConnectionManager);
38
+ _setInterchainGasPaymaster(_interchainGasPaymaster);
39
+ }
40
+
41
+ /**
42
+ * @param _destinationDomain Domain of destination chain
43
+ * @param call Call (to and data packed struct) to be made on destination chain.
44
+ * @param callback Callback function selector on `msg.sender` and optionally abi-encoded prefix arguments.
45
+ */
46
+ function query(
47
+ uint32 _destinationDomain,
48
+ Call calldata call,
49
+ bytes calldata callback
50
+ ) external {
51
+ // TODO: fix this ugly arrayification
52
+ Call[] memory calls = new Call[](1);
53
+ calls[0] = call;
54
+ bytes[] memory callbacks = new bytes[](1);
55
+ callbacks[0] = callback;
56
+ query(_destinationDomain, calls, callbacks);
57
+ }
58
+
59
+ /**
60
+ * @param _destinationDomain Domain of destination chain
61
+ * @param calls Array of calls (to and data packed struct) to be made on destination chain in sequence.
62
+ * @param callbacks Array of callback function selectors on `msg.sender` and optionally abi-encoded prefix arguments.
63
+ */
64
+ function query(
65
+ uint32 _destinationDomain,
66
+ Call[] memory calls,
67
+ bytes[] memory callbacks
68
+ ) public {
69
+ require(
70
+ calls.length == callbacks.length,
71
+ "InterchainQueryRouter: calls and callbacks must be same length"
72
+ );
73
+ _dispatch(
74
+ _destinationDomain,
75
+ abi.encode(Action.DISPATCH, msg.sender, calls, callbacks)
76
+ );
77
+ emit QueryDispatched(_destinationDomain, msg.sender);
78
+ }
79
+
80
+ // TODO: add REJECT behavior ala NodeJS Promise API
81
+ function _handle(
82
+ uint32 _origin,
83
+ bytes32, // router sender
84
+ bytes calldata _message
85
+ ) internal override {
86
+ // TODO: fix double ABI decoding with calldata slices
87
+ Action action = abi.decode(_message, (Action));
88
+ if (action == Action.DISPATCH) {
89
+ (
90
+ ,
91
+ address sender,
92
+ Call[] memory calls,
93
+ bytes[] memory callbacks
94
+ ) = abi.decode(_message, (Action, address, Call[], bytes[]));
95
+ bytes[] memory resolveCallbacks = _call(calls, callbacks);
96
+ _dispatch(
97
+ _origin,
98
+ abi.encode(Action.RESOLVE, sender, resolveCallbacks)
99
+ );
100
+ emit QueryReturned(_origin, sender);
101
+ } else if (action == Action.RESOLVE) {
102
+ (, address sender, bytes[] memory resolveCallbacks) = abi.decode(
103
+ _message,
104
+ (Action, address, bytes[])
105
+ );
106
+ proxyCallBatch(sender, resolveCallbacks);
107
+ emit QueryResolved(_origin, sender);
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,13 @@
1
+ # Middleware Contracts
2
+
3
+ ## Interchain Accounts
4
+
5
+ An interchain account is a smart contract that is deployed on a remote chain and is controlled exclusively by the deploying local account.
6
+ Interchain accounts provide developers with a [transparent multicall API](./contracts/OwnableMulticall.sol) to remote smart contracts.
7
+ This avoids the need to deploy application specific smart contracts on remote chains while simultaneously enabling cross-chain composability.
8
+
9
+ See [IBC Interchain Accounts](https://github.com/cosmos/ibc/blob/main/spec/app/ics-027-interchain-accounts/README.md) for the Cosmos ecosystem equivalent.
10
+
11
+ ## Interchain Query System
12
+
13
+ TBD
@@ -0,0 +1,23 @@
1
+ // SPDX-License-Identifier: MIT OR Apache-2.0
2
+ pragma solidity >=0.6.11;
3
+ import {IOutbox} from "../../interfaces/IOutbox.sol";
4
+
5
+ import "../AbacusConnectionClient.sol";
6
+
7
+ contract TestAbacusConnectionClient is AbacusConnectionClient {
8
+ function initialize(address _abacusConnectionManager) external initializer {
9
+ __AbacusConnectionClient_initialize(_abacusConnectionManager);
10
+ }
11
+
12
+ function outbox() external view returns (IOutbox) {
13
+ return _outbox();
14
+ }
15
+
16
+ function isInbox(address _potentialInbox) external view returns (bool) {
17
+ return _isInbox(_potentialInbox);
18
+ }
19
+
20
+ function localDomain() external view returns (uint32) {
21
+ return _localDomain();
22
+ }
23
+ }
@@ -0,0 +1,39 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {InterchainQueryRouter} from "../middleware/InterchainQueryRouter.sol";
5
+ import {Call} from "../OwnableMulticall.sol";
6
+ import {TypeCasts} from "../libs/TypeCasts.sol";
7
+
8
+ contract TestQuery {
9
+ InterchainQueryRouter public router;
10
+
11
+ event Owner(uint256, address);
12
+
13
+ constructor(address _router) {
14
+ router = InterchainQueryRouter(_router);
15
+ }
16
+
17
+ /**
18
+ * @dev Fetches owner of InterchainQueryRouter on provided domain and passes along with provided secret to `this.receiveRouterOwner`
19
+ */
20
+ function queryRouterOwner(uint32 domain, uint256 secret) external {
21
+ Call memory call = Call({
22
+ to: TypeCasts.bytes32ToAddress(router.routers(domain)),
23
+ data: abi.encodeWithSignature("owner()")
24
+ });
25
+ bytes memory callback = bytes.concat(
26
+ this.receiveRouterOwer.selector,
27
+ bytes32(secret)
28
+ );
29
+ router.query(domain, call, callback);
30
+ }
31
+
32
+ /**
33
+ * @dev `msg.sender` must be restricted to `this.router` to prevent any local account from spoofing query data.
34
+ */
35
+ function receiveRouterOwer(uint256 secret, address owner) external {
36
+ require(msg.sender == address(router), "TestQuery: not from router");
37
+ emit Owner(secret, owner);
38
+ }
39
+ }