-
Notifications
You must be signed in to change notification settings - Fork 1
/
PrimeAuctionMarket.sol
179 lines (153 loc) · 8.36 KB
/
PrimeAuctionMarket.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.12;
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
//PrimeAuctionMarket is a marketplace to auction your erc721 tokens.
//you can auction an nft you own by providing the nft address, the tokenId, the startamount, and the auctionEndDate (btwn 1-60 days)
//At the end of the duration of the auction, either the highestBidder or the seller can end the auction
//if anyBid was placed for your nft , ending the auction deposits the nft into the buyer's account, and the price into the seller's account
//otherwise no bids means token transferred back to the seller.
contract PrimeAuctionMarket is Ownable {
using Counters for Counters.Counter;
Counters.Counter private auctionId;
event List(address nftContractAddress, uint tokenId);
event Bid(address nftContractAddress, uint tokenId, address indexed sender, uint amount);
event Withdraw(address nftContractAddress, uint tokenId, address indexed bidder, uint amount);
event End(address nftContractAddress, uint tokenId, address winner, uint amount, bool isOwnerEnded, bool isWinnerEnded);
event Cancel(address nftContractAddress, uint tokenId, address owner);
struct Auction {
uint tokenId;
address nftContractAddress;
address seller;
uint endAt;
bool ended;
address highestBidder;
uint highestBid;
uint startingPrice;
//maps the amount an account can withdraw to the account
mapping(address => uint) bids;
}
mapping(uint => Auction) public auctions;
uint numAuctions;
constructor () {
}
//list token with the token contract address and tokenId. Provide a starting price, and time to end the auction in days.
//days between 1-60 days
function listToken(address _nftAdd, uint _nftId, uint _startingPrice, uint auctionDays)
public payable canAuctionToken(_nftAdd, _nftId, _startingPrice, auctionDays) returns (uint id) {
IERC721(_nftAdd).transferFrom(msg.sender, address(this), _nftId);
uint currentId = auctionId.current();
auctionId.increment();
Auction storage auction = auctions[currentId];
auction.nftContractAddress = _nftAdd;
auction.tokenId = _nftId;
auction.seller = msg.sender;
auction.ended = false;
auction.startingPrice = _startingPrice;
auction.endAt = block.timestamp + (auctionDays * 1 days);
emit List(_nftAdd, _nftId);
//check if token has been transfered
bool contractIsTokenOwner = IERC721(_nftAdd).ownerOf(_nftId) == address(this) ? true
: false;
require(contractIsTokenOwner, "Listing failed as token has not been transferred to contract");
return currentId;
}
//bid for token during the duration of the auction. bid must be greater than the highest bid
// seller cannot bid
function bid(uint _auctionId) external payable canBid(_auctionId) {
Auction storage auction = auctions[_auctionId];
address initialHighestBidder = auction.highestBidder;
uint initialHighestBid = auction.highestBid;
//remember bids mapping holds the amount an account can withdraw
if (auction.highestBidder != address(0)) {
auction.bids[initialHighestBidder] += initialHighestBid;
}
auction.highestBidder = msg.sender;
auction.highestBid = msg.value;
emit Bid(auction.nftContractAddress, auction.tokenId, msg.sender, msg.value);
}
//people who took part in the bid without winning can withdraw their money
function withdrawBids(uint _auctionId) external canWithdraw(_auctionId) {
Auction storage auction = auctions[_auctionId];
uint bal = auction.bids[msg.sender];
auction.bids[msg.sender] = 0;
(bool sent, ) = payable(msg.sender).call{value: bal}("");
require(sent, "Bid withdrawal failed");
emit Withdraw(auction.nftContractAddress, auction.tokenId, msg.sender, bal);
}
//seller can end the auction and send token to winner, if no bid s/he gets their token back
function endAuctionAsSeller(uint _auctionId) external canEndAuction(_auctionId) returns (bool) {
(bool success, address nftContractAddress, uint tokenId, address winner, uint amount) = endAuction(_auctionId);
if (success) {
emit End(nftContractAddress, tokenId, winner, amount, true, false);
return true;
}
return false;
}
//winner can claim the token and end the auction (seller gets the money for sales)
function claimTokenAsWinner(uint _auctionId) external canClaimWinner(_auctionId) returns (bool) {
(bool success, address nftContractAddress, uint tokenId, address winner, uint amount) = endAuction(_auctionId);
if (success) {
emit End(nftContractAddress, tokenId, winner, amount, false, true);
return true;
}
return false;
}
function endAuction(uint _auctionId) internal returns (bool success, address nftContractAddress, uint tokenId, address winner, uint amount){
Auction storage auction = auctions[_auctionId];
auction.ended = true;
if (auction.highestBidder != address(0)) {
IERC721(auction.nftContractAddress).transferFrom(address(this), auction.highestBidder, auction.tokenId);
(bool sent, ) = payable(auction.seller).call{value: auction.highestBid}("");
require(sent, "Could not end auction");
return (true, auction.nftContractAddress, auction.tokenId, auction.highestBidder, auction.highestBid);
} else {
IERC721(auction.nftContractAddress).transferFrom(address(this), auction.seller, auction.tokenId);
return (true, auction.nftContractAddress, auction.tokenId, address(0), 0);
}
}
//pure function that returns the constant platform price 500_000 wei
function getPlatformPrice() internal pure returns (uint) {
return 500_000;
}
modifier canEndAuction(uint _auctionId) {
Auction storage auction = auctions[_auctionId];
require(msg.sender == auction.seller, "Only owner can end auction");
require(block.timestamp >= auction.endAt, "auction duration has not ended");
require(!auction.ended, "auction already ended");
_;
}
modifier canClaimWinner(uint _auctionId){
Auction storage auction = auctions[_auctionId];
require(msg.sender == auction.highestBidder, "You are not the Winner");
require(block.timestamp >= auction.endAt, "auction duration has not ended");
require(!auction.ended, "auction already ended");
_;
}
modifier canAuctionToken(address _nftAdd, uint _nftId, uint _startingPrice, uint _auctionDays){
require(msg.value >= getPlatformPrice(), "Attach platform fee of 5_00_000 wei");
bool callerIsTokenOwner = IERC721(_nftAdd).ownerOf(_nftId) == msg.sender ? true
: false;
require(callerIsTokenOwner, "You can only list tokens owned by you");
require(_startingPrice > 0, "Starting price must be greater than 0");
require(_auctionDays > 0 && _auctionDays <60, "auction duration should be between 1 and 60 days");
_;
}
modifier canBid(uint _auctionId){
Auction storage auction = auctions[_auctionId];
require( !auction.ended && (block.timestamp < auction.endAt), "auction has ended");
string memory price_error_msg = string.concat("Bid must not be smaller than the startting price: ", Strings.toString(auction.startingPrice));
require(msg.value >= auction.startingPrice, price_error_msg);
require(msg.value > auction.highestBid, "Bid must be greater than the current highest bid");
require(msg.sender != auction.seller, "owner cannot place a bid");
_;
}
modifier canWithdraw(uint _auctionId){
Auction storage auction = auctions[_auctionId];
require(msg.sender != auction.highestBidder, "Highest bidder cannot withdraw");
require(auction.bids[msg.sender]> 0, "Nothing to withdraw");
_;
}
}