@@ -70,25 +70,33 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
7070 ***************************************************************************/
7171
7272 /**
73- * @notice Receives approval, deposit and optionally matches orders in one transaction
73+ * @notice Receives approval, deposit and optionally executes an operation in one transaction
7474 *
7575 * Usage patterns:
7676 * 1. Simple deposit: RLC.approveAndCall(escrow, amount, "")
77- * 2. Deposit + match : RLC.approveAndCall(escrow, amount, encodedOrders )
77+ * 2. Deposit + operation : RLC.approveAndCall(escrow, amount, encodedOperation )
7878 *
79- * The `data` parameter should be ABI-encoded orders if matching is desired:
80- * abi.encode(appOrder, datasetOrder, workerpoolOrder, requestOrder)
79+ * The `data` parameter should include a function selector (first 4 bytes) to identify
80+ * the operation, followed by ABI-encoded parameters. Supported operations:
81+ * - matchOrders: Validates sender is requester, then matches orders
8182 *
82- * @dev Important notes:
83- * - Match orders sponsoring is NOT supported. The requester (sender) always pays for the deal.
84- * - Clients must compute the exact deal cost and deposit the right amount for the deal to be matched.
83+ * @dev Implementation details:
84+ * - Deposits tokens first, then executes the operation if data is provided
85+ * - Extracts function selector from data to determine which operation
86+ * - Each operation has a validator (_validateMatchOrders, etc.) for preconditions
87+ * - After validation, _executeOperation performs the delegatecall
88+ * - Error handling is generalized: bubbles up revert reasons or returns 'operation-failed'
89+ * - Future operations can be added by implementing a validator and adding a selector case
90+ *
91+ * @dev matchOrders specific notes:
92+ * - Sponsoring is NOT supported. The requester (sender) always pays for the deal.
93+ * - Clients must compute the exact deal cost and deposit the right amount.
8594 * The deal cost = (appPrice + datasetPrice + workerpoolPrice) * volume.
86- * - If insufficient funds are deposited, the match will fail.
8795 *
88- * @param sender The address that approved tokens (must be requester if matching)
96+ * @param sender The address that approved tokens
8997 * @param amount Amount of tokens approved and to be deposited
9098 * @param token Address of the token (must be RLC)
91- * @param data Optional: ABI-encoded orders for matching
99+ * @param data Optional: Function selector + ABI-encoded parameters for operation
92100 * @return success True if operation succeeded
93101 *
94102 *
@@ -97,10 +105,16 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
97105 * // Compute deal cost
98106 * uint256 dealCost = (appPrice + datasetPrice + workerpoolPrice) * volume;
99107 *
100- * // Encode orders
101- * bytes memory data = abi.encode(appOrder, datasetOrder, workerpoolOrder, requestOrder);
108+ * // Encode matchOrders operation with selector
109+ * bytes memory data = abi.encodeWithSelector(
110+ * IexecPoco1.matchOrders.selector,
111+ * appOrder,
112+ * datasetOrder,
113+ * workerpoolOrder,
114+ * requestOrder
115+ * );
102116 *
103- * // One transaction does it all
117+ * // One transaction does it all: approve, deposit, and match
104118 * RLC(token).approveAndCall(iexecProxy, dealCost, data);
105119 * ```
106120 */
@@ -114,54 +128,28 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
114128 require (token == address ($.m_baseToken), "wrong-token " );
115129 _deposit (sender, amount);
116130 _mint (sender, amount);
131+
117132 if (data.length > 0 ) {
118- _decodeDataAndMatchOrders (sender, data);
133+ _executeOperation (sender, data);
119134 }
120135 return true ;
121136 }
122137
123- /******************************************************************************
124- * Token Spender: Atomic Deposit+Match if used with RLC.approveAndCall *
125- *****************************************************************************/
126-
127- /**
128- * @dev Internal function to match orders after deposit
129- * @param sender The user who deposited (must be the requester)
130- * @param data ABI-encoded orders
131- */
132- function _decodeDataAndMatchOrders (address sender , bytes calldata data ) internal {
133- // Decode the orders from calldata
134- (
135- IexecLibOrders_v5.AppOrder memory apporder ,
136- IexecLibOrders_v5.DatasetOrder memory datasetorder ,
137- IexecLibOrders_v5.WorkerpoolOrder memory workerpoolorder ,
138- IexecLibOrders_v5.RequestOrder memory requestorder
139- ) = abi.decode (
140- data,
141- (
142- IexecLibOrders_v5.AppOrder,
143- IexecLibOrders_v5.DatasetOrder,
144- IexecLibOrders_v5.WorkerpoolOrder,
145- IexecLibOrders_v5.RequestOrder
146- )
147- );
138+ function _executeOperation (address sender , bytes calldata data ) internal {
139+ // Extract the function selector (first 4 bytes)
140+ bytes4 selector = bytes4 (data[:4 ]);
148141
149- // Validate that sender is the requester
150- if (requestorder.requester != sender) revert ("caller-must-be-requester " );
142+ // Validate operation-specific preconditions before execution
143+ if (selector == IexecPoco1.matchOrders.selector ) {
144+ _validateMatchOrders (sender, data);
145+ } else {
146+ revert ("unsupported-operation " );
147+ }
151148
152- // Call matchOrders on the IexecPoco1 facet through the diamond
153- // Using delegatecall for safety: preserves msg.sender context (RLC address in this case)
154- // Note: matchOrders doesn't use msg.sender, but delegatecall is safer
155- // in case the implementation changes in the future
156- (bool success , bytes memory result ) = address (this ).delegatecall (
157- abi.encodeWithSelector (
158- IexecPoco1.matchOrders.selector ,
159- apporder,
160- datasetorder,
161- workerpoolorder,
162- requestorder
163- )
164- );
149+ // Execute the operation via delegatecall
150+ // This preserves msg.sender context and allows the operation to access
151+ // the diamond's storage and functions
152+ (bool success , bytes memory result ) = address (this ).delegatecall (data);
165153
166154 // Handle failure and bubble up revert reason
167155 if (! success) {
@@ -172,11 +160,39 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
172160 revert (add (result, 32 ), returndata_size)
173161 }
174162 } else {
175- revert ("receive-approval -failed " );
163+ revert ("operation -failed " );
176164 }
177165 }
178166 }
179167
168+ /******************************************************************************
169+ * Token Spender: Atomic Deposit+Match if used with RLC.approveAndCall *
170+ *****************************************************************************/
171+
172+ /**
173+ * @dev Validates matchOrders preconditions
174+ * @param sender The user who deposited (must be the requester)
175+ * @param data ABI-encoded matchOrders call with orders
176+ */
177+ function _validateMatchOrders (address sender , bytes calldata data ) internal pure {
178+ // Decode only the request order to validate the requester
179+ // Full decoding: (AppOrder, DatasetOrder, WorkerpoolOrder, RequestOrder)
180+ // We only need to check requestorder.requester
181+ (, , , IexecLibOrders_v5.RequestOrder memory requestorder ) = abi.decode (
182+ data[4 :],
183+ (
184+ IexecLibOrders_v5.AppOrder,
185+ IexecLibOrders_v5.DatasetOrder,
186+ IexecLibOrders_v5.WorkerpoolOrder,
187+ IexecLibOrders_v5.RequestOrder
188+ )
189+ );
190+
191+ // Validate that sender is the requester
192+ // This ensures the caller is authorized to create this deal
193+ if (requestorder.requester != sender) revert ("caller-must-be-requester " );
194+ }
195+
180196 function _deposit (address from , uint256 amount ) internal {
181197 PocoStorageLib.PocoStorage storage $ = PocoStorageLib.getPocoStorage ();
182198 require ($.m_baseToken.transferFrom (from, address (this ), amount), "failed-transferFrom " );
0 commit comments