Skip to content

Commit 4d5c97a

Browse files
committed
feat: batch cancel, getAuction
1 parent 106f808 commit 4d5c97a

6 files changed

Lines changed: 594 additions & 47 deletions

File tree

src/Auction.sol

Lines changed: 53 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -135,37 +135,18 @@ contract Auction is IAuction, Ownable2Step, Pausable, EIP712 {
135135
}
136136

137137
/// @inheritdoc IAuction
138-
function cancel(bytes32 castHash, AuthData memory auth) external whenNotPaused {
139-
// Auction must be active or ended (not settled or cancelled)
140-
AuctionState state = auctionState(castHash);
141-
if (state != AuctionState.Active && state != AuctionState.Ended) revert AuctionNotCancellable();
142-
143-
// Verify authorization
144-
if (block.timestamp > auth.deadline) revert DeadlineExpired();
145-
if (usedNonces[auth.nonce]) revert NonceAlreadyUsed();
146-
147-
// Validate signature
148-
bytes32 digest = hashCancelAuthorization(castHash, auth.nonce, auth.deadline);
149-
address signer = ECDSA.recover(digest, auth.signature);
150-
if (!authorizers[signer]) revert Unauthorized();
151-
152-
// Mark nonce as used
153-
usedNonces[auth.nonce] = true;
154-
155-
// Load refund info
156-
AuctionData storage auctionData = auctions[castHash];
157-
address refundAddress = auctionData.highestBidder;
158-
uint96 refundBidderFid = auctionData.highestBidderFid;
159-
uint256 refundAmount = auctionData.highestBid;
160-
161-
// Mark as cancelled
162-
auctionData.state = AuctionState.Cancelled;
138+
function batchCancel(bytes32[] calldata castHashes, AuthData[] calldata authDatas) external whenNotPaused {
139+
uint256 length = castHashes.length;
140+
if (length != authDatas.length) revert InvalidAuctionParams();
163141

164-
// Refund the highest bidder
165-
usdc.transfer(refundAddress, refundAmount);
166-
emit BidRefunded(castHash, refundAddress, refundAmount);
142+
for (uint256 i = 0; i < length; ++i) {
143+
_cancel(castHashes[i], authDatas[i]);
144+
}
145+
}
167146

168-
emit AuctionCancelled(castHash, refundAddress, refundBidderFid, signer);
147+
/// @inheritdoc IAuction
148+
function cancel(bytes32 castHash, AuthData memory auth) external whenNotPaused {
149+
_cancel(castHash, auth);
169150
}
170151

171152
/// @inheritdoc IAuction
@@ -260,25 +241,20 @@ contract Auction is IAuction, Ownable2Step, Pausable, EIP712 {
260241
/// @inheritdoc IAuction
261242
function auctionState(bytes32 castHash) public view returns (AuctionState) {
262243
AuctionData storage auctionData = auctions[castHash];
244+
AuctionState state = auctionData.state;
263245

264-
if (auctionData.endTime == 0) {
265-
return AuctionState.None;
266-
}
267-
268-
// If auction is in terminal state, return that state
269-
if (
270-
auctionData.state == AuctionState.Settled || auctionData.state == AuctionState.Cancelled
271-
|| auctionData.state == AuctionState.Recovered
272-
) {
273-
return auctionData.state;
246+
if (block.timestamp > auctionData.endTime && state == AuctionState.Active) {
247+
return AuctionState.Ended;
274248
}
275249

276-
// Otherwise, check if auction is active or ended based on time
277-
if (block.timestamp < auctionData.endTime) {
278-
return AuctionState.Active;
279-
}
250+
return state;
251+
}
280252

281-
return AuctionState.Ended;
253+
/// @inheritdoc IAuction
254+
function getAuction(bytes32 castHash) external view returns (AuctionData memory) {
255+
AuctionData memory auction = auctions[castHash];
256+
auction.state = auctionState(castHash);
257+
return auction;
282258
}
283259

284260
/// @inheritdoc IAuction
@@ -484,6 +460,39 @@ contract Auction is IAuction, Ownable2Step, Pausable, EIP712 {
484460
emit AuctionSettled(castHash, auctionData.highestBidder, auctionData.highestBidderFid, auctionData.highestBid);
485461
}
486462

463+
function _cancel(bytes32 castHash, AuthData memory auth) internal {
464+
// Auction must be active or ended (not settled or cancelled)
465+
AuctionState state = auctionState(castHash);
466+
if (state != AuctionState.Active && state != AuctionState.Ended) revert AuctionNotCancellable();
467+
468+
// Verify authorization
469+
if (block.timestamp > auth.deadline) revert DeadlineExpired();
470+
if (usedNonces[auth.nonce]) revert NonceAlreadyUsed();
471+
472+
// Validate signature
473+
bytes32 digest = hashCancelAuthorization(castHash, auth.nonce, auth.deadline);
474+
address signer = ECDSA.recover(digest, auth.signature);
475+
if (!authorizers[signer]) revert Unauthorized();
476+
477+
// Mark nonce as used
478+
usedNonces[auth.nonce] = true;
479+
480+
// Load refund info
481+
AuctionData storage auctionData = auctions[castHash];
482+
address refundAddress = auctionData.highestBidder;
483+
uint96 refundBidderFid = auctionData.highestBidderFid;
484+
uint256 refundAmount = auctionData.highestBid;
485+
486+
// Mark as cancelled
487+
auctionData.state = AuctionState.Cancelled;
488+
489+
// Refund the highest bidder
490+
usdc.transfer(refundAddress, refundAmount);
491+
emit BidRefunded(castHash, refundAddress, refundAmount);
492+
493+
emit AuctionCancelled(castHash, refundAddress, refundBidderFid, signer);
494+
}
495+
487496
function _permitAndTransfer(uint256 amount, PermitData memory permit) internal {
488497
IERC20Permit(address(usdc)).permit(
489498
msg.sender, address(this), amount, permit.deadline, permit.v, permit.r, permit.s

src/interfaces/IAuction.sol

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,9 @@ interface IAuction {
144144
event AuctionStarted(
145145
bytes32 indexed castHash, address indexed creator, uint96 creatorFid, uint40 endTime, address authorizer
146146
); // New auction started with initial bid and signed parameters
147-
event BidPlaced(bytes32 indexed castHash, address indexed bidder, uint96 bidderFid, uint256 amount, address indexed authorizer); // Bid placed on auction
147+
event BidPlaced(
148+
bytes32 indexed castHash, address indexed bidder, uint96 bidderFid, uint256 amount, address indexed authorizer
149+
); // Bid placed on auction
148150
event AuctionExtended(bytes32 indexed castHash, uint256 newEndTime); // Auction end time extended due to late bid
149151
event AuctionSettled(bytes32 indexed castHash, address indexed winner, uint96 winnerFid, uint256 amount); // Auction settled, NFT minted to winner
150152
event AuctionCancelled(
@@ -218,6 +220,14 @@ interface IAuction {
218220
*/
219221
function cancel(bytes32 castHash, AuthData memory auth) external;
220222

223+
/**
224+
* @notice Batch cancels multiple auctions
225+
* @param castHashes Array of cast identifiers
226+
* @param authDatas Array of signature authorizations (must match castHashes length)
227+
* @dev Reverts entire batch if any cancellation fails
228+
*/
229+
function batchCancel(bytes32[] calldata castHashes, AuthData[] calldata authDatas) external;
230+
221231
/**
222232
* @notice Emergency recovery for stuck auctions
223233
* @param castHash Cast identifier
@@ -233,6 +243,14 @@ interface IAuction {
233243
*/
234244
function auctionState(bytes32 castHash) external view returns (AuctionState);
235245

246+
/**
247+
* @notice Get auction data with calculated state
248+
* @param castHash Cast identifier
249+
* @return Auction data with current calculated state
250+
* @dev Returns empty struct for non-existent auctions
251+
*/
252+
function getAuction(bytes32 castHash) external view returns (AuctionData memory);
253+
236254
/**
237255
* @notice Computes bid authorization hash
238256
* @return EIP-712 hash for signature verification

0 commit comments

Comments
 (0)