Skip to content

Commit 273e632

Browse files
authored
Merge pull request #7 from Uniswap/mandate-reverse-dutch-auction
Updates to support reversed dutch auction
2 parents 4bed9de + 10162ae commit 273e632

File tree

4 files changed

+242
-91
lines changed

4 files changed

+242
-91
lines changed

src/allocators/ERC7683Allocator.sol

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,34 @@ pragma solidity ^0.8.27;
55
import {IERC7683Allocator} from '../interfaces/IERC7683Allocator.sol';
66
import {SimpleAllocator} from './SimpleAllocator.sol';
77
import {Claim, Mandate} from './types/TribunalStructs.sol';
8+
9+
import {IERC1271} from '@openzeppelin/contracts/interfaces/IERC1271.sol';
810
import {ECDSA} from '@openzeppelin/contracts/utils/cryptography/ECDSA.sol';
911
import {ITheCompact} from '@uniswap/the-compact/interfaces/ITheCompact.sol';
1012
import {Compact} from '@uniswap/the-compact/types/EIP712Types.sol';
1113

1214
contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
1315
/// @notice The typehash of the OrderData struct
14-
// keccak256("OrderData(address arbiter,address sponsor,uint256 nonce,uint256 id,uint256 amount,Mandate mandate)
15-
// Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,bytes32 salt)")
16-
bytes32 public constant ORDERDATA_TYPEHASH = 0x9e0e1bdb0df35509b65bbc49d209dd42496c5a3f13998f9a74dc842d6932656b;
16+
// keccak256("OrderData(address arbiter,address sponsor,uint256 nonce,uint256 id,uint256 amount,
17+
// uint256 chainId,address tribunal,address recipient,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt,uint256 targetBlock,uint256 maximumBlocksAfterTarget)")
18+
bytes32 public constant ORDERDATA_TYPEHASH = 0x9687614112a074c792f7035dc9365f34672a3aa8d3c312500bd47ddcaa0383b5;
1719

1820
/// @notice The typehash of the OrderDataGasless struct
19-
// keccak256("OrderDataGasless(address arbiter,uint256 id,uint256 amount,Mandate mandate)
20-
// Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,bytes32 salt)")
21+
// keccak256("OrderDataGasless(address arbiter,uint256 id,uint256 amount,
22+
// uint256 chainId,address tribunal,address recipient,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt)")
2123
bytes32 public constant ORDERDATA_GASLESS_TYPEHASH =
22-
0x9ab67658b7c0f35b64fdadd7adee1e58b6399a8201f38c355d3a109a2d7081d7;
24+
0xe9b624fa654c7f07ce16d31bf0165a4030d4022f62987afad8ef9d30fc8a0b88;
25+
26+
/// @notice keccak256("QualifiedClaim(bytes32 claimHash,uint256 targetBlock,uint256 maximumBlocksAfterTarget)")
27+
bytes32 public constant QUALIFICATION_TYPEHASH = 0x59866b84bd1f6c909cf2a31efd20c59e6c902e50f2c196994e5aa85cdc7d7ce0;
2328

2429
/// @notice keccak256("Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount,Mandate mandate)
25-
// Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,bytes32 salt)")
30+
// Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt)")
2631
bytes32 public constant COMPACT_WITNESS_TYPEHASH =
27-
0x27f09e0bb8ce2ae63380578af7af85055d3ada248c502e2378b85bc3d05ee0b0;
32+
0xfd9cda0e5e31a3a3476cb5b57b07e2a4d6a12815506f69c880696448cd9897a5;
2833

29-
/// @notice keccak256("Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,bytes32 salt)")
30-
bytes32 internal constant MANDATE_TYPEHASH = 0x52c75464356e20084ae43acac75087fbf0e0c678e7ffa326f369f37e88696036;
34+
/// @notice keccak256("Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt)")
35+
bytes32 internal constant MANDATE_TYPEHASH = 0x74d9c10530859952346f3e046aa2981a24bb7524b8394eb45a9deddced9d6501;
3136

