/
AuctionEnderFacet.sol
170 lines (137 loc) · 5.89 KB
/
AuctionEnderFacet.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";
import "../../../interfaces/IEscrowTicketer.sol";
import "../../../interfaces/IAuctionEnder.sol";
import "../../../interfaces/ISeenHausNFT.sol";
import "../MarketHandlerBase.sol";
/**
* @title AuctionEnderFacet
*
* @notice Handles the operation of Seen.Haus auctions.
*
* @author Cliff Hall <cliff@futurescale.com> (https://twitter.com/seaofarrows)
*/
contract AuctionEnderFacet is IAuctionEnder, MarketHandlerBase {
// Threshold to auction extension window
uint256 constant extensionWindow = 15 minutes;
/**
* @dev Modifier to protect initializer function from being invoked twice.
*/
modifier onlyUnInitialized()
{
MarketHandlerLib.MarketHandlerInitializers storage mhi = MarketHandlerLib.marketHandlerInitializers();
require(!mhi.auctionEnderFacet, "Initializer: contract is already initialized");
mhi.auctionEnderFacet = true;
_;
}
/**
* @notice Facet Initializer
*
* Register supported interfaces
*/
function initialize()
public
onlyUnInitialized
{
DiamondLib.addSupportedInterface(type(IAuctionEnder).interfaceId);
}
/**
* @notice Close out a successfully completed auction.
*
* Funds are disbursed as normal. See {MarketHandlerBase.disburseFunds}
*
* Reverts if:
* - Auction doesn't exist
* - Auction timer has not yet elapsed
* - Auction has not yet started
* - Auction has already been settled
* - Bids have been placed
*
* Emits a AuctionEnded event on success.
*
* @param _consignmentId - the id of the consignment being sold
*/
function closeAuction(uint256 _consignmentId)
external
override
{
// Get Market Handler Storage slot
MarketHandlerLib.MarketHandlerStorage storage mhs = MarketHandlerLib.marketHandlerStorage();
// Get the consignment (reverting if consignment doesn't exist)
Consignment memory consignment = getMarketController().getConsignment(_consignmentId);
// Make sure the auction exists
Auction storage auction = mhs.auctions[_consignmentId];
require(auction.start != 0, "Auction does not exist");
// Make sure timer has elapsed
uint256 endTime = auction.start + auction.duration;
require(block.timestamp > endTime, "Auction end time not yet reached");
// Make sure auction hasn't been settled
require(auction.outcome == Outcome.Pending, "Auction has already been settled");
// Make sure it there was at least one bid
require(auction.buyer != address(0), "No bids have been placed");
// Mark auction as settled
auction.state = State.Ended;
auction.outcome = Outcome.Closed;
// Distribute the funds (pay royalties, staking, multisig, and seller)
disburseFunds(_consignmentId, consignment.pendingPayout);
getMarketController().setConsignmentPendingPayout(consignment.id, 0);
// Determine if consignment is physical
address nft = getMarketController().getNft();
if (nft == consignment.tokenAddress && ISeenHausNFT(nft).isPhysical(consignment.tokenId)) {
// For physicals, issue an escrow ticket to the buyer
address escrowTicketer = getMarketController().getEscrowTicketer(_consignmentId);
IEscrowTicketer(escrowTicketer).issueTicket(_consignmentId, 1, auction.buyer);
} else {
// Release the purchased amount of the consigned token supply to buyer
getMarketController().releaseConsignment(_consignmentId, 1, auction.buyer);
}
// Notify listeners about state change
emit AuctionEnded(consignment.id, auction.outcome);
// Track the winning bid info against the token itself
emit TokenHistoryTracker(consignment.tokenAddress, consignment.tokenId, auction.buyer, auction.bid, consignment.supply, consignment.id);
}
/**
* @notice Cancel an auction
*
* If there is a standing bid, it is returned to the bidder.
* Consigned inventory will be transferred back to the seller.
*
* Reverts if:
* - Caller does not have ADMIN role or is not consignor
* - Auction doesn't exist
* - Auction has already been settled
*
* Emits a AuctionEnded event on success.
*
* @param _consignmentId - the id of the consignment being sold
*/
function cancelAuction(uint256 _consignmentId)
external
override
onlyRoleOrConsignor(ADMIN, _consignmentId)
{
// Get Market Handler Storage slot
MarketHandlerLib.MarketHandlerStorage storage mhs = MarketHandlerLib.marketHandlerStorage();
// Get the consignment (reverting if consignment doesn't exist)
Consignment memory consignment = getMarketController().getConsignment(_consignmentId);
// Make sure auction exists
Auction storage auction = mhs.auctions[_consignmentId];
require(auction.start != 0, "Auction does not exist");
// Make sure auction hasn't been settled
require(auction.state != State.Ended, "Auction has already been settled");
// Mark auction as settled
auction.state = State.Ended;
auction.outcome = Outcome.Canceled;
// Give back the previous bidder's money
if (auction.bid > 0) {
auction.buyer.transfer(auction.bid);
emit CanceledAuctionBidReturned(_consignmentId, auction.buyer, auction.bid);
}
getMarketController().setConsignmentPendingPayout(consignment.id, 0);
// Release the consigned token supply to seller
getMarketController().releaseConsignment(_consignmentId, 1, consignment.seller);
// Notify listeners about state change
emit AuctionEnded(_consignmentId, auction.outcome);
}
}