@@ -7,6 +7,8 @@ import {IexecERC20Core} from "./IexecERC20Core.sol";
77import {FacetBase} from "./FacetBase.sol " ;
88import {IexecEscrowToken} from "../interfaces/IexecEscrowToken.sol " ;
99import {IexecTokenSpender} from "../interfaces/IexecTokenSpender.sol " ;
10+ import {IexecPoco1} from "../interfaces/IexecPoco1.sol " ;
11+ import {IexecLibOrders_v5} from "../libs/IexecLibOrders_v5.sol " ;
1012import {PocoStorageLib} from "../libs/PocoStorageLib.sol " ;
1113
1214contract IexecEscrowTokenFacet is IexecEscrowToken , IexecTokenSpender , FacetBase , IexecERC20Core {
@@ -64,23 +66,121 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, FacetBase
6466 return delta;
6567 }
6668
67- // Token Spender (endpoint for approveAndCallback calls to the proxy)
69+ /***************************************************************************
70+ * Token Spender: Atomic Deposit+Match *
71+ ***************************************************************************/
72+
73+ /**
74+ * @notice Receives approval, deposit and optionally matches orders in one transaction
75+ *
76+ * Usage patterns:
77+ * 1. Simple deposit: RLC.approveAndCall(escrow, amount, "")
78+ * 2. Deposit + match: RLC.approveAndCall(escrow, amount, encodedOrders)
79+ *
80+ * The `data` parameter should be ABI-encoded orders if matching is desired:
81+ * abi.encode(appOrder, datasetOrder, workerpoolOrder, requestOrder)
82+ *
83+ * @dev Important notes:
84+ * - Match orders sponsoring is NOT supported. The requester (sender) always pays for the deal.
85+ * - Clients must compute the exact deal cost and deposit the right amount for the deal to be matched.
86+ * The deal cost = (appPrice + datasetPrice + workerpoolPrice) * volume.
87+ * - If insufficient funds are deposited, the match will fail.
88+ *
89+ * @param sender The address that approved tokens (must be requester if matching)
90+ * @param amount Amount of tokens approved and to be deposited
91+ * @param token Address of the token (must be RLC)
92+ * @param data Optional: ABI-encoded orders for matching
93+ * @return success True if operation succeeded
94+ *
95+ *
96+ * @custom:example
97+ * ```solidity
98+ * // Compute deal cost
99+ * uint256 dealCost = (appPrice + datasetPrice + workerpoolPrice) * volume;
100+ *
101+ * // Encode orders
102+ * bytes memory data = abi.encode(appOrder, datasetOrder, workerpoolOrder, requestOrder);
103+ *
104+ * // One transaction does it all
105+ * RLC(token).approveAndCall(iexecProxy, dealCost, data);
106+ * ```
107+ */
68108 function receiveApproval (
69109 address sender ,
70110 uint256 amount ,
71111 address token ,
72- bytes calldata
112+ bytes calldata data
73113 ) external override returns (bool ) {
74114 PocoStorageLib.PocoStorage storage $ = PocoStorageLib.getPocoStorage ();
75115 require (token == address ($.m_baseToken), "wrong-token " );
76116 _deposit (sender, amount);
77117 _mint (sender, amount);
118+ if (data.length > 0 ) {
119+ _decodeDataAndMatchOrders (sender, data);
120+ }
78121 return true ;
79122 }
80123
124+ /******************************************************************************
125+ * Token Spender: Atomic Deposit+Match if used with RLC.approveAndCall *
126+ *****************************************************************************/
127+
128+ /**
129+ * @dev Internal function to match orders after deposit
130+ * @param sender The user who deposited (must be the requester)
131+ * @param data ABI-encoded orders
132+ */
133+ function _decodeDataAndMatchOrders (address sender , bytes calldata data ) internal {
134+ // Decode the orders from calldata
135+ (
136+ IexecLibOrders_v5.AppOrder memory apporder ,
137+ IexecLibOrders_v5.DatasetOrder memory datasetorder ,
138+ IexecLibOrders_v5.WorkerpoolOrder memory workerpoolorder ,
139+ IexecLibOrders_v5.RequestOrder memory requestorder
140+ ) = abi.decode (
141+ data,
142+ (
143+ IexecLibOrders_v5.AppOrder,
144+ IexecLibOrders_v5.DatasetOrder,
145+ IexecLibOrders_v5.WorkerpoolOrder,
146+ IexecLibOrders_v5.RequestOrder
147+ )
148+ );
149+
150+ // Validate that sender is the requester
151+ if (requestorder.requester != sender) revert ("caller-must-be-requester " );
152+
153+ // Call matchOrders on the IexecPoco1 facet through the diamond
154+ // Using delegatecall for safety: preserves msg.sender context (RLC address in this case)
155+ // Note: matchOrders doesn't use msg.sender, but delegatecall is safer
156+ // in case the implementation changes in the future
157+ (bool success , bytes memory result ) = address (this ).delegatecall (
158+ abi.encodeWithSelector (
159+ IexecPoco1.matchOrders.selector ,
160+ apporder,
161+ datasetorder,
162+ workerpoolorder,
163+ requestorder
164+ )
165+ );
166+
167+ // Handle failure and bubble up revert reason
168+ if (! success) {
169+ if (result.length > 0 ) {
170+ // Decode and revert with the original error
171+ assembly {
172+ let returndata_size := mload (result)
173+ revert (add (result, 32 ), returndata_size)
174+ }
175+ } else {
176+ revert ("receive-approval-failed " );
177+ }
178+ }
179+ }
180+
81181 function _deposit (address from , uint256 amount ) internal {
82182 PocoStorageLib.PocoStorage storage $ = PocoStorageLib.getPocoStorage ();
83- require ($.m_baseToken.transferFrom (from, address (this ), amount), "failled -transferFrom " );
183+ require ($.m_baseToken.transferFrom (from, address (this ), amount), "failed -transferFrom " );
84184 }
85185
86186 function _withdraw (address to , uint256 amount ) internal {
0 commit comments