@@ -29,9 +29,10 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
29
29
/// @notice keccak256("Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,bytes32 salt)")
30
30
bytes32 internal constant MANDATE_TYPEHASH = 0x52c75464356e20084ae43acac75087fbf0e0c678e7ffa326f369f37e88696036 ;
31
31
32
- bytes32 immutable _COMPACT_DOMAIN_SEPARATOR;
32
+ /// @notice uint256(uint8(keccak256("ERC7683Allocator.nonce")))
33
+ uint8 internal constant NONCE_MASTER_SLOT_SEED = 0x39 ;
33
34
34
- mapping ( uint256 nonce = > bool nonceUsed ) private _userNonce ;
35
+ bytes32 immutable _COMPACT_DOMAIN_SEPARATOR ;
35
36
36
37
constructor (address compactContract_ , uint256 minWithdrawalDelay_ , uint256 maxWithdrawalDelay_ )
37
38
SimpleAllocator (compactContract_, minWithdrawalDelay_, maxWithdrawalDelay_)
@@ -105,22 +106,24 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
105
106
106
107
/// @inheritdoc IERC7683Allocator
107
108
function checkNonce (address sponsor_ , uint256 nonce_ ) external view returns (bool nonceFree_ ) {
108
- _checkNonce (sponsor_, nonce_);
109
- nonceFree_ = ! _userNonce[nonce_];
109
+ uint96 nonceWithoutAddress = _checkNonce (sponsor_, nonce_);
110
+ uint96 wordPos = uint96 (nonceWithoutAddress / 256 );
111
+ uint96 bitPos = uint96 (nonceWithoutAddress % 256 );
112
+ assembly ("memory-safe" ) {
113
+ let masterSlot := or (shl (248 , NONCE_MASTER_SLOT_SEED), or (shl (88 , sponsor_), wordPos))
114
+ nonceFree_ := iszero (and (sload (masterSlot), shl (bitPos, 1 )))
115
+ }
110
116
return nonceFree_;
111
117
}
112
118
113
119
function _open (OrderData memory orderData_ , uint32 fillDeadline_ , address sponsor_ , bytes memory sponsorSignature_ )
114
120
internal
115
121
{
116
122
// Enforce a nonce where the most significant 96 bits are the nonce and the least significant 160 bits are the sponsor
117
- _checkNonce (sponsor_, orderData_.nonce);
123
+ uint96 nonceWithoutAddress = _checkNonce (sponsor_, orderData_.nonce);
118
124
119
- // Check the nonce
120
- if (_userNonce[orderData_.nonce]) {
121
- revert NonceAlreadyInUse (orderData_.nonce);
122
- }
123
- _userNonce[orderData_.nonce] = true ;
125
+ // Set a nonce or revert if it is already used
126
+ _setNonce (sponsor_, nonceWithoutAddress);
124
127
125
128
// We do not enforce a specific tribunal or arbiter. This will allow to support new arbiters and tribunals after the deployment of the allocator
126
129
// Going with an immutable arbiter and tribunal would limit support for new chains with a fully decentralized allocator
@@ -180,36 +183,31 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
180
183
);
181
184
}
182
185
183
- function _lockTokens (OrderData memory orderData_ , address sponsor_ , uint256 identifier )
186
+ function _lockTokens (OrderData memory orderData_ , address sponsor_ , uint256 nonce )
184
187
internal
185
188
returns (bytes32 tokenHash_ )
186
189
{
187
- return
188
- _lockTokens (orderData_.arbiter, sponsor_, identifier, orderData_.expires, orderData_.id, orderData_.amount);
190
+ return _lockTokens (orderData_.arbiter, sponsor_, nonce, orderData_.expires, orderData_.id, orderData_.amount);
189
191
}
190
192
191
- function _lockTokens (
192
- address arbiter ,
193
- address sponsor ,
194
- uint256 identifier ,
195
- uint256 expires ,
196
- uint256 id ,
197
- uint256 amount
198
- ) internal returns (bytes32 tokenHash_ ) {
193
+ function _lockTokens (address arbiter , address sponsor , uint256 nonce , uint256 expires , uint256 id , uint256 amount )
194
+ internal
195
+ returns (bytes32 tokenHash_ )
196
+ {
199
197
tokenHash_ = _checkAllocation (
200
- Compact ({arbiter: arbiter, sponsor: sponsor, nonce: identifier , expires: expires, id: id, amount: amount})
198
+ Compact ({arbiter: arbiter, sponsor: sponsor, nonce: nonce , expires: expires, id: id, amount: amount})
201
199
);
202
200
_claim[tokenHash_] = expires;
203
201
_amount[tokenHash_] = amount;
204
- _nonce[tokenHash_] = identifier ;
202
+ _nonce[tokenHash_] = nonce ;
205
203
206
204
return tokenHash_;
207
205
}
208
206
209
207
function _resolveOrder (
210
208
address sponsor ,
211
209
uint32 fillDeadline ,
212
- uint256 identifier ,
210
+ uint256 nonce ,
213
211
OrderData memory orderData ,
214
212
bytes memory sponsorSignature
215
213
) internal view returns (ResolvedCrossChainOrder memory ) {
@@ -267,26 +265,46 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
267
265
originChainId: block .chainid ,
268
266
openDeadline: uint32 (orderData.expires),
269
267
fillDeadline: fillDeadline,
270
- orderId: bytes32 (identifier ),
268
+ orderId: bytes32 (nonce ),
271
269
maxSpent: maxSpent,
272
270
minReceived: minReceived,
273
271
fillInstructions: fillInstructions
274
272
});
275
273
return resolvedOrder;
276
274
}
277
275
278
- function _checkNonce (address sponsor_ , uint256 nonce_ ) internal pure {
276
+ function _checkNonce (address sponsor_ , uint256 nonce_ ) internal pure returns ( uint96 nonce ) {
279
277
// Enforce a nonce where the least significant 96 bits are the nonce and the most significant 160 bits are the sponsors address
280
278
// This ensures that the nonce is unique for a given sponsor
281
279
address expectedSponsor;
282
280
assembly ("memory-safe" ) {
283
281
expectedSponsor := shr (96 , nonce_)
282
+ nonce := shr (160 , shl (160 , nonce_))
284
283
}
285
284
if (expectedSponsor != sponsor_) {
286
285
revert InvalidNonce (nonce_);
287
286
}
288
287
}
289
288
289
+ function _setNonce (address sponsor_ , uint96 nonce_ ) internal {
290
+ bool used;
291
+ uint96 wordPos = nonce_ / 256 ; // uint96 divided by 256 means it becomes a uint88 (11 bytes)
292
+ uint96 bitPos = nonce_ % 256 ;
293
+ assembly ("memory-safe" ) {
294
+ // [NONCE_MASTER_SLOT_SEED - 1 byte][sponsor address - 20 bytes][wordPos - 11 bytes]
295
+ let masterSlot := or (shl (248 , NONCE_MASTER_SLOT_SEED), or (shl (88 , sponsor_), wordPos))
296
+ let previouslyUsedNonces := sload (masterSlot)
297
+ if and (previouslyUsedNonces, shl (bitPos, 1 )) { used := 1 }
298
+ {
299
+ let usedNonces := or (previouslyUsedNonces, shl (bitPos, 1 ))
300
+ sstore (masterSlot, usedNonces)
301
+ }
302
+ }
303
+ if (used) {
304
+ revert NonceAlreadyInUse (uint256 (bytes32 (abi.encodePacked (sponsor_, nonce_))));
305
+ }
306
+ }
307
+
290
308
function _idToToken (uint256 id_ ) internal pure returns (address token_ ) {
291
309
assembly ("memory-safe" ) {
292
310
token_ := shr (96 , shl (96 , id_))
0 commit comments