-
Notifications
You must be signed in to change notification settings - Fork 20
/
EnglishAuction.sol
207 lines (169 loc) · 6.27 KB
/
EnglishAuction.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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
// SPDX-License-Identifier: MIT
/*
English auction for NFT
Auction Process:
1. The seller of the NFT deploys this contract setting the
initial bid, the NFT to be sold, and the Token to be
sold against in the constructor.
2. The auction lasts for 7 days (auction window).
3. Participants can bid `token` to become the new highest
bidder. It is possible to increase the bid marginally,
as long as the new position still becomes highest.
The participants have to pre-approve this contract for
the `token`, in order to successfully bid.
4. All bidders but the highest one can withdraw their bid.
After the auction window is past:
- Bids are no longer possible
- A call to `end()` transfers the NFT to the highest bidder,
and the highest bid amount to the seller.
Additional features:
- Bids can be increased, and not only by the bidder
- Bids can be reduced (partially withdrawn), by the bidder
or by a trusted third party operator
- Trusted operators can be set or unset by the bidder
*/
pragma solidity ^0.8.13;
/// @title Reduced ERC721 (NFT)
contract ERC721Mock {
// Ownership of tokens token-id -> owner
mapping(uint256 => address) private ownership;
function transferFrom(
address from,
address to,
uint256 tokenId
) external {
require(ownership[tokenId] == from);
ownership[tokenId] = to;
}
}
/// @title Reduced ERC20 (token)
contract ERC20Mock {
mapping(address => uint256) private balances;
function transferFrom(
address from,
address to,
uint amount
) external returns (bool) {
if (balances[from] < amount) {
return false;
}
balances[from] -= amount;
balances[to] += amount;
return true;
}
}
/// @title English auction for NFT
contract EnglishAuction {
event Start();
event Bid(address indexed sender, uint amount);
event Withdraw(address indexed bidder, uint amount);
event End(address winner, uint amount);
ERC721Mock public nft; // The auctioned NFT
ERC20Mock public token; // Accepted token for bidding
uint public nftId;
address payable public seller; // The seller of the NFT
uint public endAt;
bool public started;
bool public ended;
address public highestBidder;
uint public highestBid;
mapping(address => uint) public bids;
mapping(address => mapping(address => bool)) public operators;
/// @param _nft the auctioned NFT
/// @param _erc20 the token to be used for bidding
/// @param _startingBid minimal bid value
constructor(
address _nft,
address _erc20,
uint _nftId,
uint _startingBid
) {
nft = ERC721Mock(_nft);
nftId = _nftId;
token = ERC20Mock(_erc20);
seller = payable(msg.sender);
highestBid = _startingBid;
}
/// Start the auction
function start() external {
require(!started, "started");
require(!ended, "started");
require(msg.sender == seller, "not seller");
started = true;
nft.transferFrom(msg.sender, address(this), nftId);
endAt = block.timestamp + 7 days;
emit Start();
}
/// Set or unset trusted operator for sender. The trusted operator can withdraw
/// bidder's funds.
function setOperator(address operator, bool trusted) external {
operators[msg.sender][operator] = trusted;
}
function bid(uint amount) external {
_bid(msg.sender, msg.sender, amount);
}
/// Send tokens to increase the bid of `bidder`
function bidFor(address bidder, uint amount) external {
_bid(bidder, msg.sender, amount);
}
/// Bidding implementation.
/// @dev Funds are transferred from `payer` to support the bid of `bidder`.
function _bid(address bidder, address payer, uint amount) internal {
require(started, "not started");
require(block.timestamp < endAt, "ended");
uint previousBid = highestBid;
require(
token.transferFrom(payer, address(this), amount),
"token transfer failed"
);
bids[bidder] += amount;
highestBidder = bidder;
highestBid = bids[highestBidder];
require(bids[highestBidder] > previousBid, "new high value > highest");
emit Bid(bidder, amount);
}
/// Withdraw implementation.
/// @dev Bid of `bidder` is reduced and funds are sent to the `recipient`.
function _withdraw(address bidder, address recipient, uint256 amount) internal {
require(bidder != highestBidder, "bidder cannot withdraw");
bids[bidder] -= amount;
bool success = token.transferFrom(address(this), recipient, amount);
require(success, "token transfer failed");
emit Withdraw(bidder, amount);
}
/// Withdraw entire bid.
function withdraw() external {
_withdraw(msg.sender, msg.sender, bids[msg.sender]);
}
/// Reduce sender's bid amount, transferring the funds to recipient.
function withdrawAmount(address recipient, uint amount) external {
_withdraw(msg.sender, recipient, amount);
}
/// Reduce bid of `bidder`, transferring funds to message sender.
/// @notice message sender must be a trusted operator or the bidder.
function withdrawFor(address bidder, uint amount) external {
require(
operators[bidder][msg.sender] || msg.sender == bidder,
"that operator was not allowed"
);
_withdraw(bidder, msg.sender, amount);
}
/// End the auction, transfer the NFT to the winning bidder, and the highest bid
/// amount to the seller.
/// @notice If there is no winner, the seller receives the NFT and not tokens.
function end() external {
require(started, "not started");
require(block.timestamp >= endAt, "not ended");
require(!ended, "ended");
bool _success;
ended = true;
if (highestBidder != address(0)) {
nft.transferFrom(address(this), highestBidder, nftId);
_success = token.transferFrom(address(this), seller, bids[highestBidder]);
require(_success, "token transfer failed");
} else {
nft.transferFrom(address(this), seller, nftId);
}
emit End(highestBidder, highestBid);
}
}