-
Notifications
You must be signed in to change notification settings - Fork 14
feat: receive approval generic #323
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
394908b
68c1e89
23a067f
fc6926b
0ae822a
fb844af
7f08cf3
3517bf0
47ecf74
0b8ed3e
bd2adf1
0e57654
fda1c38
6ca7c61
921df75
0c661b3
d9b56ba
b51807f
ad0130f
689e1c3
ec05830
22e3b3e
be65435
648078e
f8681d3
b229dc5
0e656ee
137dd04
c49a93e
69ebbcd
f21473b
024675e
b06e552
d554755
ff00267
204f2f3
2806a5d
81c7557
0a9144e
fe6c25c
8123e0f
8f85fc3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,25 +70,33 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2 | |
| ***************************************************************************/ | ||
|
|
||
| /** | ||
| * @notice Receives approval, deposit and optionally matches orders in one transaction | ||
| * @notice Receives approval, deposit and optionally executes an operation in one transaction | ||
| * | ||
| * Usage patterns: | ||
| * 1. Simple deposit: RLC.approveAndCall(escrow, amount, "") | ||
| * 2. Deposit + match: RLC.approveAndCall(escrow, amount, encodedOrders) | ||
| * 2. Deposit + operation: RLC.approveAndCall(escrow, amount, encodedOperation) | ||
| * | ||
| * The `data` parameter should be ABI-encoded orders if matching is desired: | ||
| * abi.encode(appOrder, datasetOrder, workerpoolOrder, requestOrder) | ||
| * The `data` parameter should include a function selector (first 4 bytes) to identify | ||
| * the operation, followed by ABI-encoded parameters. Supported operations: | ||
| * - matchOrders: Validates sender is requester, then matches orders | ||
| * | ||
| * @dev Important notes: | ||
| * - Match orders sponsoring is NOT supported. The requester (sender) always pays for the deal. | ||
| * - Clients must compute the exact deal cost and deposit the right amount for the deal to be matched. | ||
| * @dev Implementation details: | ||
| * - Deposits tokens first, then executes the operation if data is provided | ||
| * - Extracts function selector from data to determine which operation | ||
| * - Each operation has a validator (_validateMatchOrders, etc.) for preconditions | ||
| * - After validation, _executeOperation performs the delegatecall | ||
| * - Error handling is generalized: bubbles up revert reasons or returns 'operation-failed' | ||
| * - Future operations can be added by implementing a validator and adding a selector case | ||
| * | ||
| * @dev matchOrders specific notes: | ||
| * - Sponsoring is NOT supported. The requester (sender) always pays for the deal. | ||
| * - Clients must compute the exact deal cost and deposit the right amount. | ||
| * The deal cost = (appPrice + datasetPrice + workerpoolPrice) * volume. | ||
| * - If insufficient funds are deposited, the match will fail. | ||
| * | ||
| * @param sender The address that approved tokens (must be requester if matching) | ||
| * @param sender The address that approved tokens | ||
| * @param amount Amount of tokens approved and to be deposited | ||
| * @param token Address of the token (must be RLC) | ||
| * @param data Optional: ABI-encoded orders for matching | ||
| * @param data Optional: Function selector + ABI-encoded parameters for operation | ||
| * @return success True if operation succeeded | ||
| * | ||
| * | ||
|
|
@@ -97,10 +105,16 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2 | |
| * // Compute deal cost | ||
| * uint256 dealCost = (appPrice + datasetPrice + workerpoolPrice) * volume; | ||
| * | ||
| * // Encode orders | ||
| * bytes memory data = abi.encode(appOrder, datasetOrder, workerpoolOrder, requestOrder); | ||
| * // Encode matchOrders operation with selector | ||
| * bytes memory data = abi.encodeWithSelector( | ||
| * IexecPoco1.matchOrders.selector, | ||
| * appOrder, | ||
| * datasetOrder, | ||
| * workerpoolOrder, | ||
| * requestOrder | ||
| * ); | ||
| * | ||
| * // One transaction does it all | ||
| * // One transaction does it all: approve, deposit, and match | ||
| * RLC(token).approveAndCall(iexecProxy, dealCost, data); | ||
| * ``` | ||
| */ | ||
|
|
@@ -114,54 +128,28 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2 | |
| require(token == address($.m_baseToken), "wrong-token"); | ||
| _deposit(sender, amount); | ||
| _mint(sender, amount); | ||
|
|
||
| if (data.length > 0) { | ||
| _decodeDataAndMatchOrders(sender, data); | ||
| _executeOperation(sender, data); | ||
| } | ||
| return true; | ||
| } | ||
|
|
||
| /****************************************************************************** | ||
| * Token Spender: Atomic Deposit+Match if used with RLC.approveAndCall * | ||
| *****************************************************************************/ | ||
|
|
||
| /** | ||
| * @dev Internal function to match orders after deposit | ||
| * @param sender The user who deposited (must be the requester) | ||
| * @param data ABI-encoded orders | ||
| */ | ||
| function _decodeDataAndMatchOrders(address sender, bytes calldata data) internal { | ||
| // Decode the orders from calldata | ||
| ( | ||
| IexecLibOrders_v5.AppOrder memory apporder, | ||
| IexecLibOrders_v5.DatasetOrder memory datasetorder, | ||
| IexecLibOrders_v5.WorkerpoolOrder memory workerpoolorder, | ||
| IexecLibOrders_v5.RequestOrder memory requestorder | ||
| ) = abi.decode( | ||
| data, | ||
| ( | ||
| IexecLibOrders_v5.AppOrder, | ||
| IexecLibOrders_v5.DatasetOrder, | ||
| IexecLibOrders_v5.WorkerpoolOrder, | ||
| IexecLibOrders_v5.RequestOrder | ||
| ) | ||
| ); | ||
| function _executeOperation(address sender, bytes calldata data) internal { | ||
| // Extract the function selector (first 4 bytes) | ||
| bytes4 selector = bytes4(data[:4]); | ||
|
|
||
| // Validate that sender is the requester | ||
| if (requestorder.requester != sender) revert("caller-must-be-requester"); | ||
| // Validate operation-specific preconditions before execution | ||
| if (selector == IexecPoco1.matchOrders.selector) { | ||
| _validateMatchOrders(sender, data); | ||
| } else { | ||
| revert("unsupported-operation"); | ||
| } | ||
|
|
||
| // Call matchOrders on the IexecPoco1 facet through the diamond | ||
| // Using delegatecall for safety: preserves msg.sender context (RLC address in this case) | ||
| // Note: matchOrders doesn't use msg.sender, but delegatecall is safer | ||
| // in case the implementation changes in the future | ||
| (bool success, bytes memory result) = address(this).delegatecall( | ||
| abi.encodeWithSelector( | ||
| IexecPoco1.matchOrders.selector, | ||
| apporder, | ||
| datasetorder, | ||
| workerpoolorder, | ||
| requestorder | ||
| ) | ||
| ); | ||
| // Execute the operation via delegatecall | ||
| // This preserves msg.sender context and allows the operation to access | ||
| // the diamond's storage and functions | ||
| (bool success, bytes memory result) = address(this).delegatecall(data); | ||
|
|
||
| // Handle failure and bubble up revert reason | ||
| if (!success) { | ||
|
|
@@ -172,11 +160,39 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2 | |
| revert(add(result, 32), returndata_size) | ||
| } | ||
| } else { | ||
| revert("receive-approval-failed"); | ||
| revert("operation-failed"); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /****************************************************************************** | ||
| * Token Spender: Atomic Deposit+Match if used with RLC.approveAndCall * | ||
| *****************************************************************************/ | ||
|
|
||
| /** | ||
| * @dev Validates matchOrders preconditions | ||
| * @param sender The user who deposited (must be the requester) | ||
| * @param data ABI-encoded matchOrders call with orders | ||
| */ | ||
| function _validateMatchOrders(address sender, bytes calldata data) internal pure { | ||
| // Decode only the request order to validate the requester | ||
| // Full decoding: (AppOrder, DatasetOrder, WorkerpoolOrder, RequestOrder) | ||
| // We only need to check requestorder.requester | ||
| (, , , IexecLibOrders_v5.RequestOrder memory requestorder) = abi.decode( | ||
| data[4:], | ||
|
Comment on lines
+181
to
+182
|
||
| ( | ||
| IexecLibOrders_v5.AppOrder, | ||
| IexecLibOrders_v5.DatasetOrder, | ||
| IexecLibOrders_v5.WorkerpoolOrder, | ||
| IexecLibOrders_v5.RequestOrder | ||
| ) | ||
| ); | ||
|
|
||
| // Validate that sender is the requester | ||
| // This ensures the caller is authorized to create this deal | ||
| if (requestorder.requester != sender) revert("caller-must-be-requester"); | ||
| } | ||
|
|
||
| function _deposit(address from, uint256 amount) internal { | ||
| PocoStorageLib.PocoStorage storage $ = PocoStorageLib.getPocoStorage(); | ||
| require($.m_baseToken.transferFrom(from, address(this), amount), "failed-transferFrom"); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function is marked as
purebut it performsabi.decodeon calldata. While this is technically allowed in newer Solidity versions, the function should be marked asviewfor clarity and consistency, as it reads from calldata which is external data.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Function state mutability can be restricted to puresolidity(2018)