Skip to content

Commit 293bc91

Browse files
committed
improve claimSeller function on DutchAuction
1 parent 1b878a1 commit 293bc91

File tree

4 files changed

+36
-81
lines changed

4 files changed

+36
-81
lines changed

hardhat/contracts/dutchAuctionConfidential/DutchAuctionSellConfERC20.sol

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ contract DutchAuctionSellingConfidentialERC20 is
3636
uint256 public immutable startAt;
3737
/// @notice Timestamp when the auction ends
3838
uint256 public immutable expiresAt;
39-
/// @notice Timestamp when the auction refund claims end
40-
uint256 public immutable claimsExpiresAt;
4139
/// @notice Minimum price per token
4240
uint64 public immutable reservePrice;
4341
/// @notice Total amount of tokens being auctioned
@@ -106,7 +104,6 @@ contract DutchAuctionSellingConfidentialERC20 is
106104
discountRate = _discountRate;
107105
startAt = block.timestamp;
108106
expiresAt = block.timestamp + _biddingTime;
109-
claimsExpiresAt = block.timestamp + 3 * _biddingTime; // could choose a potentially different design
110107
reservePrice = _reservePrice;
111108
stoppable = _isStoppable;
112109

@@ -291,17 +288,22 @@ contract DutchAuctionSellingConfidentialERC20 is
291288

