@@ -5,19 +5,17 @@ pragma solidity ^0.8.27;
5
5
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol " ;
6
6
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol " ;
7
7
import {Address} from "@openzeppelin/contracts/utils/Address.sol " ;
8
- import {CAIP2} from "@openzeppelin/contracts/utils/CAIP2.sol " ;
9
- import {CAIP10} from "@openzeppelin/contracts/utils/CAIP10.sol " ;
10
8
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol " ;
11
- import {Strings} from "@openzeppelin/contracts/utils/Strings.sol " ;
12
9
import {IERC7786GatewaySource , IERC7786Receiver } from "../interfaces/IERC7786.sol " ;
10
+ import {InteroperableAddress} from "@openzeppelin/contracts/utils/draft-InteroperableAddress.sol " ;
13
11
14
12
/**
15
13
* @dev N of M gateway: Sends your message through M independent gateways. It will be delivered to the receiver by an
16
14
* equivalent aggregator on the destination chain if N of the M gateways agree.
17
15
*/
18
16
contract ERC7786Aggregator is IERC7786GatewaySource , IERC7786Receiver , Ownable , Pausable {
19
- using EnumerableSet for * ;
20
- using Strings for * ;
17
+ using EnumerableSet for EnumerableSet.AddressSet ;
18
+ using InteroperableAddress for bytes ;
21
19
22
20
struct Outbox {
23
21
address gateway;
@@ -30,18 +28,18 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
30
28
bool executed;
31
29
}
32
30
33
- event OutboxDetails (bytes32 indexed outboxId , Outbox[] outbox );
31
+ event OutboxDetails (bytes32 indexed sendId , Outbox[] outbox );
34
32
event Received (bytes32 indexed receiveId , address gateway );
35
33
event ExecutionSuccess (bytes32 indexed receiveId );
36
34
event ExecutionFailed (bytes32 indexed receiveId );
37
35
event GatewayAdded (address indexed gateway );
38
36
event GatewayRemoved (address indexed gateway );
39
37
event ThresholdUpdated (uint8 threshold );
40
38
41
- error ERC7786AggregatorValueNotSupported ();
39
+ error UnsupportedNativeTransfer ();
42
40
error ERC7786AggregatorInvalidCrosschainSender ();
43
41
error ERC7786AggregatorAlreadyExecuted ();
44
- error ERC7786AggregatorRemoteNotRegistered (string caip2 );
42
+ error ERC7786AggregatorRemoteNotRegistered (bytes2 chainType , bytes chainReference );
45
43
error ERC7786AggregatorGatewayAlreadyRegistered (address gateway );
46
44
error ERC7786AggregatorGatewayNotRegistered (address gateway );
47
45
error ERC7786AggregatorThresholdViolation ();
@@ -52,7 +50,7 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
52
50
****************************************************************************************************************/
53
51
54
52
/// @dev address of the matching aggregator for a given CAIP2 chain
55
- mapping (string caip2 = > string ) private _remotes;
53
+ mapping (bytes2 chainType = > mapping ( bytes chainReference = > bytes addr ) ) private _remotes;
56
54
57
55
/// @dev Tracking of the received message pending final delivery
58
56
mapping (bytes32 id = > Tracker) private _trackers;
@@ -69,8 +67,8 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
69
67
/****************************************************************************************************************
70
68
* E V E N T S & E R R O R S *
71
69
****************************************************************************************************************/
72
- event RemoteRegistered (string chainId , string aggregator );
73
- error RemoteAlreadyRegistered (string chainId );
70
+ event RemoteRegistered (bytes remote );
71
+ error RemoteAlreadyRegistered (bytes remote );
74
72
75
73
/****************************************************************************************************************
76
74
* F U N C T I O N S *
@@ -92,31 +90,29 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
92
90
/// @inheritdoc IERC7786GatewaySource
93
91
/// @dev Using memory instead of calldata avoids stack too deep errors
94
92
function sendMessage (
95
- string calldata destinationChain ,
96
- string memory receiver ,
97
- bytes memory payload ,
98
- bytes [] memory attributes
99
- ) public payable virtual whenNotPaused returns (bytes32 outboxId ) {
100
- if (attributes.length > 0 ) revert UnsupportedAttribute (bytes4 (attributes[0 ]));
101
- if (msg .value > 0 ) revert ERC7786AggregatorValueNotSupported ();
93
+ bytes calldata recipient , // Binary Interoperable Address
94
+ bytes calldata payload ,
95
+ bytes [] calldata attributes
96
+ ) public payable virtual whenNotPaused returns (bytes32 sendId ) {
97
+ require (msg .value == 0 , UnsupportedNativeTransfer ());
98
+ // Use of `if () revert` syntax to avoid accessing attributes[0] if it's empty
99
+ if (attributes.length > 0 )
100
+ revert UnsupportedAttribute (attributes[0 ].length < 0x04 ? bytes4 (0 ) : bytes4 (attributes[0 ][0 :4 ]));
101
+
102
102
// address of the remote aggregator, revert if not registered
103
- string memory aggregator = getRemoteAggregator (destinationChain);
103
+ bytes memory aggregator = getRemoteAggregator (recipient);
104
+ bytes memory sender = InteroperableAddress.formatEvmV1 (block .chainid , msg .sender );
104
105
105
106
// wrapping the payload
106
- bytes memory wrappedPayload = abi.encode (++ _nonce, msg . sender . toChecksumHexString (), receiver , payload);
107
+ bytes memory wrappedPayload = abi.encode (++ _nonce, sender, recipient , payload);
107
108
108
109
// Post on all gateways
109
110
Outbox[] memory outbox = new Outbox [](_gateways.length ());
110
111
bool needsId = false ;
111
112
for (uint256 i = 0 ; i < outbox.length ; ++ i) {
112
113
address gateway = _gateways.at (i);
113
114
// send message
114
- bytes32 id = IERC7786GatewaySource (gateway).sendMessage (
115
- destinationChain,
116
- aggregator,
117
- wrappedPayload,
118
- attributes
119
- );
115
+ bytes32 id = IERC7786GatewaySource (gateway).sendMessage (aggregator, wrappedPayload, attributes);
120
116
// if ID, track it
121
117
if (id != bytes32 (0 )) {
122
118
outbox[i] = Outbox (gateway, id);
@@ -125,17 +121,11 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
125
121
}
126
122
127
123
if (needsId) {
128
- outboxId = keccak256 (abi.encode (outbox));
129
- emit OutboxDetails (outboxId , outbox);
124
+ sendId = keccak256 (abi.encode (outbox));
125
+ emit OutboxDetails (sendId , outbox);
130
126
}
131
127
132
- emit MessagePosted (
133
- outboxId,
134
- CAIP10.local (msg .sender ),
135
- CAIP10.format (destinationChain, receiver),
136
- payload,
137
- attributes
138
- );
128
+ emit MessageSent (sendId, sender, recipient, payload, 0 , attributes);
139
129
}
140
130
141
131
// ============================================== IERC7786Receiver ===============================================
@@ -182,18 +172,21 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
182
172
* some value for unknown reason. In that case we want to register this gateway having delivered the message and
183
173
* not revert. Any value accrued that way can be recovered by the admin using the {sweep} function.
184
174
*/
175
+ // slither-disable-next-line reentrancy-no-eth
185
176
function executeMessage (
186
- string calldata /*messageId*/ , // gateway specific, empty or unique
187
- string calldata sourceChain , // CAIP-2 chain identifier
188
- string calldata sender , // CAIP-10 account address (does not include the chain identifier)
177
+ bytes32 /*receiveId*/ ,
178
+ bytes calldata sender , // Binary Interoperable Address
189
179
bytes calldata payload ,
190
180
bytes [] calldata attributes
191
181
) public payable virtual whenNotPaused returns (bytes4 ) {
192
- // Check sender is a trusted remote aggregator
193
- if (! _remotes[sourceChain].equal (sender)) revert ERC7786AggregatorInvalidCrosschainSender ();
182
+ // Check sender is a trusted aggregator
183
+ require (
184
+ keccak256 (getRemoteAggregator (sender)) == keccak256 (sender),
185
+ ERC7786AggregatorInvalidCrosschainSender ()
186
+ );
194
187
195
188
// Message reception tracker
196
- bytes32 id = keccak256 (abi.encode (sourceChain, sender, payload, attributes));
189
+ bytes32 id = keccak256 (abi.encode (sender, payload, attributes));
197
190
Tracker storage tracker = _trackers[id];
198
191
199
192
// If call is first from a trusted gateway
@@ -210,9 +203,9 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
210
203
}
211
204
212
205
// Parse payload
213
- (, string memory originalSender , string memory receiver , bytes memory unwrappedPayload ) = abi.decode (
206
+ (, bytes memory originalSender , bytes memory recipient , bytes memory unwrappedPayload ) = abi.decode (
214
207
payload,
215
- (uint256 , string , string , bytes )
208
+ (uint256 , bytes , bytes , bytes )
216
209
);
217
210
218
211
// If ready to execute, and not yet executed
@@ -222,10 +215,11 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
222
215
223
216
bytes memory call = abi.encodeCall (
224
217
IERC7786Receiver .executeMessage,
225
- (uint256 (id). toHexString ( 32 ), sourceChain , originalSender, unwrappedPayload, attributes)
218
+ (id , originalSender, unwrappedPayload, attributes)
226
219
);
227
220
// slither-disable-next-line reentrancy-no-eth
228
- (bool success , bytes memory returndata ) = receiver.parseAddress ().call (call);
221
+ (, address target ) = recipient.parseEvmV1 ();
222
+ (bool success , bytes memory returndata ) = target.call (call);
229
223
230
224
if (! success) {
231
225
// rollback to enable retry
@@ -253,10 +247,18 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
253
247
return _threshold;
254
248
}
255
249
256
- function getRemoteAggregator (string calldata caip2 ) public view virtual returns (string memory ) {
257
- string memory aggregator = _remotes[caip2];
258
- if (bytes (aggregator).length == 0 ) revert ERC7786AggregatorRemoteNotRegistered (caip2);
259
- return aggregator;
250
+ function getRemoteAggregator (bytes memory chain ) public view virtual returns (bytes memory ) {
251
+ (bytes2 chainType , bytes memory chainReference , ) = chain.parseV1 ();
252
+ return getRemoteAggregator (chainType, chainReference);
253
+ }
254
+
255
+ function getRemoteAggregator (
256
+ bytes2 chainType ,
257
+ bytes memory chainReference
258
+ ) public view virtual returns (bytes memory ) {
259
+ bytes memory addr = _remotes[chainType][chainReference];
260
+ require (bytes (addr).length != 0 , ERC7786AggregatorRemoteNotRegistered (chainType, chainReference));
261
+ return InteroperableAddress.formatV1 (chainType, chainReference, addr);
260
262
}
261
263
262
264
// =================================================== Setters ===================================================
@@ -273,8 +275,8 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
273
275
_setThreshold (newThreshold);
274
276
}
275
277
276
- function registerRemoteAggregator (string memory caip2 , string memory aggregator ) public virtual onlyOwner {
277
- _registerRemoteAggregator (caip2, aggregator);
278
+ function registerRemoteAggregator (bytes calldata aggregator ) public virtual onlyOwner {
279
+ _registerRemoteAggregator (aggregator);
278
280
}
279
281
280
282
function pause () public virtual onlyOwner {
@@ -293,25 +295,25 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
293
295
// ================================================== Internal ===================================================
294
296
295
297
function _addGateway (address gateway ) internal virtual {
296
- if ( ! _gateways.add (gateway)) revert ERC7786AggregatorGatewayAlreadyRegistered (gateway);
298
+ require ( _gateways.add (gateway), ERC7786AggregatorGatewayAlreadyRegistered (gateway) );
297
299
emit GatewayAdded (gateway);
298
300
}
299
301
300
302
function _removeGateway (address gateway ) internal virtual {
301
- if ( ! _gateways.remove (gateway)) revert ERC7786AggregatorGatewayNotRegistered (gateway);
302
- if (_threshold > _gateways.length ()) revert ERC7786AggregatorThresholdViolation ();
303
+ require ( _gateways.remove (gateway), ERC7786AggregatorGatewayNotRegistered (gateway) );
304
+ require (_threshold <= _gateways.length (), ERC7786AggregatorThresholdViolation () );
303
305
emit GatewayRemoved (gateway);
304
306
}
305
307
306
308
function _setThreshold (uint8 newThreshold ) internal virtual {
307
- if (newThreshold == 0 || _threshold > _gateways.length ()) revert ERC7786AggregatorThresholdViolation ();
309
+ require (newThreshold > 0 && newThreshold <= _gateways.length (), ERC7786AggregatorThresholdViolation () );
308
310
_threshold = newThreshold;
309
311
emit ThresholdUpdated (newThreshold);
310
312
}
311
313
312
- function _registerRemoteAggregator (string memory caip2 , string memory aggregator ) internal virtual {
313
- _remotes[caip2] = aggregator;
314
-
315
- emit RemoteRegistered (caip2, aggregator);
314
+ function _registerRemoteAggregator (bytes calldata aggregator ) internal virtual {
315
+ ( bytes2 chainType , bytes calldata chainReference , bytes calldata addr ) = aggregator. parseV1Calldata () ;
316
+ _remotes[chainType][chainReference] = addr;
317
+ emit RemoteRegistered (aggregator);
316
318
}
317
319
}
0 commit comments