Skip to content

Commit f9fc20c

Browse files
Amxxernestognw
andauthored
Update Crosschain contract: transition from CAIP-10 to ERC-7930 (#171)
Co-authored-by: ernestognw <[email protected]>
1 parent f84f33d commit f9fc20c

16 files changed

+260
-253
lines changed

contracts/crosschain/ERC7786Aggregator.sol

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,17 @@ pragma solidity ^0.8.27;
55
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
66
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
77
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";
108
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
11-
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
129
import {IERC7786GatewaySource, IERC7786Receiver} from "../interfaces/IERC7786.sol";
10+
import {InteroperableAddress} from "@openzeppelin/contracts/utils/draft-InteroperableAddress.sol";
1311

1412
/**
1513
* @dev N of M gateway: Sends your message through M independent gateways. It will be delivered to the receiver by an
1614
* equivalent aggregator on the destination chain if N of the M gateways agree.
1715
*/
1816
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;
2119

2220
struct Outbox {
2321
address gateway;
@@ -30,18 +28,18 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
3028
bool executed;
3129
}
3230

33-
event OutboxDetails(bytes32 indexed outboxId, Outbox[] outbox);
31+
event OutboxDetails(bytes32 indexed sendId, Outbox[] outbox);
3432
event Received(bytes32 indexed receiveId, address gateway);
3533
event ExecutionSuccess(bytes32 indexed receiveId);
3634
event ExecutionFailed(bytes32 indexed receiveId);
3735
event GatewayAdded(address indexed gateway);
3836
event GatewayRemoved(address indexed gateway);
3937
event ThresholdUpdated(uint8 threshold);
4038

41-
error ERC7786AggregatorValueNotSupported();
39+
error UnsupportedNativeTransfer();
4240
error ERC7786AggregatorInvalidCrosschainSender();
4341
error ERC7786AggregatorAlreadyExecuted();
44-
error ERC7786AggregatorRemoteNotRegistered(string caip2);
42+
error ERC7786AggregatorRemoteNotRegistered(bytes2 chainType, bytes chainReference);
4543
error ERC7786AggregatorGatewayAlreadyRegistered(address gateway);
4644
error ERC7786AggregatorGatewayNotRegistered(address gateway);
4745
error ERC7786AggregatorThresholdViolation();
@@ -52,7 +50,7 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
5250
****************************************************************************************************************/
5351

5452
/// @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;
5654

5755
/// @dev Tracking of the received message pending final delivery
5856
mapping(bytes32 id => Tracker) private _trackers;
@@ -69,8 +67,8 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
6967
/****************************************************************************************************************
7068
* E V E N T S & E R R O R S *
7169
****************************************************************************************************************/
72-
event RemoteRegistered(string chainId, string aggregator);
73-
error RemoteAlreadyRegistered(string chainId);
70+
event RemoteRegistered(bytes remote);
71+
error RemoteAlreadyRegistered(bytes remote);
7472

7573
/****************************************************************************************************************
7674
* F U N C T I O N S *
@@ -92,31 +90,29 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
9290
/// @inheritdoc IERC7786GatewaySource
9391
/// @dev Using memory instead of calldata avoids stack too deep errors
9492
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+
102102
// 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);
104105

105106
// 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);
107108

108109
// Post on all gateways
109110
Outbox[] memory outbox = new Outbox[](_gateways.length());
110111
bool needsId = false;
111112
for (uint256 i = 0; i < outbox.length; ++i) {
112113
address gateway = _gateways.at(i);
113114
// 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);
120116
// if ID, track it
121117
if (id != bytes32(0)) {
122118
outbox[i] = Outbox(gateway, id);
@@ -125,17 +121,11 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
125121
}
126122

127123
if (needsId) {
128-
outboxId = keccak256(abi.encode(outbox));
129-
emit OutboxDetails(outboxId, outbox);
124+
sendId = keccak256(abi.encode(outbox));
125+
emit OutboxDetails(sendId, outbox);
130126
}
131127

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);
139129
}
140130

141131
// ============================================== IERC7786Receiver ===============================================
@@ -182,18 +172,21 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
182172
* some value for unknown reason. In that case we want to register this gateway having delivered the message and
183173
* not revert. Any value accrued that way can be recovered by the admin using the {sweep} function.
184174
*/
175+
// slither-disable-next-line reentrancy-no-eth
185176
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
189179
bytes calldata payload,
190180
bytes[] calldata attributes
191181
) 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+
);
194187

195188
// Message reception tracker
196-
bytes32 id = keccak256(abi.encode(sourceChain, sender, payload, attributes));
189+
bytes32 id = keccak256(abi.encode(sender, payload, attributes));
197190
Tracker storage tracker = _trackers[id];
198191

199192
// If call is first from a trusted gateway
@@ -210,9 +203,9 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
210203
}
211204

212205
// 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(
214207
payload,
215-
(uint256, string, string, bytes)
208+
(uint256, bytes, bytes, bytes)
216209
);
217210

218211
// If ready to execute, and not yet executed
@@ -222,10 +215,11 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
222215

223216
bytes memory call = abi.encodeCall(
224217
IERC7786Receiver.executeMessage,
225-
(uint256(id).toHexString(32), sourceChain, originalSender, unwrappedPayload, attributes)
218+
(id, originalSender, unwrappedPayload, attributes)
226219
);
227220
// 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);
229223

230224
if (!success) {
231225
// rollback to enable retry
@@ -253,10 +247,18 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
253247
return _threshold;
254248
}
255249

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);
260262
}
261263

262264
// =================================================== Setters ===================================================
@@ -273,8 +275,8 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
273275
_setThreshold(newThreshold);
274276
}
275277

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);
278280
}
279281

280282
function pause() public virtual onlyOwner {
@@ -293,25 +295,25 @@ contract ERC7786Aggregator is IERC7786GatewaySource, IERC7786Receiver, Ownable,
293295
// ================================================== Internal ===================================================
294296

295297
function _addGateway(address gateway) internal virtual {
296-
if (!_gateways.add(gateway)) revert ERC7786AggregatorGatewayAlreadyRegistered(gateway);
298+
require(_gateways.add(gateway), ERC7786AggregatorGatewayAlreadyRegistered(gateway));
297299
emit GatewayAdded(gateway);
298300
}
299301

300302
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());
303305
emit GatewayRemoved(gateway);
304306
}
305307

306308
function _setThreshold(uint8 newThreshold) internal virtual {
307-
if (newThreshold == 0 || _threshold > _gateways.length()) revert ERC7786AggregatorThresholdViolation();
309+
require(newThreshold > 0 && newThreshold <= _gateways.length(), ERC7786AggregatorThresholdViolation());
308310
_threshold = newThreshold;
309311
emit ThresholdUpdated(newThreshold);
310312
}
311313

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);
316318
}
317319
}

0 commit comments

Comments
 (0)