Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions contracts/AllPayAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ contract AllPayAuction is Auction {
uint256 deadlineExtension,
uint256 protocolFee
);
event AuctionCancelled(uint256 indexed auctionId, address indexed auctioneer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This event should be placed in abstract contract since it's same in all contracts.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


function createAuction(
string memory name,
Expand Down Expand Up @@ -115,6 +116,16 @@ contract AllPayAuction is Auction {
emit Withdrawn(auctionId, withdrawAmount);
}

function cancelAuction(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) notClaimed(auctions[auctionId].isClaimed) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here also notClaimed is unnecessary

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also i just realized checking beforeDeadline is also redundant here as and our only condition is no one has made a bid yet.

AuctionData storage auction = auctions[auctionId];
require(msg.sender == auction.auctioneer, "Only auctioneer can cancel");
require(auction.highestBid == 0, "Cannot cancel auction with bids");
auction.isClaimed = true;
auction.deadline = block.timestamp; // Set deadline to now, preventing future bids via beforeDeadline modifier
sendFunds(auction.auctionType == AuctionType.NFT, auction.auctionedToken, auction.auctioneer, auction.auctionedTokenIdOrAmount);
emit AuctionCancelled(auctionId, auction.auctioneer);
}

function claim(uint256 auctionId) external exists(auctionId) onlyAfterDeadline(auctions[auctionId].deadline) notClaimed(auctions[auctionId].isClaimed) {
AuctionData storage auction = auctions[auctionId];
auction.isClaimed = true;
Expand Down
11 changes: 11 additions & 0 deletions contracts/EnglishAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ contract EnglishAuction is Auction {
uint256 deadlineExtension,
uint256 protocolFee
);
event AuctionCancelled(uint256 indexed auctionId, address indexed auctioneer);

function createAuction(
string memory name,
Expand Down Expand Up @@ -118,6 +119,16 @@ contract EnglishAuction is Auction {
emit Withdrawn(auctionId, withdrawAmount);
}

function cancelAuction(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) notClaimed(auctions[auctionId].isClaimed) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here also not necessary of using notClaimed

AuctionData storage auction = auctions[auctionId];
require(msg.sender == auction.auctioneer, "Only auctioneer can cancel");
require(auction.highestBid == 0, "Cannot cancel auction with bids");
auction.isClaimed = true;
auction.deadline = block.timestamp; // Set deadline to now, preventing future bids via beforeDeadline modifier
sendFunds(auction.auctionType == AuctionType.NFT, auction.auctionedToken, auction.auctioneer, auction.auctionedTokenIdOrAmount);
emit AuctionCancelled(auctionId, auction.auctioneer);
}

function claim(uint256 auctionId) external exists(auctionId) onlyAfterDeadline(auctions[auctionId].deadline) notClaimed(auctions[auctionId].isClaimed) {
AuctionData storage auction = auctions[auctionId];
auction.isClaimed = true;
Expand Down
14 changes: 13 additions & 1 deletion contracts/ExponentialReverseDutchAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ contract ExponentialReverseDutchAuction is Auction {
uint256 deadline,
uint256 protocolFee
);
event AuctionCancelled(uint256 indexed auctionId, address indexed auctioneer);

function createAuction(
string memory name,
Expand Down Expand Up @@ -130,8 +131,19 @@ contract ExponentialReverseDutchAuction is Auction {
sendERC20(auction.biddingToken,feeRecipient,fees);
emit Withdrawn(auctionId, withdrawAmount);
}

function cancelAuction(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) {
AuctionData storage auction = auctions[auctionId];
require(msg.sender == auction.auctioneer, "Only auctioneer can cancel");
require(auction.winner == auction.auctioneer, "Cannot cancel auction with bids");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In dutch auction since if a single bid takes place the item will be claimed instantly.
So just checking auction.isClaimed would be perfect and easy to understand.

require(!auction.isClaimed, "Auctioned asset has already been claimed");
auction.isClaimed = true;
auction.deadline = block.timestamp; // Set deadline to now, preventing future bids via beforeDeadline modifier
sendFunds(auction.auctionType == AuctionType.NFT, auction.auctionedToken, auction.auctioneer, auction.auctionedTokenIdOrAmount);
emit AuctionCancelled(auctionId, auction.auctioneer);
}

function bid(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) notClaimed(auctions[auctionId].isClaimed) {
function bid(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) {
AuctionData storage auction = auctions[auctionId];
auction.winner = msg.sender;
uint256 currentPrice = getCurrentPrice(auctionId);
Expand Down
14 changes: 13 additions & 1 deletion contracts/LinearReverseDutchAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ contract LinearReverseDutchAuction is Auction {
uint256 deadline,
uint256 protocolFee
);
event AuctionCancelled(uint256 indexed auctionId, address indexed auctioneer);

function createAuction(
string memory name,
Expand Down Expand Up @@ -108,7 +109,18 @@ contract LinearReverseDutchAuction is Auction {
emit Withdrawn(auctionId, withdrawAmount);
}

function bid(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) notClaimed(auctions[auctionId].isClaimed) {
function cancelAuction(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) {
AuctionData storage auction = auctions[auctionId];
require(msg.sender == auction.auctioneer, "Only auctioneer can cancel");
require(auction.winner == auction.auctioneer, "Cannot cancel auction with bids");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here as well

require(!auction.isClaimed, "Auctioned asset has already been claimed");
auction.isClaimed = true;
auction.deadline = block.timestamp; // Set deadline to now, preventing future bids via beforeDeadline modifier
sendFunds(auction.auctionType == AuctionType.NFT, auction.auctionedToken, auction.auctioneer, auction.auctionedTokenIdOrAmount);
emit AuctionCancelled(auctionId, auction.auctioneer);
}

function bid(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) {
AuctionData storage auction = auctions[auctionId];
auction.winner = msg.sender;
uint256 currentPrice = getCurrentPrice(auctionId);
Expand Down
14 changes: 13 additions & 1 deletion contracts/LogarithmicReverseDutchAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ contract LogarithmicReverseDutchAuction is Auction {
uint256 deadline,
uint256 protocolFee
);
event AuctionCancelled(uint256 indexed auctionId, address indexed auctioneer);

function createAuction(
string memory name,
Expand Down Expand Up @@ -167,8 +168,19 @@ contract LogarithmicReverseDutchAuction is Auction {
sendERC20(auction.biddingToken,feeRecipient,fees);
emit Withdrawn(auctionId, withdrawAmount);
}

function cancelAuction(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) {
AuctionData storage auction = auctions[auctionId];
require(msg.sender == auction.auctioneer, "Only auctioneer can cancel");
require(auction.winner == auction.auctioneer, "Cannot cancel auction with bids");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here also

require(!auction.isClaimed, "Auctioned asset has already been claimed");
auction.isClaimed = true;
auction.deadline = block.timestamp; // Set deadline to now, preventing future bids via beforeDeadline modifier
sendFunds(auction.auctionType == AuctionType.NFT, auction.auctionedToken, auction.auctioneer, auction.auctionedTokenIdOrAmount);
emit AuctionCancelled(auctionId, auction.auctioneer);
}

function bid(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) notClaimed(auctions[auctionId].isClaimed) {
function bid(uint256 auctionId) external exists(auctionId) beforeDeadline(auctions[auctionId].deadline) {
AuctionData storage auction = auctions[auctionId];
auction.winner = msg.sender;
uint256 currentPrice = getCurrentPrice(auctionId);
Expand Down
12 changes: 12 additions & 0 deletions contracts/VickreyAuction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ contract VickreyAuction is Auction {
uint256 protocolFee
);
event BidRevealed(uint256 indexed auctionId, address indexed bidder, uint256 bidAmount);
event AuctionCancelled(uint256 indexed auctionId, address indexed auctioneer);

function createAuction(
string memory name,
Expand Down Expand Up @@ -143,6 +144,17 @@ contract VickreyAuction is Auction {
emit BidRevealed(auctionId, msg.sender, bidAmount);
}

function cancelAuction(uint256 auctionId) external exists(auctionId) notClaimed(auctions[auctionId].isClaimed) {
AuctionData storage auction = auctions[auctionId];
require(msg.sender == auction.auctioneer, "Only auctioneer can cancel");
require(block.timestamp < auction.bidCommitEnd, "Cannot cancel: commit phase started or ended");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here instead of checking time you should check that there are no commits in auction,otherwise someone has initiated the participation and in that case auction shall not be cancelled.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not required then,after checking commitFee is zero.So this line could be removed.

require(auction.accumulatedCommitFee == 0, "Cannot cancel: commitments exist");
auction.isClaimed = true;
auction.bidCommitEnd = block.timestamp; // Set commit end to now, preventing future commits via beforeDeadline modifier
sendFunds(auction.auctionType == AuctionType.NFT, auction.auctionedToken, auction.auctioneer, auction.auctionedTokenIdOrAmount);
emit AuctionCancelled(auctionId, auction.auctioneer);
}

function withdraw(uint256 auctionId) external exists(auctionId) onlyAfterDeadline(auctions[auctionId].bidRevealEnd) {
AuctionData storage auction = auctions[auctionId];
uint256 withdrawAmount = auction.availableFunds;
Expand Down
176 changes: 176 additions & 0 deletions test/AllPayAuction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,4 +298,180 @@ describe('AllPayAuction', function () {
expect(balanceAfter).to.be.gt(balanceBefore);
});
});

describe('Auction Cancellation', function () {
it('should allow auctioneer to cancel auction before any bids', async function () {
await mockNFT.connect(auctioneer).approve(await allPayAuction.getAddress(), 1);
await allPayAuction
.connect(auctioneer)
.createAuction(
'Test Auction',
'Test Description',
'https://example.com/test.jpg',
0,
await mockNFT.getAddress(),
1,
await biddingToken.getAddress(),
ethers.parseEther('1'),
ethers.parseEther('0.1'),
5,
10,
);

expect(await mockNFT.ownerOf(1)).to.equal(await allPayAuction.getAddress());

await expect(allPayAuction.connect(auctioneer).cancelAuction(0))
.to.emit(allPayAuction, 'AuctionCancelled')
.withArgs(0, await auctioneer.getAddress());

expect(await mockNFT.ownerOf(1)).to.equal(await auctioneer.getAddress());
const auction = await allPayAuction.auctions(0);
expect(auction.isClaimed).to.be.true;
});

it('should allow auctioneer to cancel token auction before any bids', async function () {
const amount = ethers.parseEther('10');
await mockToken.connect(auctioneer).approve(await allPayAuction.getAddress(), amount);

await allPayAuction
.connect(auctioneer)
.createAuction(
'Token Auction',
'Test Description',
'https://example.com/test.jpg',
1,
await mockToken.getAddress(),
amount,
await biddingToken.getAddress(),
ethers.parseEther('1'),
ethers.parseEther('0.1'),
5,
10,
);

const balanceBefore = await mockToken.balanceOf(await auctioneer.getAddress());
await allPayAuction.connect(auctioneer).cancelAuction(0);
const balanceAfter = await mockToken.balanceOf(await auctioneer.getAddress());
expect(balanceAfter).to.equal(balanceBefore + amount);
});

it('should not allow non-auctioneer to cancel auction', async function () {
await mockNFT.connect(auctioneer).approve(await allPayAuction.getAddress(), 1);
await allPayAuction
.connect(auctioneer)
.createAuction(
'Test Auction',
'Test Description',
'https://example.com/test.jpg',
0,
await mockNFT.getAddress(),
1,
await biddingToken.getAddress(),
ethers.parseEther('1'),
ethers.parseEther('0.1'),
5,
10,
);

await expect(allPayAuction.connect(bidder1).cancelAuction(0)).to.be.revertedWith('Only auctioneer can cancel');
});

it('should not allow cancellation after bid is placed', async function () {
await mockNFT.connect(auctioneer).approve(await allPayAuction.getAddress(), 1);
await allPayAuction
.connect(auctioneer)
.createAuction(
'Test Auction',
'Test Description',
'https://example.com/test.jpg',
0,
await mockNFT.getAddress(),
1,
await biddingToken.getAddress(),
ethers.parseEther('1'),
ethers.parseEther('0.1'),
5,
10,
);

const bidAmount = ethers.parseEther('1.5');
await biddingToken.connect(bidder1).approve(await allPayAuction.getAddress(), bidAmount);
await allPayAuction.connect(bidder1).bid(0, bidAmount);

await expect(allPayAuction.connect(auctioneer).cancelAuction(0)).to.be.revertedWith('Cannot cancel auction with bids');
});

it('should not allow cancellation after deadline', async function () {
await mockNFT.connect(auctioneer).approve(await allPayAuction.getAddress(), 1);
await allPayAuction
.connect(auctioneer)
.createAuction(
'Test Auction',
'Test Description',
'https://example.com/test.jpg',
0,
await mockNFT.getAddress(),
1,
await biddingToken.getAddress(),
ethers.parseEther('1'),
ethers.parseEther('0.1'),
5,
10,
);

await ethers.provider.send('evm_increaseTime', [10]);
await ethers.provider.send('evm_mine', []);

await expect(allPayAuction.connect(auctioneer).cancelAuction(0)).to.be.revertedWith('Deadline of auction reached');
});

it('should not allow cancellation of already cancelled auction', async function () {
await mockNFT.connect(auctioneer).approve(await allPayAuction.getAddress(), 1);
await allPayAuction
.connect(auctioneer)
.createAuction(
'Test Auction',
'Test Description',
'https://example.com/test.jpg',
0,
await mockNFT.getAddress(),
1,
await biddingToken.getAddress(),
ethers.parseEther('1'),
ethers.parseEther('0.1'),
5,
10,
);

await allPayAuction.connect(auctioneer).cancelAuction(0);
await expect(allPayAuction.connect(auctioneer).cancelAuction(0)).to.be.revertedWith(
'Deadline of auction reached',
);
});

it('should not allow bidding on cancelled auction', async function () {
await mockNFT.connect(auctioneer).approve(await allPayAuction.getAddress(), 1);
await allPayAuction
.connect(auctioneer)
.createAuction(
'Test Auction',
'Test Description',
'https://example.com/test.jpg',
0,
await mockNFT.getAddress(),
1,
await biddingToken.getAddress(),
ethers.parseEther('1'),
ethers.parseEther('0.1'),
5,
10,
);

await allPayAuction.connect(auctioneer).cancelAuction(0);

const bidAmount = ethers.parseEther('1.5');
await biddingToken.connect(bidder1).approve(await allPayAuction.getAddress(), bidAmount);
await expect(allPayAuction.connect(bidder1).bid(0, bidAmount)).to.be.revertedWith('Deadline of auction reached');
});
});
});
Loading