@@ -14,18 +14,18 @@ import {BatchCompact, Lock} from '@uniswap/the-compact/types/EIP712Types.sol';
14
14
15
15
contract ERC7683Allocator is OnChainAllocator , IERC7683Allocator {
16
16
/// @notice The typehash of the OrderDataOnChain struct
17
- // keccak256("OrderDataOnChain(address arbiter,uint256 expires, Order order,uint200 targetBlock,uint56 maximumBlocksAfterTarget )
17
+ // keccak256("OrderDataOnChain(Order order,uint256 expires )
18
18
// Lock(bytes12 lockTag,address token,uint256 amount)
19
- // Order(Lock[] commitments,uint256 chainId,address tribunal,address recipient,address settlementToken,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt)")
19
+ // Order(address arbiter, Lock[] commitments,uint256 chainId,address tribunal,address recipient,address settlementToken,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt,bytes32 qualification )")
20
20
bytes32 public constant ORDERDATA_ONCHAIN_TYPEHASH =
21
- 0x95d7f00c299b34a562258ba851472a8d9bd0d8a1b88fce3a37b7d27ca06e77c4 ;
21
+ 0x037a34e1ded3bcc84f59dfc185efc3553c509ebab317153a8dddefce2eaee6f0 ;
22
22
23
23
/// @notice The typehash of the OrderDataGasless struct
24
- // keccak256("OrderDataGasless(address arbiter, Order order)
24
+ // keccak256("OrderDataGasless(Order order,bool deposit )
25
25
// Lock(bytes12 lockTag,address token,uint256 amount)
26
- // Order(Lock[] commitments,uint256 chainId,address tribunal,address recipient,address settlementToken,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt)")
26
+ // Order(address arbiter, Lock[] commitments,uint256 chainId,address tribunal,address recipient,address settlementToken,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt,bytes32 qualification )")
27
27
bytes32 public constant ORDERDATA_GASLESS_TYPEHASH =
28
- 0xdebd9e7866045b7f0ce8613ffbb31daa3fa5c6e6ac228316ba9f57fda63b7489 ;
28
+ 0x79e4af6feaa84a46fd69ed25e4595e9f6e8690ba3a6c564bfa235542f9faf55c ;
29
29
30
30
/// @notice keccak256("BatchCompact(address arbiter,address sponsor,uint256 nonce,uint256 expires,Lock[] commitments,Mandate mandate)
31
31
// Lock(bytes12 lockTag,address token,uint256 amount)
@@ -41,32 +41,49 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
41
41
constructor (address compactContract_ ) OnChainAllocator (compactContract_) {}
42
42
43
43
/// @inheritdoc IOriginSettler
44
- function openFor (GaslessCrossChainOrder calldata order_ , bytes calldata sponsorSignature_ , bytes calldata )
45
- external
46
- {
44
+ function openFor (
45
+ GaslessCrossChainOrder calldata order_ ,
46
+ bytes calldata sponsorSignature_ ,
47
+ bytes calldata fillerData
48
+ ) external {
47
49
// Check if orderDataType is the one expected by the allocator
48
50
if (order_.orderDataType != ORDERDATA_GASLESS_TYPEHASH) {
49
51
revert InvalidOrderDataType (order_.orderDataType, ORDERDATA_GASLESS_TYPEHASH);
50
52
}
51
- /// TODO: Potentially useless check, since the allocator Id gets checked later.
52
53
if (order_.originSettler != address (this )) {
53
54
revert InvalidOriginSettler (order_.originSettler, address (this ));
54
55
}
56
+
57
+ // Decode the orderData
58
+ (Order calldata orderData , uint32 deposit ) = _decodeOrderData (order_.orderData);
59
+
60
+ uint160 caller = uint160 (deposit * uint160 (msg .sender )); // for a deposit, the nonce will be scoped to the caller + user
61
+ bytes32 nonceIdentifier = _toNonceId (address (caller), order_.user);
62
+
55
63
// Early revert if the expected nonce is not the next nonce
56
- if (order_.nonce != nonces[order_.user ] + 1 ) {
57
- revert InvalidNonce (order_.nonce, nonces[order_.user ] + 1 );
64
+ if (order_.nonce != nonces[nonceIdentifier ] + 1 ) {
65
+ revert InvalidNonce (order_.nonce, nonces[nonceIdentifier ] + 1 );
58
66
}
59
67
60
- // Decode the orderData
61
- (, Order calldata orderData ,,) = _decodeOrderData (order_.orderData, false );
68
+ bytes32 qualification = bytes32 (uint256 (orderData.qualification) * deposit); // delete qualification if not a deposit
62
69
63
70
ResolvedCrossChainOrder memory resolvedOrder = _resolveOrder (
64
- order_.user, order_.nonce, order_.openDeadline, order_.fillDeadline, orderData, sponsorSignature_, 0 , 0
71
+ order_.user,
72
+ order_.nonce,
73
+ order_.openDeadline,
74
+ order_.fillDeadline,
75
+ orderData,
76
+ sponsorSignature_,
77
+ qualification
65
78
);
66
79
67
80
bytes32 mandateHash = _mandateHash (orderData, order_.fillDeadline);
68
81
69
- _open (order_.user, order_.openDeadline, orderData, sponsorSignature_, mandateHash, bytes32 (0 ), resolvedOrder);
82
+ if (deposit == 0 ) {
83
+ _open (order_.user, order_.openDeadline, orderData, sponsorSignature_, mandateHash, resolvedOrder);
84
+ } else {
85
+ _openAndRegister (order_.user, order_.openDeadline, orderData, mandateHash, resolvedOrder);
86
+ }
70
87
}
71
88
72
89
/// @inheritdoc IOriginSettler
@@ -77,31 +94,21 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
77
94
}
78
95
79
96
// Decode the orderData
80
- (uint32 expires , Order calldata orderData , uint200 targetBlock , uint56 maximumBlocksAfterTarget ) =
81
- _decodeOrderData (order.orderData, true );
97
+ (Order calldata orderData , uint32 expires ) = _decodeOrderData (order.orderData);
82
98
83
99
bytes32 mandateHash = _mandateHash (orderData, order.fillDeadline);
84
100
85
101
ResolvedCrossChainOrder memory resolvedOrder = _resolveOrder (
86
102
msg .sender ,
87
- nonces[msg .sender ] + 1 ,
103
+ nonces[_toNonceId ( address ( 0 ), msg .sender ) ] + 1 ,
88
104
expires,
89
105
order.fillDeadline,
90
106
orderData,
91
107
LibBytes.emptyCalldata (),
92
- targetBlock,
93
- maximumBlocksAfterTarget
108
+ orderData.qualification
94
109
);
95
110
96
- _open (
97
- msg .sender ,
98
- expires,
99
- orderData,
100
- LibBytes.emptyCalldata (),
101
- mandateHash,
102
- bytes32 (abi.encodePacked (targetBlock, maximumBlocksAfterTarget)),
103
- resolvedOrder
104
- );
111
+ _open (msg .sender , expires, orderData, LibBytes.emptyCalldata (), mandateHash, resolvedOrder);
105
112
}
106
113
107
114
/// @inheritdoc IOriginSettler
@@ -114,17 +121,22 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
114
121
if (order_.orderDataType != ORDERDATA_GASLESS_TYPEHASH) {
115
122
revert InvalidOrderDataType (order_.orderDataType, ORDERDATA_GASLESS_TYPEHASH);
116
123
}
117
- /// TODO: Potentially useless check, since the allocator Id gets checked later.
118
124
if (order_.originSettler != address (this )) {
119
125
revert InvalidOriginSettler (order_.originSettler, address (this ));
120
126
}
127
+
128
+ // Decode the orderData
129
+ (Order calldata orderData , uint32 deposit ) = _decodeOrderData (order_.orderData);
130
+
131
+ uint160 caller = uint160 (deposit * uint160 (msg .sender )); // for a deposit, the nonce will be scoped to the caller + user
132
+ bytes32 nonceIdentifier = _toNonceId (address (caller), order_.user);
133
+
121
134
// Early revert if the expected nonce is not the next nonce
122
- if (order_.nonce != nonces[order_.user ] + 1 ) {
123
- revert InvalidNonce (order_.nonce, nonces[order_.user ] + 1 );
135
+ if (order_.nonce != nonces[nonceIdentifier ] + 1 ) {
136
+ revert InvalidNonce (order_.nonce, nonces[nonceIdentifier ] + 1 );
124
137
}
125
138
126
- // Decode the orderData
127
- (, Order calldata orderData ,,) = _decodeOrderData (order_.orderData, false );
139
+ bytes32 qualification = bytes32 (uint256 (orderData.qualification) * deposit);
128
140
129
141
return _resolveOrder (
130
142
order_.user,
@@ -133,8 +145,7 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
133
145
order_.fillDeadline,
134
146
orderData,
135
147
LibBytes.emptyCalldata (),
136
- 0 ,
137
- 0
148
+ qualification
138
149
);
139
150
}
140
151
@@ -146,18 +157,16 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
146
157
}
147
158
148
159
// Decode the orderData
149
- (uint32 expires , Order calldata orderData , uint200 targetBlock , uint56 maximumBlocksAfterTarget ) =
150
- _decodeOrderData (order.orderData, true );
160
+ (Order calldata orderData , uint32 expires ) = _decodeOrderData (order.orderData);
151
161
152
162
return _resolveOrder (
153
163
msg .sender ,
154
- nonces[msg .sender ] + 1 ,
164
+ nonces[_toNonceId ( address ( 0 ), msg .sender ) ] + 1 ,
155
165
expires,
156
166
order.fillDeadline,
157
167
orderData,
158
168
LibBytes.emptyCalldata (),
159
- targetBlock,
160
- maximumBlocksAfterTarget
169
+ orderData.qualification
161
170
);
162
171
}
163
172
@@ -206,8 +215,18 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
206
215
}
207
216
208
217
/// @inheritdoc IERC7683Allocator
209
- function checkNonce (uint256 nonce_ , address sponsor_ ) external view returns (bool nonceValid ) {
210
- return nonces[sponsor_] + 1 == nonce_;
218
+ function checkNonce (GaslessCrossChainOrder calldata order_ , address caller )
219
+ external
220
+ view
221
+ returns (bool nonceValid )
222
+ {
223
+ (, uint32 deposit ) = _decodeOrderData (order_.orderData);
224
+
225
+ caller = address (uint160 (deposit * uint160 (caller))); // for a deposit, the nonce will be scoped to the caller + user
226
+ bytes32 nonceIdentifier = _toNonceId (caller, order_.user);
227
+
228
+ // Early revert if the expected nonce is not the next nonce
229
+ return (order_.nonce == nonces[nonceIdentifier] + 1 );
211
230
}
212
231
213
232
/// @inheritdoc IERC7683Allocator
@@ -219,10 +238,9 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
219
238
address sponsor ,
220
239
uint32 expires ,
221
240
Order calldata orderData ,
222
- bytes calldata sponsorSignature_ ,
223
- bytes32 mandateHash_ ,
224
- bytes32 qualification_ ,
225
- ResolvedCrossChainOrder memory resolvedOrder_
241
+ bytes calldata sponsorSignature ,
242
+ bytes32 mandateHash ,
243
+ ResolvedCrossChainOrder memory resolvedOrder
226
244
) internal {
227
245
// Register the allocation on chain
228
246
(bytes32 claimHash , uint256 nonce ) = allocateFor (
@@ -231,27 +249,54 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
231
249
orderData.arbiter,
232
250
expires,
233
251
BATCH_COMPACT_WITNESS_TYPEHASH,
234
- mandateHash_,
235
- sponsorSignature_
252
+ mandateHash,
253
+ sponsorSignature
254
+ );
255
+
256
+ if (sponsor == msg .sender && orderData.qualification != bytes32 (0 )) {
257
+ qualifications[claimHash] = orderData.qualification;
258
+ }
259
+
260
+ // Emit an open event
261
+ emit Open (bytes32 (nonce), resolvedOrder);
262
+ }
263
+
264
+ function _openAndRegister (
265
+ address sponsor ,
266
+ uint32 expires ,
267
+ Order calldata orderData ,
268
+ bytes32 mandateHash_ ,
269
+ ResolvedCrossChainOrder memory resolvedOrder_
270
+ ) internal {
271
+ // Register the allocation on chain
272
+ (bytes32 claimHash , uint256 [] memory registeredAmounts , uint256 nonce ) = allocateAndRegister (
273
+ sponsor, orderData.commitments, orderData.arbiter, expires, BATCH_COMPACT_WITNESS_TYPEHASH, mandateHash_
236
274
);
237
275
238
- qualifications[claimHash] = qualification_;
276
+ for (uint256 i = 0 ; i < registeredAmounts.length ; i++ ) {
277
+ if (registeredAmounts[i] != orderData.commitments[i].amount) {
278
+ // FOT tokens unsupported
279
+ revert UnsupportedToken (orderData.commitments[i].token);
280
+ }
281
+ }
282
+
283
+ if (orderData.qualification != bytes32 (0 )) {
284
+ qualifications[claimHash] = orderData.qualification;
285
+ }
239
286
240
287
// Emit an open event
241
288
emit Open (bytes32 (nonce), resolvedOrder_);
242
289
}
243
290
244
- function _decodeOrderData (bytes calldata orderData , bool onChain )
291
+ function _decodeOrderData (bytes calldata orderData )
245
292
internal
246
293
pure
247
- returns (uint32 expires , Order calldata order , uint200 targetBlock , uint56 maximumBlocksAfterTarget )
294
+ returns (Order calldata order , uint32 additionalInput )
248
295
{
249
296
// orderData includes the OrderData(OnChain/Gasless) struct, and the nested Order struct.
250
297
// 0x00: OrderDataOnChain.offset
251
298
// 0x20: OrderDataOnChain.order.offset
252
299
// 0x40: OrderDataOnChain.expires
253
- // 0x60: OrderDataOnChain.targetBlock
254
- // 0x80: OrderDataOnChain.maximumBlocksAfterTarget
255
300
256
301
// 0x00: OrderDataGasless.offset
257
302
// 0x20: OrderDataGasless.order.offset
@@ -262,9 +307,7 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
262
307
order := add (orderData.offset, add (s, 0x20 )) // Add 0x20 since the OrderStruct is within the `OrderData...` struct
263
308
if shr (64 , or (s, or (l, orderData.offset))) { revert (l, 0x00 ) }
264
309
265
- expires := mul (calldataload (add (orderData.offset, 0x40 )), onChain)
266
- targetBlock := mul (calldataload (add (orderData.offset, 0x60 )), onChain)
267
- maximumBlocksAfterTarget := mul (calldataload (add (orderData.offset, 0x80 )), onChain)
310
+ additionalInput := calldataload (add (orderData.offset, 0x40 ))
268
311
}
269
312
}
270
313
@@ -275,8 +318,7 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
275
318
uint32 fillDeadline ,
276
319
Order calldata orderData ,
277
320
bytes calldata sponsorSignature ,
278
- uint200 targetBlock ,
279
- uint56 maximumBlocksAfterTarget
321
+ bytes32 qualification
280
322
) internal view returns (ResolvedCrossChainOrder memory ) {
281
323
ResolvedCrossChainOrder memory resolvedOrder = ResolvedCrossChainOrder ({
282
324
user: sponsor,
@@ -319,7 +361,7 @@ contract ERC7683Allocator is OnChainAllocator, IERC7683Allocator {
319
361
fillInstructions[0 ] = FillInstruction ({
320
362
destinationChainId: orderData.chainId,
321
363
destinationSettler: _addressToBytes32 (orderData.tribunal),
322
- originData: abi.encode (claim, mandate, targetBlock, maximumBlocksAfterTarget )
364
+ originData: abi.encode (claim, mandate, uint200 ( bytes25 (qualification)), uint56 ( uint256 (qualification)) )
323
365
});
324
366
resolvedOrder.fillInstructions = fillInstructions;
325
367
0 commit comments