Skip to content

Commit 81778ee

Browse files
authored
feat: Use custom errors when receiveApproval fails and set callbackgas to 200k (#331)
1 parent 52e8fc7 commit 81778ee

File tree

16 files changed

+170
-183
lines changed

16 files changed

+170
-183
lines changed

abis/contracts/IexecInterfaceToken.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
[
2+
{
3+
"inputs": [],
4+
"name": "CallerIsNotTheRequester",
5+
"type": "error"
6+
},
27
{
38
"inputs": [
49
{
@@ -10,6 +15,22 @@
1015
"name": "IncompatibleDatasetOrder",
1116
"type": "error"
1217
},
18+
{
19+
"inputs": [],
20+
"name": "OperationFailed",
21+
"type": "error"
22+
},
23+
{
24+
"inputs": [
25+
{
26+
"internalType": "bytes4",
27+
"name": "selector",
28+
"type": "bytes4"
29+
}
30+
],
31+
"name": "UnsupportedOperation",
32+
"type": "error"
33+
},
1334
{
1435
"anonymous": false,
1536
"inputs": [

abis/contracts/facets/IexecEscrowTokenFacet.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,25 @@
11
[
2+
{
3+
"inputs": [],
4+
"name": "CallerIsNotTheRequester",
5+
"type": "error"
6+
},
7+
{
8+
"inputs": [],
9+
"name": "OperationFailed",
10+
"type": "error"
11+
},
12+
{
13+
"inputs": [
14+
{
15+
"internalType": "bytes4",
16+
"name": "selector",
17+
"type": "bytes4"
18+
}
19+
],
20+
"name": "UnsupportedOperation",
21+
"type": "error"
22+
},
223
{
324
"anonymous": false,
425
"inputs": [

abis/contracts/interfaces/IexecEscrowToken.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,25 @@
11
[
2+
{
3+
"inputs": [],
4+
"name": "CallerIsNotTheRequester",
5+
"type": "error"
6+
},
7+
{
8+
"inputs": [],
9+
"name": "OperationFailed",
10+
"type": "error"
11+
},
12+
{
13+
"inputs": [
14+
{
15+
"internalType": "bytes4",
16+
"name": "selector",
17+
"type": "bytes4"
18+
}
19+
],
20+
"name": "UnsupportedOperation",
21+
"type": "error"
22+
},
223
{
324
"stateMutability": "payable",
425
"type": "fallback"

abis/human-readable-abis/contracts/IexecInterfaceToken.sol/IexecInterfaceToken.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
[
2+
"error CallerIsNotTheRequester()",
23
"error IncompatibleDatasetOrder(string)",
4+
"error OperationFailed()",
5+
"error UnsupportedOperation(bytes4)",
36
"event AccurateContribution(address indexed,bytes32 indexed)",
47
"event Approval(address indexed,address indexed,uint256)",
58
"event BroadcastAppOrder(tuple(address,uint256,uint256,bytes32,address,address,address,bytes32,bytes))",

abis/human-readable-abis/contracts/facets/IexecEscrowTokenFacet.sol/IexecEscrowTokenFacet.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
[
2+
"error CallerIsNotTheRequester()",
3+
"error OperationFailed()",
4+
"error UnsupportedOperation(bytes4)",
25
"event Approval(address indexed,address indexed,uint256)",
36
"event Lock(address,uint256)",
47
"event Reward(address,uint256,bytes32)",

abis/human-readable-abis/contracts/interfaces/IexecEscrowToken.sol/IexecEscrowToken.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
[
2+
"error CallerIsNotTheRequester()",
3+
"error OperationFailed()",
4+
"error UnsupportedOperation(bytes4)",
25
"function deposit(uint256) returns (bool)",
36
"function depositFor(uint256,address) returns (bool)",
47
"function depositForArray(uint256[],address[]) returns (bool)",

contracts/facets/IexecConfigurationFacet.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ contract IexecConfigurationFacet is IexecConfiguration, FacetBase {
3737
$.m_datasetregistry = IRegistry(_datasetregistryAddress);
3838
$.m_workerpoolregistry = IRegistry(_workerpoolregistryAddress);
3939
$.m_v3_iexecHub = IexecHubV3Interface(_v3_iexecHubAddress);
40-
$.m_callbackgas = 100000;
40+
$.m_callbackgas = 200_000;
4141
}
4242

4343
function domain() external view override returns (IexecLibOrders_v5.EIP712Domain memory) {

contracts/facets/IexecEscrowTokenFacet.sol

Lines changed: 30 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
6666
}
6767

6868
/***************************************************************************
69-
* Token Spender: Atomic Deposit+Match *
69+
* Token Spender: Atomic Deposit + Match *
7070
***************************************************************************/
7171

7272
/**
73-
* @notice Receives approval, deposit and optionally executes an operation in one transaction
73+
* @notice Receives approval, deposit and optionally executes a supported operation in one transaction.
7474
*
7575
* Usage patterns:
7676
* 1. Simple deposit: RLC.approveAndCall(escrow, amount, "")
@@ -82,21 +82,21 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
8282
*
8383
* @dev Implementation details:
8484
* - 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
85+
* - Extracts function selector from data to determine the operation
86+
* - Each operation has a validator (_validateMatchOrders, etc.) to check preconditions
8787
* - After validation, _executeOperation performs the delegatecall
88-
* - Error handling is generalized: bubbles up revert reasons or returns 'operation-failed'
88+
* - Error handling is generalized: reverts are bubbled up with revert reasons or custom errors
8989
* - Future operations can be added by implementing a validator and adding a selector case
9090
*
9191
* @dev matchOrders specific notes:
92-
* - Sponsoring is NOT supported. The requester (sender) always pays for the deal.
92+
* - Sponsoring is NOT supported. The requester (specified in the request order) always pays for the deal.
9393
* - Clients must compute the exact deal cost and deposit the right amount.
9494
* The deal cost = (appPrice + datasetPrice + workerpoolPrice) * volume.
9595
*
9696
* @param sender The address that approved tokens
9797
* @param amount Amount of tokens approved and to be deposited
9898
* @param token Address of the token (must be RLC)
99-
* @param data Optional: Function selector + ABI-encoded parameters for operation
99+
* @param data Optional: Function selector + ABI-encoded parameters
100100
* @return success True if operation succeeded
101101
*
102102
*
@@ -114,7 +114,7 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
114114
* requestOrder
115115
* );
116116
*
117-
* // One transaction does it all: approve, deposit, and match
117+
* // Call the RLC contract with the encoded data.
118118
* RLC(token).approveAndCall(iexecProxy, dealCost, data);
119119
* ```
120120
*/
@@ -128,56 +128,52 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
128128
require(token == address($.m_baseToken), "wrong-token");
129129
_deposit(sender, amount);
130130
_mint(sender, amount);
131-
132131
if (data.length > 0) {
133132
_executeOperation(sender, data);
134133
}
135134
return true;
136135
}
137136

137+
/**
138+
* Executes a supported operation after depositing tokens.
139+
* @param sender The address that approved tokens and initiated the operation
140+
* @param data ABI-encoded function selector and parameters of the operation
141+
*/
138142
function _executeOperation(address sender, bytes calldata data) internal {
139143
// Extract the function selector (first 4 bytes)
140144
bytes4 selector = bytes4(data[:4]);
141-
142145
// Validate operation-specific preconditions before execution
143146
if (selector == IexecPoco1.matchOrders.selector) {
144147
_validateMatchOrders(sender, data);
145148
} else {
146-
revert("unsupported-operation");
149+
revert UnsupportedOperation(selector);
147150
}
148-
149151
// 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+
// This preserves `msg.sender` context and allows the operation to access
153+
// the diamond's storage and functions.
154+
// Note: here `msg.sender` is the RLC token contract.
152155
(bool success, bytes memory result) = address(this).delegatecall(data);
153-
156+
if (success) {
157+
return;
158+
}
154159
// Handle failure and bubble up revert reason
155-
if (!success) {
156-
if (result.length > 0) {
157-
// Decode and revert with the original error
158-
assembly {
159-
let returndata_size := mload(result)
160-
revert(add(result, 32), returndata_size)
161-
}
162-
} else {
163-
revert("operation-failed");
164-
}
160+
if (result.length == 0) {
161+
revert OperationFailed();
162+
}
163+
// Decode and revert with the original error
164+
assembly {
165+
let returndata_size := mload(result)
166+
revert(add(result, 32), returndata_size)
165167
}
166168
}
167169

168-
/******************************************************************************
169-
* Token Spender: Atomic Deposit+Match if used with RLC.approveAndCall *
170-
*****************************************************************************/
171-
172170
/**
173171
* @dev Validates matchOrders preconditions
174172
* @param sender The user who deposited (must be the requester)
175-
* @param data ABI-encoded matchOrders call with orders
173+
* @param data matchOrders calldata
176174
*/
177175
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
176+
// Decode orders and check that the sender is the requester.
181177
(, , , IexecLibOrders_v5.RequestOrder memory requestorder) = abi.decode(
182178
data[4:],
183179
(
@@ -187,10 +183,8 @@ contract IexecEscrowTokenFacet is IexecEscrowToken, IexecTokenSpender, IexecERC2
187183
IexecLibOrders_v5.RequestOrder
188184
)
189185
);
190-
// Validate that sender is the requester
191-
// This ensures the caller is authorized to create this deal
192186
if (requestorder.requester != sender) {
193-
revert("caller-must-be-requester");
187+
revert CallerIsNotTheRequester();
194188
}
195189
}
196190

contracts/interfaces/IexecEscrowToken.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44
pragma solidity ^0.8.0;
55

66
interface IexecEscrowToken {
7+
error UnsupportedOperation(bytes4 selector);
8+
error OperationFailed();
9+
error CallerIsNotTheRequester();
10+
711
receive() external payable;
812
fallback() external payable;
913
function deposit(uint256) external returns (bool);

docs/solidity/index.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ function recover() external returns (uint256)
252252
function receiveApproval(address sender, uint256 amount, address token, bytes data) external returns (bool)
253253
```
254254

255-
Receives approval, deposit and optionally executes an operation in one transaction
255+
Receives approval, deposit and optionally executes a supported operation in one transaction.
256256

257257
Usage patterns:
258258
1. Simple deposit: RLC.approveAndCall(escrow, amount, "")
@@ -264,14 +264,14 @@ the operation, followed by ABI-encoded parameters. Supported operations:
264264

265265
_Implementation details:
266266
- Deposits tokens first, then executes the operation if data is provided
267-
- Extracts function selector from data to determine which operation
268-
- Each operation has a validator (_validateMatchOrders, etc.) for preconditions
267+
- Extracts function selector from data to determine the operation
268+
- Each operation has a validator (_validateMatchOrders, etc.) to check preconditions
269269
- After validation, _executeOperation performs the delegatecall
270-
- Error handling is generalized: bubbles up revert reasons or returns 'operation-failed'
270+
- Error handling is generalized: reverts are bubbled up with revert reasons or custom errors
271271
- Future operations can be added by implementing a validator and adding a selector case
272272

273273
matchOrders specific notes:
274-
- Sponsoring is NOT supported. The requester (sender) always pays for the deal.
274+
- Sponsoring is NOT supported. The requester (specified in the request order) always pays for the deal.
275275
- Clients must compute the exact deal cost and deposit the right amount.
276276
The deal cost = (appPrice + datasetPrice + workerpoolPrice) * volume._
277277

@@ -282,13 +282,13 @@ matchOrders specific notes:
282282
| sender | address | The address that approved tokens |
283283
| amount | uint256 | Amount of tokens approved and to be deposited |
284284
| token | address | Address of the token (must be RLC) |
285-
| data | bytes | Optional: Function selector + ABI-encoded parameters for operation |
285+
| data | bytes | Optional: Function selector + ABI-encoded parameters |
286286

287287
#### Return Values
288288

289289
| Name | Type | Description |
290290
| ---- | ---- | ----------- |
291-
| [0] | bool | success True if operation succeeded @custom:example ```solidity // Compute deal cost uint256 dealCost = (appPrice + datasetPrice + workerpoolPrice) * volume; // Encode matchOrders operation with selector bytes memory data = abi.encodeWithSelector( IexecPoco1.matchOrders.selector, appOrder, datasetOrder, workerpoolOrder, requestOrder ); // One transaction does it all: approve, deposit, and match RLC(token).approveAndCall(iexecProxy, dealCost, data); ``` |
291+
| [0] | bool | success True if operation succeeded @custom:example ```solidity // Compute deal cost uint256 dealCost = (appPrice + datasetPrice + workerpoolPrice) * volume; // Encode matchOrders operation with selector bytes memory data = abi.encodeWithSelector( IexecPoco1.matchOrders.selector, appOrder, datasetOrder, workerpoolOrder, requestOrder ); // Call the RLC contract with the encoded data. RLC(token).approveAndCall(iexecProxy, dealCost, data); ``` |
292292

293293
## IexecOrderManagementFacet
294294

0 commit comments

Comments
 (0)