3237
/// @notice uint256(uint8(keccak256("ERC7683Allocator.nonce")))
3338
uint8 internal constant NONCE_MASTER_SLOT_SEED = 0x39;
@@ -101,7 +106,7 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
101106
/// @inheritdoc IERC7683Allocator
102107
function getCompactWitnessTypeString() external pure returns (string memory) {
103108
return
104-
'Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount,Mandate mandate)Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,bytes32 salt))';
109+
'Compact(address arbiter,address sponsor,uint256 nonce,uint256 expires,uint256 id,uint256 amount,Mandate mandate)Mandate(uint256 chainId,address tribunal,address recipient,uint256 expires,address token,uint256 minimumAmount,uint256 baselinePriorityFee,uint256 scalingFactor,uint256[] decayCurve,bytes32 salt))';
105110
}
106111

107112
/// @inheritdoc IERC7683Allocator
@@ -116,6 +121,12 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
116121
return nonceFree_;
117122
}
118123

124+
/// @inheritdoc IERC7683Allocator
125+
function createFillerData(address claimant_) external pure returns (bytes memory fillerData) {
126+
fillerData = abi.encode(claimant_);
127+
return fillerData;
128+
}
129+
119130
function _open(OrderData memory orderData_, uint32 fillDeadline_, address sponsor_, bytes memory sponsorSignature_)
120131
internal
121132
{
@@ -151,15 +162,16 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
151162
orderData_.minimumAmount,
152163
orderData_.baselinePriorityFee,
153164
orderData_.scalingFactor,
165+
keccak256(abi.encodePacked(orderData_.decayCurve)),
154166
orderData_.salt
155167
)
156168
)
157169
)
158170
);
159-
bytes32 digest = keccak256(abi.encodePacked(bytes2(0x1901), _COMPACT_DOMAIN_SEPARATOR, claimHash));
160171

161172
// We check for the length, which means this could also be triggered by a zero length signature provided in the openFor function. This enables relaying of orders if the claim was registered on the compact.
162173
if (sponsorSignature_.length > 0) {
174+
bytes32 digest = keccak256(abi.encodePacked(bytes2(0x1901), _COMPACT_DOMAIN_SEPARATOR, claimHash));
163175
// confirm the signature matches the digest
164176
address signer = ECDSA.recover(digest, sponsorSignature_);
165177
if (sponsor_ != signer) {
@@ -174,7 +186,13 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
174186
}
175187
}
176188

177-
_sponsor[digest] = tokenHash;
189+
bytes32 qualifiedClaimHash = keccak256(
190+
abi.encode(QUALIFICATION_TYPEHASH, claimHash, orderData_.targetBlock, orderData_.maximumBlocksAfterTarget)
191+
);
192+
bytes32 qualifiedDigest =
193+
keccak256(abi.encodePacked(bytes2(0x1901), _COMPACT_DOMAIN_SEPARATOR, qualifiedClaimHash));
194+
195+
_sponsor[qualifiedDigest] = tokenHash;
178196

179197
// Emit an open event
180198
emit Open(
@@ -200,7 +218,6 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
200218
_claim[tokenHash_] = expires;
201219
_amount[tokenHash_] = amount;
202220
_nonce[tokenHash_] = nonce;
203-
204221
return tokenHash_;
205222
}
206223

