@@ -9,8 +9,21 @@ import { ITheCompact } from "src/interfaces/ITheCompact.sol";
9
9
import { ISimpleAllocator } from "src/interfaces/ISimpleAllocator.sol " ;
10
10
import { Compact } from "src/types/EIP712Types.sol " ;
11
11
import { ResetPeriod } from "src/lib/IdLib.sol " ;
12
+ import { console } from "forge-std/console.sol " ;
12
13
13
14
contract SimpleAllocator is ISimpleAllocator {
15
+ // abi.decode(bytes("Compact(address arbiter,address "), (bytes32))
16
+ bytes32 constant COMPACT_TYPESTRING_FRAGMENT_ONE = 0x436f6d70616374286164647265737320617262697465722c6164647265737320 ;
17
+ // abi.decode(bytes("sponsor,uint256 nonce,uint256 ex"), (bytes32))
18
+ bytes32 constant COMPACT_TYPESTRING_FRAGMENT_TWO = 0x73706f6e736f722c75696e74323536206e6f6e63652c75696e74323536206578 ;
19
+ // abi.decode(bytes("pires,uint256 id,uint256 amount)"), (bytes32))
20
+ bytes32 constant COMPACT_TYPESTRING_FRAGMENT_THREE = 0x70697265732c75696e743235362069642c75696e7432353620616d6f756e7429 ;
21
+ // uint200(abi.decode(bytes(",Witness witness)Witness("), (bytes25)))
22
+ uint200 constant WITNESS_TYPESTRING = 0x2C5769746E657373207769746E657373295769746E65737328 ;
23
+
24
+ // keccak256("Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount)")
25
+ bytes32 constant COMPACT_TYPEHASH = 0xcdca950b17b5efc016b74b912d8527dfba5e404a688cbc3dab16cb943287fec2 ;
26
+
14
27
address public immutable COMPACT_CONTRACT;
15
28
address public immutable ARBITER;
16
29
uint256 public immutable MIN_WITHDRAWAL_DELAY;
@@ -36,51 +49,15 @@ contract SimpleAllocator is ISimpleAllocator {
36
49
37
50
/// @inheritdoc ISimpleAllocator
38
51
function lock (Compact calldata compact_ ) external {
39
- // Check msg.sender is sponsor
40
- if (msg .sender != compact_.sponsor) {
41
- revert InvalidCaller (msg .sender , compact_.sponsor);
42
- }
43
- bytes32 tokenHash = _getTokenHash (compact_.id, msg .sender );
44
- // Check no lock is already active for this sponsor
45
- if (_claim[tokenHash] > block .timestamp && ! ITheCompact (COMPACT_CONTRACT).hasConsumedAllocatorNonce (_nonce[tokenHash], address (this ))) {
46
- revert ClaimActive (compact_.sponsor);
47
- }
48
- // Check arbiter is valid
49
- if (compact_.arbiter != ARBITER) {
50
- revert InvalidArbiter (compact_.arbiter);
51
- }
52
- // Check expiration is not too soon or too late
53
- if (compact_.expires < block .timestamp + MIN_WITHDRAWAL_DELAY || compact_.expires > block .timestamp + MAX_WITHDRAWAL_DELAY) {
54
- revert InvalidExpiration (compact_.expires);
55
- }
56
- // Check expiration is not longer then the tokens forced withdrawal time
57
- (,, ResetPeriod resetPeriod ,) = ITheCompact (COMPACT_CONTRACT).getLockDetails (compact_.id);
58
- if (compact_.expires > block .timestamp + _resetPeriodToSeconds (resetPeriod)) {
59
- revert ForceWithdrawalAvailable (compact_.expires, block .timestamp + _resetPeriodToSeconds (resetPeriod));
60
- }
61
- // Check expiration is not past an active force withdrawal
62
- (, uint256 forcedWithdrawalExpiration ) = ITheCompact (COMPACT_CONTRACT).getForcedWithdrawalStatus (compact_.sponsor, compact_.id);
63
- if (forcedWithdrawalExpiration != 0 && forcedWithdrawalExpiration < compact_.expires) {
64
- revert ForceWithdrawalAvailable (compact_.expires, forcedWithdrawalExpiration);
65
- }
66
- // Check nonce is not yet consumed
67
- if (ITheCompact (COMPACT_CONTRACT).hasConsumedAllocatorNonce (compact_.nonce, address (this ))) {
68
- revert NonceAlreadyConsumed (compact_.nonce);
69
- }
70
-
71
- uint256 balance = ERC6909 (COMPACT_CONTRACT).balanceOf (msg .sender , compact_.id);
72
- // Check balance is enough
73
- if (balance < compact_.amount) {
74
- revert InsufficientBalance (msg .sender , compact_.id, balance, compact_.amount);
75
- }
52
+ bytes32 tokenHash = _checkAllocation (compact_);
76
53
77
54
bytes32 digest = keccak256 (
78
55
abi.encodePacked (
79
56
bytes2 (0x1901 ),
80
57
ITheCompact (COMPACT_CONTRACT).DOMAIN_SEPARATOR (),
81
58
keccak256 (
82
59
abi.encode (
83
- keccak256 ( " Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount) " ) ,
60
+ COMPACT_TYPEHASH ,
84
61
compact_.arbiter,
85
62
compact_.sponsor,
86
63
compact_.nonce,
@@ -100,6 +77,53 @@ contract SimpleAllocator is ISimpleAllocator {
100
77
emit Locked (compact_.sponsor, compact_.id, compact_.amount, compact_.expires);
101
78
}
102
79
80
+ /// @inheritdoc ISimpleAllocator
81
+ function lockWithWitness (Compact calldata compact_ , bytes32 typestringHash_ , bytes32 witnessHash_ ) external {
82
+ bytes32 tokenHash = _checkAllocation (compact_);
83
+
84
+ console.log ("claimHash SimpleAllocator " );
85
+ // console.logBytes32(claimHash);
86
+ console.log ("arbiter SimpleAllocator " );
87
+ console.logAddress (compact_.arbiter);
88
+ console.log ("sponsor SimpleAllocator " );
89
+ console.logAddress (compact_.sponsor);
90
+ console.log ("nonce SimpleAllocator " );
91
+ console.logUint (compact_.nonce);
92
+ console.log ("expires SimpleAllocator " );
93
+ console.logUint (compact_.expires);
94
+ console.log ("id SimpleAllocator " );
95
+ console.logUint (compact_.id);
96
+ console.log ("amount SimpleAllocator " );
97
+ console.logUint (compact_.amount);
98
+ bytes32 digest = keccak256 (
99
+ abi.encodePacked (
100
+ bytes2 (0x1901 ),
101
+ ITheCompact (COMPACT_CONTRACT).DOMAIN_SEPARATOR (),
102
+ keccak256 (
103
+ abi.encode (
104
+ typestringHash_, // keccak256("Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount,Witness witness)Witness(uint256 witnessArgument)")
105
+ compact_.arbiter,
106
+ compact_.sponsor,
107
+ compact_.nonce,
108
+ compact_.expires,
109
+ compact_.id,
110
+ compact_.amount,
111
+ witnessHash_
112
+ )
113
+ )
114
+ )
115
+ );
116
+ console.log ("digest SimpleAllocator " );
117
+ console.logBytes32 (digest);
118
+
119
+ _claim[tokenHash] = compact_.expires;
120
+ _amount[tokenHash] = compact_.amount;
121
+ _nonce[tokenHash] = compact_.nonce;
122
+ _sponsor[digest] = tokenHash;
123
+
124
+ emit Locked (compact_.sponsor, compact_.id, compact_.amount, compact_.expires);
125
+ }
126
+
103
127
/// @inheritdoc IAllocator
104
128
function attest (address operator_ , address from_ , address , uint256 id_ , uint256 amount_ ) external view returns (bytes4 ) {
105
129
if (msg .sender != COMPACT_CONTRACT) {
@@ -161,7 +185,7 @@ contract SimpleAllocator is ISimpleAllocator {
161
185
ITheCompact (COMPACT_CONTRACT).DOMAIN_SEPARATOR (),
162
186
keccak256 (
163
187
abi.encode (
164
- keccak256 ( " Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount) " ) ,
188
+ COMPACT_TYPEHASH ,
165
189
compact_.arbiter,
166
190
compact_.sponsor,
167
191
compact_.nonce,
@@ -177,10 +201,74 @@ contract SimpleAllocator is ISimpleAllocator {
177
201
return (active, active ? expires : 0 );
178
202
}
179
203
204
+ /// @dev example of a witness type string input:
205
+ /// "uint256 witnessArgument"
206
+ /// @dev full typestring:
207
+ /// Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount,Witness witness)Witness(uint256 witnessArgument)
208
+ function getTypestringHashForWitness (string calldata witness_ ) external pure returns (bytes32 typestringHash_ ) {
209
+ assembly {
210
+ let memoryOffset := mload (0x40 )
211
+ mstore (memoryOffset, COMPACT_TYPESTRING_FRAGMENT_ONE)
212
+ mstore (add (memoryOffset, 0x20 ), COMPACT_TYPESTRING_FRAGMENT_TWO)
213
+ mstore (add (memoryOffset, 0x40 ), COMPACT_TYPESTRING_FRAGMENT_THREE)
214
+ mstore (add (memoryOffset, sub (0x60 , 0x01 )), shl (56 , WITNESS_TYPESTRING))
215
+ let witnessPointer := add (memoryOffset, add (sub (0x60 , 0x01 ), 0x19 ))
216
+ calldatacopy (witnessPointer, witness_.offset, witness_.length )
217
+ let witnessEnd := add (witnessPointer, witness_.length )
218
+ mstore8 (witnessEnd, 0x29 )
219
+ typestringHash_ := keccak256 (memoryOffset, sub (add (witnessEnd, 0x01 ), memoryOffset))
220
+
221
+ mstore (0x40 , add (or (witnessEnd, 0x1f ), 0x20 ))
222
+ }
223
+ return typestringHash_;
224
+ }
225
+
180
226
function _getTokenHash (uint256 id_ , address sponsor_ ) internal pure returns (bytes32 ) {
181
227
return keccak256 (abi.encode (id_, sponsor_));
182
228
}
183
229
230
+ function _checkAllocation (Compact calldata compact_ ) internal view returns (bytes32 ) {
231
+ // Check msg.sender is sponsor
232
+ if (msg .sender != compact_.sponsor) {
233
+ revert InvalidCaller (msg .sender , compact_.sponsor);
234
+ }
235
+ bytes32 tokenHash = _getTokenHash (compact_.id, msg .sender );
236
+ // Check no lock is already active for this sponsor
237
+ if (_claim[tokenHash] > block .timestamp && ! ITheCompact (COMPACT_CONTRACT).hasConsumedAllocatorNonce (_nonce[tokenHash], address (this ))) {
238
+ revert ClaimActive (compact_.sponsor);
239
+ }
240
+ // Check arbiter is valid
241
+ if (compact_.arbiter != ARBITER) {
242
+ revert InvalidArbiter (compact_.arbiter);
243
+ }
244
+ // Check expiration is not too soon or too late
245
+ if (compact_.expires < block .timestamp + MIN_WITHDRAWAL_DELAY || compact_.expires > block .timestamp + MAX_WITHDRAWAL_DELAY) {
246
+ revert InvalidExpiration (compact_.expires);
247
+ }
248
+ // Check expiration is not longer then the tokens forced withdrawal time
249
+ (,, ResetPeriod resetPeriod ,) = ITheCompact (COMPACT_CONTRACT).getLockDetails (compact_.id);
250
+ if (compact_.expires > block .timestamp + _resetPeriodToSeconds (resetPeriod)) {
251
+ revert ForceWithdrawalAvailable (compact_.expires, block .timestamp + _resetPeriodToSeconds (resetPeriod));
252
+ }
253
+ // Check expiration is not past an active force withdrawal
254
+ (, uint256 forcedWithdrawalExpiration ) = ITheCompact (COMPACT_CONTRACT).getForcedWithdrawalStatus (compact_.sponsor, compact_.id);
255
+ if (forcedWithdrawalExpiration != 0 && forcedWithdrawalExpiration < compact_.expires) {
256
+ revert ForceWithdrawalAvailable (compact_.expires, forcedWithdrawalExpiration);
257
+ }
258
+ // Check nonce is not yet consumed
259
+ if (ITheCompact (COMPACT_CONTRACT).hasConsumedAllocatorNonce (compact_.nonce, address (this ))) {
260
+ revert NonceAlreadyConsumed (compact_.nonce);
261
+ }
262
+
263
+ uint256 balance = ERC6909 (COMPACT_CONTRACT).balanceOf (msg .sender , compact_.id);
264
+ // Check balance is enough
265
+ if (balance < compact_.amount) {
266
+ revert InsufficientBalance (msg .sender , compact_.id, balance, compact_.amount);
267
+ }
268
+
269
+ return tokenHash;
270
+ }
271
+
184
272
/// @dev copied from IdLib.sol
185
273
function _resetPeriodToSeconds (ResetPeriod resetPeriod_ ) internal pure returns (uint256 duration ) {
186
274
assembly ("memory-safe" ) {
0 commit comments