2
2
pragma solidity 0.8.24 ;
3
3
4
4
import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol " ;
5
- import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol " ;
6
5
import {CAIP10} from "openzeppelin-contracts/contracts/utils/CAIP10.sol " ;
7
6
import {CAIP2} from "openzeppelin-contracts/contracts/utils/CAIP2.sol " ;
8
7
import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol " ;
@@ -18,7 +17,6 @@ import {Paymaster} from "./Paymaster.sol";
18
17
/// @notice An inbox contract within RIP-7755. This contract's sole purpose is to route requested transactions on
19
18
/// destination chains and store record of their fulfillment.
20
19
contract RIP7755Inbox is ERC7786Base , Paymaster {
21
- using Address for address payable ;
22
20
using Strings for string ;
23
21
24
22
struct MainStorage {
@@ -35,6 +33,16 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
35
33
address fulfiller;
36
34
}
37
35
36
+ /// @notice A message structure used for internal processing
37
+ struct InternalMessage {
38
+ /// @dev The fulfiller address allowed to claim on source chain
39
+ address fulfiller;
40
+ /// @dev Boolean value specifying if the request represents an ERC-4337 UserOp
41
+ bool isUserOp;
42
+ /// @dev Address of the specified precheck contract. This is optional.
43
+ address precheckContract;
44
+ }
45
+
38
46
/// @notice Main storage location used as the base for the fulfillmentInfo mapping following EIP-7201. Derived from
39
47
/// the equation keccak256(abi.encode(uint256(keccak256(bytes("RIP-7755"))) - 1)) & ~bytes32(uint256(0xff))
40
48
bytes32 private constant _MAIN_STORAGE_LOCATION = 0xfd1017d80ffe8da8a74488ee7408c9efa1877e094afa95857de95797c1228500 ;
@@ -81,21 +89,29 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
81
89
Message[] calldata messages ,
82
90
bytes [] calldata globalAttributes
83
91
) external payable returns (bytes4 ) {
84
- address fulfiller = _getFulfiller (globalAttributes);
85
- _revertIfUserOp (globalAttributes);
92
+ InternalMessage memory m = _processAttributes (globalAttributes);
93
+
94
+ if (m.fulfiller == address (0 )) {
95
+ revert AttributeNotFound (_FULFILLER_ATTRIBUTE_SELECTOR);
96
+ }
97
+
98
+ if (m.isUserOp) {
99
+ revert UserOp ();
100
+ }
101
+
86
102
bytes32 messageId = getRequestId (sourceChain, sender, messages, globalAttributes);
87
103
88
- _runPrecheck (sourceChain, sender, messages, globalAttributes);
104
+ _runPrecheck (sourceChain, sender, messages, globalAttributes, m.precheckContract );
89
105
90
106
if (_getFulfillmentInfo (messageId).timestamp != 0 ) {
91
107
revert CallAlreadyFulfilled ();
92
108
}
93
109
94
- _setFulfillmentInfo (messageId, fulfiller);
110
+ _setFulfillmentInfo (messageId, m. fulfiller);
95
111
96
112
_sendCallsAndValidateMsgValue (messages);
97
113
98
- emit CallFulfilled ({requestHash: messageId, fulfilledBy: fulfiller});
114
+ emit CallFulfilled ({requestHash: messageId, fulfilledBy: m. fulfiller});
99
115
100
116
return 0x675b049b ; // this function's sig
101
117
}
@@ -134,8 +150,8 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
134
150
uint256 valueSent;
135
151
136
152
for (uint256 i; i < messages.length ; i++ ) {
137
- address payable to = payable ( messages[i].receiver.parseAddress () );
138
- uint256 value = _locateAttributeValue (messages[i].attributes, _VALUE_ATTRIBUTE_SELECTOR );
153
+ address to = messages[i].receiver.parseAddress ();
154
+ uint256 value = _locateAttributeValue (messages[i].attributes);
139
155
_call (to, messages[i].payload, value);
140
156
141
157
unchecked {
@@ -148,11 +164,16 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
148
164
}
149
165
}
150
166
151
- function _call (address payable to , bytes memory data , uint256 value ) private {
152
- if (data.length == 0 ) {
153
- to.sendValue (value);
154
- } else {
155
- to.functionCallWithValue (data, value);
167
+ function _call (address to , bytes memory data , uint256 value ) private {
168
+ bytes memory result;
169
+ /// @solidity memory-safe-assembly
170
+ assembly {
171
+ result := mload (0x40 )
172
+ if iszero (call (gas (), to, value, add (data, 0x20 ), mload (data), codesize (), 0x00 )) {
173
+ // Bubble up the revert if the call reverts.
174
+ returndatacopy (result, 0x00 , returndatasize ())
175
+ revert (result, returndatasize ())
176
+ }
156
177
}
157
178
}
158
179
@@ -167,39 +188,35 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
167
188
string calldata sourceChain , // [CAIP-2] chain identifier
168
189
string calldata sender , // [CAIP-10] account address
169
190
Message[] calldata messages ,
170
- bytes [] calldata attributes
191
+ bytes [] calldata attributes ,
192
+ address precheck
171
193
) private view {
172
- (bool found , bytes calldata precheckAttribute ) =
173
- _locateAttributeUnchecked (attributes, _PRECHECK_ATTRIBUTE_SELECTOR);
174
-
175
- if (! found) {
194
+ if (precheck == address (0 )) {
176
195
return ;
177
196
}
178
197
179
- address precheckContract = abi.decode (precheckAttribute[4 :], (address ));
180
- IPrecheckContract (precheckContract).precheckCall (sourceChain, sender, messages, attributes, msg .sender );
198
+ IPrecheckContract (precheck).precheckCall (sourceChain, sender, messages, attributes, msg .sender );
181
199
}
182
200
183
201
function _getFulfillmentInfo (bytes32 requestHash ) private view returns (FulfillmentInfo memory ) {
184
202
MainStorage storage $ = _getMainStorage ();
185
203
return $.fulfillmentInfo[requestHash];
186
204
}
187
205
188
- function _getFulfiller (bytes [] calldata attributes ) private pure returns (address ) {
189
- bytes calldata fulfillerAttribute = _locateAttribute (attributes, _FULFILLER_ATTRIBUTE_SELECTOR);
190
- return abi.decode (fulfillerAttribute[4 :], (address ));
191
- }
192
-
193
- function _revertIfUserOp (bytes [] calldata attributes ) private pure {
194
- (bool found , bytes calldata userOpAttribute ) =
195
- _locateAttributeUnchecked (attributes, _USER_OP_ATTRIBUTE_SELECTOR);
196
- if (found) {
197
- bool isUserOp = abi.decode (userOpAttribute[4 :], (bool ));
206
+ function _processAttributes (bytes [] calldata attributes ) private pure returns (InternalMessage memory ) {
207
+ InternalMessage memory message;
198
208
199
- if (isUserOp) {
200
- revert UserOp ();
209
+ for (uint256 i; i < attributes.length ; i++ ) {
210
+ if (bytes4 (attributes[i]) == _FULFILLER_ATTRIBUTE_SELECTOR) {
211
+ message.fulfiller = abi.decode (attributes[i][4 :], (address ));
212
+ } else if (bytes4 (attributes[i]) == _USER_OP_ATTRIBUTE_SELECTOR) {
213
+ message.isUserOp = abi.decode (attributes[i][4 :], (bool ));
214
+ } else if (bytes4 (attributes[i]) == _PRECHECK_ATTRIBUTE_SELECTOR) {
215
+ message.precheckContract = abi.decode (attributes[i][4 :], (address ));
201
216
}
202
217
}
218
+
219
+ return message;
203
220
}
204
221
205
222
function _filterOutFulfiller (bytes [] calldata attributes ) private pure returns (bytes [] memory ) {
0 commit comments