@@ -220,6 +237,7 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
220237
minimumAmount: orderData.minimumAmount,
221238
baselinePriorityFee: orderData.baselinePriorityFee,
222239
scalingFactor: orderData.scalingFactor,
240+
decayCurve: orderData.decayCurve,
223241
salt: orderData.salt
224242
});
225243
Claim memory claim = Claim({
@@ -239,7 +257,7 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
239257
fillInstructions[0] = FillInstruction({
240258
destinationChainId: orderData.chainId,
241259
destinationSettler: _addressToBytes32(orderData.tribunal),
242-
originData: abi.encode(claim, mandate)
260+
originData: abi.encode(claim, mandate, orderData.targetBlock, orderData.maximumBlocksAfterTarget)
243261
});
244262

245263
Output memory spent = Output({
@@ -337,7 +355,10 @@ contract ERC7683Allocator is SimpleAllocator, IERC7683Allocator {
337355
minimumAmount: orderDataGasless_.minimumAmount,
338356
baselinePriorityFee: orderDataGasless_.baselinePriorityFee,
339357
scalingFactor: orderDataGasless_.scalingFactor,
340-
salt: orderDataGasless_.salt
358+
decayCurve: orderDataGasless_.decayCurve,
359+
salt: orderDataGasless_.salt,
360+
targetBlock: 0,
361+
maximumBlocksAfterTarget: 0
341362
});
342363
return orderData_;
343364
}

src/allocators/types/TribunalStructs.sol

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ struct Claim {
1212
}
1313

1414
struct Mandate {
15-
// uint256 chainId; // (implicit arg, included in EIP712 payload)
16-
// address tribunal; // (implicit arg, included in EIP712 payload)
17-
address recipient; // Recipient of settled tokens
18-
uint256 expires; // Mandate expiration timestamp
19-
address token; // Settlement token (address(0) for native)
20-
uint256 minimumAmount; // Minimum settlement amount
21-
uint256 baselinePriorityFee; // Base fee threshold where scaling kicks in
22-
uint256 scalingFactor; // Fee scaling multiplier (1e18 baseline)
23-
bytes32 salt; // Replay protection parameter
15+
// uint256 chainId; // (implicit arg, included in EIP712 payload).
16+
// address tribunal; // (implicit arg, included in EIP712 payload).
17+
address recipient; // Recipient of filled tokens.
18+
uint256 expires; // Mandate expiration timestamp.
19+
address token; // Fill token (address(0) for native).
20+
uint256 minimumAmount; // Minimum fill amount.
21+
uint256 baselinePriorityFee; // Base fee threshold where scaling kicks in.
22+
uint256 scalingFactor; // Fee scaling multiplier (1e18 baseline).
23+
uint256[] decayCurve; // Block durations, fill increases, & claim decreases.
24+
bytes32 salt; // Replay protection parameter.
2425
}

src/interfaces/IERC7683Allocator.sol

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ interface IERC7683Allocator is IOriginSettler {
2222
uint256 minimumAmount; // Minimum settlement amount
2323
uint256 baselinePriorityFee; // Base fee threshold where scaling kicks in
2424
uint256 scalingFactor; // Fee scaling multiplier (1e18 baseline)
25+
uint256[] decayCurve; // Block durations, fill increases, & claim decreases.
2526
bytes32 salt; // Replay protection parameter
27+
// ADDITIONAL INPUT
28+
uint256 targetBlock; // The block number at the target chain on which the PGA is executed / the reverse dutch auction starts.
29+
uint256 maximumBlocksAfterTarget; // Blocks after target block that are still fillable.
2630
}
2731

2832
struct OrderDataGasless {
@@ -42,6 +46,7 @@ interface IERC7683Allocator is IOriginSettler {
4246
uint256 minimumAmount; // Minimum settlement amount
4347
uint256 baselinePriorityFee; // Base fee threshold where scaling kicks in
4448
uint256 scalingFactor; // Fee scaling multiplier (1e18 baseline)
49+
uint256[] decayCurve; // Block durations, fill increases, & claim decreases.
4550
bytes32 salt; // Replay protection parameter
4651
}
4752

@@ -76,4 +81,8 @@ interface IERC7683Allocator is IOriginSettler {
7681
/// @notice Checks if a nonce is free to be used
7782
/// @dev The nonce is the most significant 96 bits. The least significant 160 bits must be the sponsor address
7883
function checkNonce(address sponsor_, uint256 nonce_) external view returns (bool nonceFree_);
84+
85+
/// @notice Creates the filler data for the open event to be used on the IDestinationSettler
86+
/// @param claimant_ The address claiming the origin tokens after a successful fill (typically the address of the filler)
87+
function createFillerData(address claimant_) external pure returns (bytes memory fillerData);
7988
}

0 commit comments

Comments
 (0)