292289
/// @notice Claim proceeds for the seller after auction ends
293290
/// @dev Transfers all remaining tokens and payments to seller
294-
function claimSeller() external onlyOwner onlyAfterClaimsEnd {
295-
// Get the total amount of payment tokens in the contract
296-
euint64 contractPaymentBalance = paymentToken.balanceOf(address(this));
297-
euint64 contractAuctionBalance = token.balanceOf(address(this));
291+
function claimSeller() external onlyOwner onlyAfterAuctionEnds {
292+
// Transfer remaining auction tokens back to seller
293+
TFHE.allowTransient(tokensLeft, address(token));
294+
token.transfer(seller, tokensLeft);
295+
296+
// Get current price
297+
uint64 endPricePerToken = getPrice();
298298

299-
// Transfer all payment token and auction tokens to the seller
300-
TFHE.allowTransient(contractPaymentBalance, address(paymentToken));
301-
paymentToken.transfer(seller, contractPaymentBalance);
299+
// Calculate and transfer payment tokens to seller
300+
euint64 contractAuctionBalance = TFHE.mul(endPricePerToken, TFHE.sub(TFHE.asEuint64(amount), tokensLeft));
301+
TFHE.allowTransient(contractAuctionBalance, address(paymentToken));
302+
paymentToken.transfer(seller, contractAuctionBalance);
302303

303-
TFHE.allowTransient(contractAuctionBalance, address(token));
304-
token.transfer(seller, contractAuctionBalance);
304+
tokensLeft = TFHE.asEuint64(0);
305+
TFHE.allowThis(tokensLeft);
306+
TFHE.allow(tokensLeft, owner());
305307
}
306308

307309
/// @notice Request decryption of remaining tokens
@@ -343,18 +345,8 @@ contract DutchAuctionSellingConfidentialERC20 is
343345
modifier onlyAfterAuctionEnds() {
344346
if (!auctionStart) revert AuctionNotStarted();
345347
if (block.timestamp < expiresAt && manuallyStopped == false) revert TooEarly(expiresAt);
346-
if (block.timestamp >= claimsExpiresAt) revert TooLate(claimsExpiresAt);
347348
_;
348349
}
349-
350-
/// @notice Modifier to ensure function is called after refund claim period ends
351-
/// @dev Reverts if called before the auction refund claims period end time and not manually stopped
352-
modifier onlyAfterClaimsEnd() {
353-
if (!auctionStart) revert AuctionNotStarted();
354-
if (block.timestamp < claimsExpiresAt && manuallyStopped == false) revert TooEarly(claimsExpiresAt);
355-
_;
356-
}
357-
358350
/// @notice Get the user's current bid information
359351
/// @dev Returns the decrypted values of token amount and paid amount
360352
/// @return tokenAmount Amount of tokens bid for

hardhat/contracts/dutchAuctionConfidential/DutchAuctionSellConfERC20NoRefund.sol

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ contract DutchAuctionSellingConfidentialERC20NoRefund is
3636
uint256 public immutable startAt;
3737
/// @notice Timestamp when the auction ends
3838
uint256 public immutable expiresAt;
39-
/// @notice Timestamp when the auction refund claims end
40-
uint256 public immutable claimsExpiresAt;
4139
/// @notice Minimum price per token
4240
uint64 public immutable reservePrice;
4341
/// @notice Total amount of tokens being auctioned
@@ -106,7 +104,6 @@ contract DutchAuctionSellingConfidentialERC20NoRefund is
106104
discountRate = _discountRate;
107105
startAt = block.timestamp;
108106
expiresAt = block.timestamp + _biddingTime;
109-
claimsExpiresAt = block.timestamp + 3 * _biddingTime; // could choose a potentially different design
110107
reservePrice = _reservePrice;
111108
stoppable = _isStoppable;
112109

@@ -265,17 +262,22 @@ contract DutchAuctionSellingConfidentialERC20NoRefund is
265262

266263
/// @notice Claim proceeds for the seller after auction ends
267264
/// @dev Transfers all remaining tokens and payments to seller
268-
function claimSeller() external onlyOwner onlyAfterClaimsEnd {
269-
// Get the total amount of payment tokens in the contract
270-
euint64 contractPaymentBalance = paymentToken.balanceOf(address(this));
271-
euint64 contractAuctionBalance = token.balanceOf(address(this));
265+
function claimSeller() external onlyOwner onlyAfterAuctionEnds {
266+
// Transfer remaining auction tokens back to seller
267+
TFHE.allowTransient(tokensLeft, address(token));
268+
token.transfer(seller, tokensLeft);
269+
270+
// Get current price
271+
uint64 endPricePerToken = getPrice();
272272

273-
// Transfer all payment token and auction tokens to the seller
274-
TFHE.allowTransient(contractPaymentBalance, address(paymentToken));
275-
paymentToken.transfer(seller, contractPaymentBalance);
273+
// Calculate and transfer payment tokens to seller
274+
euint64 contractAuctionBalance = TFHE.mul(endPricePerToken, TFHE.sub(TFHE.asEuint64(amount), tokensLeft));
275+
TFHE.allowTransient(contractAuctionBalance, address(paymentToken));
276+
paymentToken.transfer(seller, contractAuctionBalance);
276277

277-
TFHE.allowTransient(contractAuctionBalance, address(token));
278-
token.transfer(seller, contractAuctionBalance);
278+
tokensLeft = TFHE.asEuint64(0);
279+
TFHE.allowThis(tokensLeft);
280+
TFHE.allow(tokensLeft, owner());
279281
}
280282

281283
/// @notice Request decryption of remaining tokens
@@ -317,15 +319,6 @@ contract DutchAuctionSellingConfidentialERC20NoRefund is
317319
modifier onlyAfterAuctionEnds() {
318320
if (!auctionStart) revert AuctionNotStarted();
319321
if (block.timestamp < expiresAt && manuallyStopped == false) revert TooEarly(expiresAt);
320-
if (block.timestamp >= claimsExpiresAt) revert TooLate(claimsExpiresAt);
321-
_;
322-
}
323-
324-
/// @notice Modifier to ensure function is called after refund claim period ends
325-
/// @dev Reverts if called before the auction refund claims period end time and not manually stopped
326-
modifier onlyAfterClaimsEnd() {
327-
if (!auctionStart) revert AuctionNotStarted();
328-
if (block.timestamp < claimsExpiresAt && manuallyStopped == false) revert TooEarly(claimsExpiresAt);
329322
_;
330323
}
331324

hardhat/contracts/dutchAuctionConfidential/README.md

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -86,8 +86,8 @@ Both implementations follow a Dutch auction mechanism where:
8686
4. Tokens are transferred immediately upon successful bid
8787

8888
The key difference is in how prices and refunds are handled:
89-
- DutchAuctionSellingConfidentialERC20.sol adjusts all previous bids to the latest (lower) price
90-
- DutchAuctionSellingConfidentialERC20NoRefund.sol keeps track of each bid at its original price
89+
- `DutchAuctionSellingConfidentialERC20.sol` adjusts all previous bids to the latest (lower) price
90+
- `DutchAuctionSellingConfidentialERC20NoRefund.sol` keeps track of each bid at its original price
9191

9292
### Immediate Token Transfer on Bid
9393
Tokens are transferred to buyers immediately upon successfully bidding rather than requiring a separate claim step. This design choice:
@@ -99,31 +99,6 @@ Tokens are transferred to buyers immediately upon successfully bidding rather th
9999

100100
The tradeoff is higher FHE gas costs during bidding, but this is outweighed by the UX benefits and reduced support overhead.
101101

102-
### Two-phase Auction Conclusion
103-
The auction uses a two-phase conclusion process:
104-
105-
1. Bidding Phase (`startTime` to `expiresAt`):
106-
- Active bidding period
107-
- Price decreases according to discount rate
108-
- Immediate token transfers on successful bids
109-
110-
2. Claims Phase (`expiresAt` to `claimsExpiresAt`):
111-
- No new bids accepted
112-
- Users can claim refunds using `claimUserRefund()`
113-
- Duration is 3x the bidding period
114-
115-
3. Seller Claim Phase (`claimsExpiresAt` onward):
116-
- Users cannot claim refunds any longer
117-
- Seller claims remaining funds and tokens using `claimSeller()`
118-
119-
This design:
120-
- Ensures users have adequate time to claim refunds
121-
- Prevents the seller from withdrawing funds before users can claim
122-
- Provides clear timeframes for all participants
123-
- Maintains fairness for all participants
124-
125-
The extended claims period (3x bidding time) could be adjusted based on specific needs, but provides a reasonable window for users to act while not indefinitely locking seller funds.
126-
127102
## Mathematics of Refunds
128103

129104
**Basic formula for price calculation:**

hardhat/test/confidentialDutchAuction/confDutchAuctionNoRefund.ts

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -446,8 +446,6 @@ describe("DutchAuctionSellingConfidentialERC20NoRefund", function () {
446446

447447
// Claim potential refunds
448448
await this.auction.connect(this.signers.bob).claimUserRefund();
449-
// Wait for refunds to process
450-
await time.increase(15 * 24 * 60 * 60 + 1);
451449
await this.auction.connect(this.signers.alice).claimSeller();
452450

453451
// Get final balances
@@ -496,6 +494,12 @@ describe("DutchAuctionSellingConfidentialERC20NoRefund", function () {
496494
this.paymentTokenAddress,
497495
);
498496

497+
// Verify that the auction state hasn't changed
498+
await this.auction.connect(this.signers.alice).requestTokensLeftReveal();
499+
await awaitAllDecryptionResults();
500+
const endAuctionTokensLeft = await this.auction.tokensLeftReveal();
501+
expect(endAuctionTokensLeft).to.equal(0);
502+
499503
// Verify auction token balances
500504
expect(decryptedInitialAuctionTokenAlice).to.equal(0n); // Alice transferred all tokens to auction contract
501505
expect(decryptedFinalAuctionTokenAlice).to.equal(TOKEN_AMOUNT - bidAmount);
@@ -617,9 +621,6 @@ describe("DutchAuctionSellingConfidentialERC20NoRefund", function () {
617621
// Claim refunds (this will now handle the refund for both bids at once)
618622
await this.auction.connect(this.signers.bob).claimUserRefund();
619623

620-
// Wait for claims period to end
621-
await time.increase(15 * 24 * 60 * 60 + 1);
622-
623624
// Claim seller proceeds
624625
await this.auction.connect(this.signers.alice).claimSeller();
625626

@@ -726,9 +727,6 @@ describe("DutchAuctionSellingConfidentialERC20NoRefund", function () {
726727
// Verify final balance after refund
727728
expect(decryptedInitialBalance - decryptedFinalBalance).to.equal(expectedFinalCost);
728729

729-
// Move time to after claims period
730-
await time.increase(15 * 24 * 60 * 60 + 1);
731-
732730
// Seller claims proceeds
733731
await this.auction.connect(this.signers.alice).claimSeller();
734732

@@ -863,9 +861,6 @@ describe("DutchAuctionSellingConfidentialERC20NoRefund", function () {
863861
this.paymentTokenAddress,
864862
);
865863

866-
// Move to after claims period
867-
await time.increase(15 * 24 * 60 * 60);
868-
869864
// Seller claims
870865
await this.auction.connect(this.signers.alice).claimSeller();
871866

0 commit comments

Comments
 (0)