@hyperlane-xyz/core 0.5.0 → 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.
- package/contracts/AbacusConnectionClient.sol +141 -0
- package/contracts/OwnableMulticall.sol +60 -0
- package/contracts/Router.sol +194 -0
- package/contracts/middleware/InterchainAccountRouter.sol +93 -0
- package/contracts/middleware/InterchainQueryRouter.sol +110 -0
- package/contracts/middleware/README.md +13 -0
- package/contracts/test/TestAbacusConnectionClient.sol +23 -0
- package/contracts/test/TestQuery.sol +39 -0
- package/contracts/test/TestRouter.sol +47 -0
- package/dist/contracts/AbacusConnectionClient.d.ts +169 -0
- package/dist/contracts/AbacusConnectionClient.d.ts.map +1 -0
- package/dist/contracts/AbacusConnectionClient.js +4 -0
- package/dist/contracts/AbacusConnectionClient.js.map +1 -0
- package/dist/contracts/OwnableMulticall.d.ts +123 -0
- package/dist/contracts/OwnableMulticall.d.ts.map +1 -0
- package/dist/contracts/OwnableMulticall.js +4 -0
- package/dist/contracts/OwnableMulticall.js.map +1 -0
- package/dist/contracts/Router.d.ts +222 -0
- package/dist/contracts/Router.d.ts.map +1 -0
- package/dist/contracts/Router.js +4 -0
- package/dist/contracts/Router.js.map +1 -0
- package/dist/contracts/index.d.ts +5 -0
- package/dist/contracts/index.d.ts.map +1 -1
- package/dist/contracts/middleware/InterchainAccountRouter.d.ts +301 -0
- package/dist/contracts/middleware/InterchainAccountRouter.d.ts.map +1 -0
- package/dist/contracts/middleware/InterchainAccountRouter.js +4 -0
- package/dist/contracts/middleware/InterchainAccountRouter.js.map +1 -0
- package/dist/contracts/middleware/InterchainQueryRouter.d.ts +333 -0
- package/dist/contracts/middleware/InterchainQueryRouter.d.ts.map +1 -0
- package/dist/contracts/middleware/InterchainQueryRouter.js +4 -0
- package/dist/contracts/middleware/InterchainQueryRouter.js.map +1 -0
- package/dist/contracts/middleware/index.d.ts +3 -0
- package/dist/contracts/middleware/index.d.ts.map +1 -0
- package/dist/contracts/middleware/index.js +3 -0
- package/dist/contracts/middleware/index.js.map +1 -0
- package/dist/contracts/test/TestAbacusConnectionClient.d.ts +209 -0
- package/dist/contracts/test/TestAbacusConnectionClient.d.ts.map +1 -0
- package/dist/contracts/test/TestAbacusConnectionClient.js +4 -0
- package/dist/contracts/test/TestAbacusConnectionClient.js.map +1 -0
- package/dist/contracts/test/TestQuery.d.ts +87 -0
- package/dist/contracts/test/TestQuery.d.ts.map +1 -0
- package/dist/contracts/test/TestQuery.js +4 -0
- package/dist/contracts/test/TestQuery.js.map +1 -0
- package/dist/contracts/test/TestRouter.d.ts +295 -0
- package/dist/contracts/test/TestRouter.d.ts.map +1 -0
- package/dist/contracts/test/TestRouter.js +4 -0
- package/dist/contracts/test/TestRouter.js.map +1 -0
- package/dist/contracts/test/index.d.ts +3 -0
- package/dist/contracts/test/index.d.ts.map +1 -1
- package/dist/factories/contracts/AbacusConnectionClient__factory.d.ts +43 -0
- package/dist/factories/contracts/AbacusConnectionClient__factory.d.ts.map +1 -0
- package/dist/factories/contracts/AbacusConnectionClient__factory.js +161 -0
- package/dist/factories/contracts/AbacusConnectionClient__factory.js.map +1 -0
- package/dist/factories/contracts/OwnableMulticall__factory.d.ts +78 -0
- package/dist/factories/contracts/OwnableMulticall__factory.d.ts.map +1 -0
- package/dist/factories/contracts/OwnableMulticall__factory.js +136 -0
- package/dist/factories/contracts/OwnableMulticall__factory.js.map +1 -0
- package/dist/factories/contracts/Router__factory.d.ts +36 -0
- package/dist/factories/contracts/Router__factory.d.ts.map +1 -0
- package/dist/factories/contracts/Router__factory.js +240 -0
- package/dist/factories/contracts/Router__factory.js.map +1 -0
- package/dist/factories/contracts/index.d.ts +4 -0
- package/dist/factories/contracts/index.d.ts.map +1 -1
- package/dist/factories/contracts/index.js +8 -1
- package/dist/factories/contracts/index.js.map +1 -1
- package/dist/factories/contracts/middleware/InterchainAccountRouter__factory.d.ts +69 -0
- package/dist/factories/contracts/middleware/InterchainAccountRouter__factory.d.ts.map +1 -0
- package/dist/factories/contracts/middleware/InterchainAccountRouter__factory.js +389 -0
- package/dist/factories/contracts/middleware/InterchainAccountRouter__factory.js.map +1 -0
- package/dist/factories/contracts/middleware/InterchainQueryRouter__factory.d.ts +69 -0
- package/dist/factories/contracts/middleware/InterchainQueryRouter__factory.d.ts.map +1 -0
- package/dist/factories/contracts/middleware/InterchainQueryRouter__factory.js +438 -0
- package/dist/factories/contracts/middleware/InterchainQueryRouter__factory.js.map +1 -0
- package/dist/factories/contracts/middleware/index.d.ts +3 -0
- package/dist/factories/contracts/middleware/index.d.ts.map +1 -0
- package/dist/factories/contracts/middleware/index.js +11 -0
- package/dist/factories/contracts/middleware/index.js.map +1 -0
- package/dist/factories/contracts/test/TestAbacusConnectionClient__factory.d.ts +48 -0
- package/dist/factories/contracts/test/TestAbacusConnectionClient__factory.d.ts.map +1 -0
- package/dist/factories/contracts/test/TestAbacusConnectionClient__factory.js +242 -0
- package/dist/factories/contracts/test/TestAbacusConnectionClient__factory.js.map +1 -0
- package/dist/factories/contracts/test/TestQuery__factory.d.ts +66 -0
- package/dist/factories/contracts/test/TestQuery__factory.d.ts.map +1 -0
- package/dist/factories/contracts/test/TestQuery__factory.js +120 -0
- package/dist/factories/contracts/test/TestQuery__factory.js.map +1 -0
- package/dist/factories/contracts/test/TestRouter__factory.d.ts +48 -0
- package/dist/factories/contracts/test/TestRouter__factory.d.ts.map +1 -0
- package/dist/factories/contracts/test/TestRouter__factory.js +366 -0
- package/dist/factories/contracts/test/TestRouter__factory.js.map +1 -0
- package/dist/factories/contracts/test/index.d.ts +3 -0
- package/dist/factories/contracts/test/index.d.ts.map +1 -1
- package/dist/factories/contracts/test/index.js +7 -1
- package/dist/factories/contracts/test/index.js.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -1
- package/dist/index.js.map +1 -1
- 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
|
+
}
|