22pragma solidity 0.8.24 ;
33
44import {IEntryPoint} from "account-abstraction/interfaces/IEntryPoint.sol " ;
5- import {Address} from "openzeppelin-contracts/contracts/utils/Address.sol " ;
65import {CAIP10} from "openzeppelin-contracts/contracts/utils/CAIP10.sol " ;
76import {CAIP2} from "openzeppelin-contracts/contracts/utils/CAIP2.sol " ;
87import {Strings} from "openzeppelin-contracts/contracts/utils/Strings.sol " ;
@@ -18,7 +17,6 @@ import {Paymaster} from "./Paymaster.sol";
1817/// @notice An inbox contract within RIP-7755. This contract's sole purpose is to route requested transactions on
1918/// destination chains and store record of their fulfillment.
2019contract RIP7755Inbox is ERC7786Base , Paymaster {
21- using Address for address payable ;
2220 using Strings for string ;
2321
2422 struct MainStorage {
@@ -35,6 +33,16 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
3533 address fulfiller;
3634 }
3735
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+
3846 /// @notice Main storage location used as the base for the fulfillmentInfo mapping following EIP-7201. Derived from
3947 /// the equation keccak256(abi.encode(uint256(keccak256(bytes("RIP-7755"))) - 1)) & ~bytes32(uint256(0xff))
4048 bytes32 private constant _MAIN_STORAGE_LOCATION = 0xfd1017d80ffe8da8a74488ee7408c9efa1877e094afa95857de95797c1228500 ;
@@ -81,21 +89,29 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
8189 Message[] calldata messages ,
8290 bytes [] calldata globalAttributes
8391 ) 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+
86102 bytes32 messageId = getRequestId (sourceChain, sender, messages, globalAttributes);
87103
88- _runPrecheck (sourceChain, sender, messages, globalAttributes);
104+ _runPrecheck (sourceChain, sender, messages, globalAttributes, m.precheckContract );
89105
90106 if (_getFulfillmentInfo (messageId).timestamp != 0 ) {
91107 revert CallAlreadyFulfilled ();
92108 }
93109
94- _setFulfillmentInfo (messageId, fulfiller);
110+ _setFulfillmentInfo (messageId, m. fulfiller);
95111
96112 _sendCallsAndValidateMsgValue (messages);
97113
98- emit CallFulfilled ({requestHash: messageId, fulfilledBy: fulfiller});
114+ emit CallFulfilled ({requestHash: messageId, fulfilledBy: m. fulfiller});
99115
100116 return 0x675b049b ; // this function's sig
101117 }
@@ -134,8 +150,8 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
134150 uint256 valueSent;
135151
136152 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);
139155 _call (to, messages[i].payload, value);
140156
141157 unchecked {
@@ -148,11 +164,16 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
148164 }
149165 }
150166
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+ }
156177 }
157178 }
158179
@@ -167,39 +188,35 @@ contract RIP7755Inbox is ERC7786Base, Paymaster {
167188 string calldata sourceChain , // [CAIP-2] chain identifier
168189 string calldata sender , // [CAIP-10] account address
169190 Message[] calldata messages ,
170- bytes [] calldata attributes
191+ bytes [] calldata attributes ,
192+ address precheck
171193 ) private view {
172- (bool found , bytes calldata precheckAttribute ) =
173- _locateAttributeUnchecked (attributes, _PRECHECK_ATTRIBUTE_SELECTOR);
174-
175- if (! found) {
194+ if (precheck == address (0 )) {
176195 return ;
177196 }
178197
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 );
181199 }
182200
183201 function _getFulfillmentInfo (bytes32 requestHash ) private view returns (FulfillmentInfo memory ) {
184202 MainStorage storage $ = _getMainStorage ();
185203 return $.fulfillmentInfo[requestHash];
186204 }
187205
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;
198208
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 ));
201216 }
202217 }
218+
219+ return message;
203220 }
204221
205222 function _filterOutFulfiller (bytes [] calldata attributes ) private pure returns (bytes [] memory ) {
0 commit comments