@simplenft/api 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/blueprint.config.ts +13 -0
- package/contracts/buyer_profile.tact +73 -0
- package/contracts/imports/stdlib.fc +625 -0
- package/contracts/jetton_eq.tact +79 -0
- package/contracts/message.tact +189 -0
- package/contracts/nft_item.tact +98 -0
- package/contracts/nft_item_free.tact +98 -0
- package/contracts/packages/math/float.fc +95 -0
- package/contracts/packages/misc/distributor_messages.tact +154 -0
- package/contracts/packages/nap/errcodes.tact +5 -0
- package/contracts/packages/nap/messages.tact +34 -0
- package/contracts/packages/token/jetton/JettonMaster.tact +127 -0
- package/contracts/packages/token/jetton/JettonWallet.tact +248 -0
- package/contracts/packages/token/nft/AuctionErrorCode.tact +55 -0
- package/contracts/packages/token/nft/NFTAuction.tact +226 -0
- package/contracts/packages/token/nft/NFTAuctionMarket.tact +302 -0
- package/contracts/packages/token/nft/NFTCollection.tact +69 -0
- package/contracts/packages/token/nft/NFTItem.tact +190 -0
- package/contracts/packages/token/nft/extensions/NFTEditable.tact +3 -0
- package/contracts/packages/token/nft/extensions/NFTRoyalty.tact +63 -0
- package/contracts/packages/traits/taxable.tact +31 -0
- package/contracts/packages/utils/Estimatable.tact +11 -0
- package/contracts/packages/utils/Lockable.tact +23 -0
- package/contracts/sbt_item.tact +70 -0
- package/contracts/simple_nft_collection.tact +177 -0
- package/contracts/simple_nft_collection_v2.tact +304 -0
- package/contracts/simple_nft_master.tact +102 -0
- package/dist/api.d.ts +10 -0
- package/dist/api.js +58 -0
- package/dist/backend-service.d.ts +13 -0
- package/dist/backend-service.js +29 -0
- package/dist/backend-types.d.ts +60 -0
- package/dist/backend-types.js +2 -0
- package/dist/content.d.ts +3 -0
- package/dist/content.js +30 -0
- package/dist/contracts/tact_NftItem.d.ts +619 -0
- package/dist/contracts/tact_NftItem.js +2312 -0
- package/dist/contracts/tact_SimpleNftCollectionV2.d.ts +658 -0
- package/dist/contracts/tact_SimpleNftCollectionV2.js +2427 -0
- package/dist/contracts/tact_SimpleNftMaster.d.ts +624 -0
- package/dist/contracts/tact_SimpleNftMaster.js +2350 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +254 -0
- package/dist/types.d.ts +53 -0
- package/dist/types.js +2 -0
- package/package.json +56 -0
- package/src/api.ts +62 -0
- package/src/backend-service.ts +40 -0
- package/src/backend-types.ts +72 -0
- package/src/content.ts +36 -0
- package/src/contracts/tact_NftItem.ts +2718 -0
- package/src/contracts/tact_SimpleNftCollectionV2.ts +2843 -0
- package/src/contracts/tact_SimpleNftMaster.ts +2759 -0
- package/src/index.ts +361 -0
- package/src/types.ts +62 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file provides traits for the NFT Auction contract, allowing users to bid on NFTs.
|
|
3
|
+
When the auction ends, the NFT goes to the highest bidder and the seller receives the bid amount.
|
|
4
|
+
|
|
5
|
+
Reference:
|
|
6
|
+
[Official Implementation](https://github.com/ton-blockchain/token-contract/blob/991bdb4925653c51b0b53ab212c53143f71f5476/nft/nft-marketplace.fc)
|
|
7
|
+
[Official Implementation](https://github.com/ton-blockchain/token-contract/blob/991bdb4925653c51b0b53ab212c53143f71f5476/nft/nft-sale.fc)
|
|
8
|
+
[NFT Auction Template](https://github.com/avolabs-io/nft-auction)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
trait NFTAuctionStandard {
|
|
12
|
+
virtual const minTonsForStorage: Int = ton("0.03");
|
|
13
|
+
virtual const gasConsumption: Int = ton("0.03");
|
|
14
|
+
|
|
15
|
+
owner: Address;
|
|
16
|
+
nftAddress: Address;
|
|
17
|
+
seller: Address;
|
|
18
|
+
auctionInfo: AuctionInfo;
|
|
19
|
+
auctionBidPeriod: Int;
|
|
20
|
+
isInitialized: Int;
|
|
21
|
+
auctionEndTime: Int;
|
|
22
|
+
|
|
23
|
+
//********************************************//
|
|
24
|
+
// Messages //
|
|
25
|
+
//********************************************//
|
|
26
|
+
|
|
27
|
+
// @dev Default receive function to receive funds
|
|
28
|
+
receive() {}
|
|
29
|
+
|
|
30
|
+
// @dev Initializes the auction when called by the owner
|
|
31
|
+
receive(msg: BuildNftAuction) {
|
|
32
|
+
let ctx: Context = context();
|
|
33
|
+
require(ctx.sender == self.owner, "Only owner can build nft auction contract");
|
|
34
|
+
require(self.isInitialized == 0, "Contract is already initialized");
|
|
35
|
+
require(msg.auctionInfo.buyNowPrice > msg.auctionInfo.reservePrice, "BuyNowPrice must be greater than reservePrice");
|
|
36
|
+
self.auctionInfo = msg.auctionInfo;
|
|
37
|
+
self.isInitialized = 1;
|
|
38
|
+
self.auctionBidPeriod = 0;
|
|
39
|
+
self.auctionEndTime = 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// @dev Accepts bids for the NFT as long as the auction is active
|
|
43
|
+
receive("Bid") {
|
|
44
|
+
// Check if auction is still active.
|
|
45
|
+
require(now() < self.auctionEndTime || self.auctionEndTime == 0, "Auction ended");
|
|
46
|
+
require(self.isInitialized == 1, "Contract is not initialized");
|
|
47
|
+
require(now() < self.auctionBidPeriod | self.auctionBidPeriod ==0, "Auction bid period ended");
|
|
48
|
+
|
|
49
|
+
let ctx: Context = context();
|
|
50
|
+
let buyer: Address = ctx.sender;
|
|
51
|
+
require(buyer != self.auctionInfo.nftSeller, "NFT Seller cannot bid");
|
|
52
|
+
let bidValue: Int = ctx.value;
|
|
53
|
+
let buyNowPrice: Int = self.auctionInfo.buyNowPrice;
|
|
54
|
+
if(bidValue >= buyNowPrice) {
|
|
55
|
+
self.auctionInfo.nftHighestBid = bidValue;
|
|
56
|
+
// Pay winning bid amount to seller.
|
|
57
|
+
self._send_winning_bid_amount();
|
|
58
|
+
// Transfer NFT to buyer
|
|
59
|
+
self._transfer_nft(buyer);
|
|
60
|
+
self.isInitialized = 0;
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let bidIncreaseAmount: Int = (self.auctionInfo.nftHighestBid * (10000 + self.auctionInfo.bidIncreasePercentage)) / 10000;
|
|
65
|
+
require(bidValue > bidIncreaseAmount, "Bid doesn't meet the minimum increase requirement");
|
|
66
|
+
// Send back previous highest bid to previous highest bidder.
|
|
67
|
+
let prevNftHighestBidder: Address = self.auctionInfo.nftHighestBidder;
|
|
68
|
+
let prevNftHighestBid: Int = self.auctionInfo.nftHighestBid;
|
|
69
|
+
let paybackTon: Int = max(prevNftHighestBid - self.minTonsForStorage - self.gasConsumption,0);
|
|
70
|
+
send(SendParameters{
|
|
71
|
+
to: prevNftHighestBidder,
|
|
72
|
+
value: paybackTon,
|
|
73
|
+
mode: SendPayGasSeparately,
|
|
74
|
+
bounce: false,
|
|
75
|
+
body: "Pay bid money back to the prevNftHighestBidder".asComment()
|
|
76
|
+
});
|
|
77
|
+
// Update highest bid and Transfer ton back to previous highest bidder.
|
|
78
|
+
self.auctionInfo.nftHighestBidder = ctx.sender;
|
|
79
|
+
self.auctionInfo.nftHighestBid = bidValue;
|
|
80
|
+
// If bid value is greater than reserve price, then the auction is being started.
|
|
81
|
+
if(bidValue > self.auctionInfo.reservePrice) {
|
|
82
|
+
self._update_auction_bid_period();
|
|
83
|
+
if(self.auctionEndTime == 0) {
|
|
84
|
+
// If the auction start, then set the auction end time.
|
|
85
|
+
self._update_auction_end_time();
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// @dev Settles the auction, transferring the NFT to the highest bidder and the funds to the seller
|
|
91
|
+
receive("settleAuction") {
|
|
92
|
+
require(now() >= self.auctionBidPeriod, "Auction not yet ended");
|
|
93
|
+
// Pay winning bid amount to seller.
|
|
94
|
+
self._send_winning_bid_amount();
|
|
95
|
+
|
|
96
|
+
// Transfer NFT to buyer
|
|
97
|
+
let buyer: Address = self.auctionInfo.nftHighestBidder;
|
|
98
|
+
self._transfer_nft(buyer);
|
|
99
|
+
self.isInitialized = 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// @dev Allows owner to adjust auction's reserve or buy-now prices.
|
|
103
|
+
receive(msg: ReviseAuction) {
|
|
104
|
+
let ctx: Context = context();
|
|
105
|
+
require(ctx.sender == self.owner, "Only owner can revise auction contract");
|
|
106
|
+
require(self.auctionInfo.reservePrice == msg.reviseAuctionInfo.reservePrice || msg.reviseAuctionInfo.buyNowPrice == self.auctionInfo.buyNowPrice, "Cannot update reserve price and buy now price at the same time.");
|
|
107
|
+
|
|
108
|
+
// Update the reserve price of the auction.
|
|
109
|
+
// This can only be done if no bid has been made that already exceeds the original minimum price.
|
|
110
|
+
if(self.auctionEndTime == 0 && self.auctionInfo.reservePrice != msg.reviseAuctionInfo.reservePrice && msg.reviseAuctionInfo.reservePrice < self.auctionInfo.buyNowPrice) {
|
|
111
|
+
|
|
112
|
+
self.auctionInfo.reservePrice = msg.reviseAuctionInfo.reservePrice;
|
|
113
|
+
if(self.auctionInfo.nftHighestBid > self.auctionInfo.reservePrice) {
|
|
114
|
+
self._update_auction_bid_period();
|
|
115
|
+
self._update_auction_end_time();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// Update the buy now price of the auction.
|
|
119
|
+
// This can only be done if no bid has been made that already exceeds the original minimum price.
|
|
120
|
+
if(self.auctionEndTime == 0 && msg.reviseAuctionInfo.buyNowPrice != self.auctionInfo.buyNowPrice && msg.reviseAuctionInfo.buyNowPrice > self.auctionInfo.reservePrice) {
|
|
121
|
+
self.auctionInfo.buyNowPrice = msg.reviseAuctionInfo.buyNowPrice;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// @dev Ends the auction and transfers the NFT to the highest bidder or back to the seller(If autcion not started)
|
|
126
|
+
receive("EndAuction") {
|
|
127
|
+
// If this auction started, it will transfer NFT to highest bidder.
|
|
128
|
+
// Else, it will transfer NFT to seller.
|
|
129
|
+
if(self.auctionEndTime > 0) {
|
|
130
|
+
// Pay royalty to the creator of the NFT
|
|
131
|
+
// TODO: Implement royalty payment
|
|
132
|
+
|
|
133
|
+
// Pay winning bid amount to seller.
|
|
134
|
+
self._send_winning_bid_amount();
|
|
135
|
+
// Transfer NFT to buyer
|
|
136
|
+
let buyer: Address = self.auctionInfo.nftHighestBidder;
|
|
137
|
+
self._transfer_nft(buyer);
|
|
138
|
+
self.isInitialized = 0;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Transfer NFT to seller
|
|
142
|
+
let seller: Address = self.auctionInfo.nftSeller;
|
|
143
|
+
self._transfer_nft(seller);
|
|
144
|
+
self.isInitialized = 0;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
//********************************************//
|
|
149
|
+
// Internal functions //
|
|
150
|
+
//********************************************//
|
|
151
|
+
|
|
152
|
+
// @dev Updates the auction bid period time based on the latest bid and the defined auction bid period
|
|
153
|
+
virtual inline fun _update_auction_bid_period() {
|
|
154
|
+
self.auctionBidPeriod = now() + self.auctionInfo.auctionBidPeriod;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// @dev Updates the auction end time
|
|
158
|
+
virtual inline fun _update_auction_end_time() {
|
|
159
|
+
self.auctionEndTime = now() + self.auctionInfo.auctionPeriod;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// @dev Transfer the NFT to the highest bidder
|
|
163
|
+
// @note If you want change msg value, you should make sure that is enough for NFT Auction market contract to transfer NFT.
|
|
164
|
+
virtual inline fun _transfer_nft(buyer: Address) {
|
|
165
|
+
send(SendParameters{
|
|
166
|
+
to: self.owner,
|
|
167
|
+
value: ton("0.06"),
|
|
168
|
+
bounce: true,
|
|
169
|
+
mode: SendPayGasSeparately,
|
|
170
|
+
body: TransferNFT {
|
|
171
|
+
nftAddress: self.nftAddress,
|
|
172
|
+
seller: self.auctionInfo.nftSeller,
|
|
173
|
+
query_id: 0,
|
|
174
|
+
new_owner: buyer,
|
|
175
|
+
response_destination: buyer,
|
|
176
|
+
custom_payload: emptyCell(),
|
|
177
|
+
forward_amount: 0,
|
|
178
|
+
forward_payload: emptySlice()
|
|
179
|
+
}.toCell()
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// @dev Transfers the highest bid amount to the seller
|
|
184
|
+
virtual inline fun _send_winning_bid_amount() {
|
|
185
|
+
let seller: Address = self.auctionInfo.beneficiary;
|
|
186
|
+
let winningBidAmount: Int = self.auctionInfo.nftHighestBid;
|
|
187
|
+
send(SendParameters{
|
|
188
|
+
to: seller,
|
|
189
|
+
value: winningBidAmount - ton("0.06"),
|
|
190
|
+
mode: SendPayGasSeparately,
|
|
191
|
+
bounce: false,
|
|
192
|
+
body: "Pay winning bid amount".asComment()
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// @dev Initializes the auction end time to 0, allowing the seller to auction the NFT again in the future
|
|
197
|
+
virtual inline fun _init_auction_end() {
|
|
198
|
+
let ctx: Context = context();
|
|
199
|
+
require(ctx.sender == self.owner, "Only owner can init auction end time.");
|
|
200
|
+
self.auctionBidPeriod = 0;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
//*********************************//
|
|
204
|
+
// Getters //
|
|
205
|
+
//*********************************//
|
|
206
|
+
|
|
207
|
+
// @dev Returns the current auction information
|
|
208
|
+
get fun get_auctin_info(): AuctionInfo {
|
|
209
|
+
return self.auctionInfo;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// @dev Checks if the auction is initialized and returns the state (1 for initialized, 0 otherwise)
|
|
213
|
+
get fun get_is__initialized(): Int {
|
|
214
|
+
return self.isInitialized;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// @dev Returns the end time of the auction
|
|
218
|
+
get fun get_auction_end(): Int {
|
|
219
|
+
return self.auctionEndTime;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// @dev Retruns the auction bid period
|
|
223
|
+
get fun get_auction_bid_period(): Int {
|
|
224
|
+
return self.auctionBidPeriod;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file provides traits for NFT auction market which can be easily used in a permissionless and flexible manner to auction (or simply buy/sell) NFTs.
|
|
3
|
+
Sellers and bidders are able to make customized auctions and bids that allow for a holistic NFT auction/sale mechanism.
|
|
4
|
+
|
|
5
|
+
Reference:
|
|
6
|
+
[Official Implementation](https://github.com/ton-blockchain/token-contract/blob/991bdb4925653c51b0b53ab212c53143f71f5476/nft/nft-marketplace.fc)
|
|
7
|
+
[Official Implementation](https://github.com/ton-blockchain/token-contract/blob/991bdb4925653c51b0b53ab212c53143f71f5476/nft/nft-sale.fc)
|
|
8
|
+
[NFT Auction Template](https://github.com/avolabs-io/nft-auction)
|
|
9
|
+
*/
|
|
10
|
+
import "./NFTItem";
|
|
11
|
+
|
|
12
|
+
message SetUpAuction {
|
|
13
|
+
nftAddress: Address; // NFT address to be auctioned
|
|
14
|
+
reservePrice: Int as coins; // minimum bid price to start the auction timer
|
|
15
|
+
buyNowPrice: Int as coins; // price at which the NFT can be directly bought
|
|
16
|
+
auctionPeriod: Int as uint256; // time when the auction ends after it starts
|
|
17
|
+
beneficiary: Address?; // the address of the beneficiary
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
message ReviseSetUpAuction {
|
|
21
|
+
nftAddress: Address; // NFT address to be auctioned
|
|
22
|
+
reservePrice: Int as coins; // minimum bid price to start the auction timer
|
|
23
|
+
buyNowPrice: Int as coins; // price at which the NFT can be directly bought
|
|
24
|
+
auctionPeriod: Int as uint256; // time when the auction ends after it starts
|
|
25
|
+
beneficiary: Address?; // the address of the beneficiary
|
|
26
|
+
// TODO: beneficiary: Address?; // the address of the beneficiary
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
message ReviseAuction {
|
|
30
|
+
reviseAuctionInfo: AuctionInfo;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
message BuildNftAuction {
|
|
34
|
+
auctionInfo: AuctionInfo;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
message EndAuction {
|
|
38
|
+
nftAddress: Address;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// @dev This message is used to ask NFT auction market contract to transfer NFT to the new owner
|
|
42
|
+
message TransferNFT {
|
|
43
|
+
nftAddress: Address;
|
|
44
|
+
seller: Address;
|
|
45
|
+
query_id: Int as uint64;
|
|
46
|
+
new_owner: Address;
|
|
47
|
+
response_destination: Address;
|
|
48
|
+
custom_payload: Cell?;
|
|
49
|
+
forward_amount: Int as coins;
|
|
50
|
+
forward_payload: Slice as remaining;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// @dev This struct is used to store auction information
|
|
54
|
+
// @note If you want to use custom auction parameters or logic, consider overriding this struct and setUpAuction function in a derived contract
|
|
55
|
+
struct AuctionInfo {
|
|
56
|
+
bidIncreasePercentage: Int as uint256; // the minimum percentage by which a new bid must exceed the current highest bid
|
|
57
|
+
auctionBidPeriod: Int as uint256; // increments the length of time the auction is open in which a new bid can be made after each bid
|
|
58
|
+
auctionPeriod: Int as uint256; // the time at which the auction will end
|
|
59
|
+
reservePrice: Int as coins; // the minimum price that must be paid for the NFT
|
|
60
|
+
buyNowPrice: Int as coins; // the price that must be paid for the NFT if the buyer chooses to buy it now
|
|
61
|
+
nftHighestBid: Int as coins; // the highest bid that has been made so far
|
|
62
|
+
nftHighestBidder: Address; // the address of the bidder who has made the highest bid so far
|
|
63
|
+
nftSeller: Address; // the address of the seller
|
|
64
|
+
whitelistedBuyer: Address; // the seller can specify a whitelisted address for a sale (this is effectively a direct sale)
|
|
65
|
+
nftRecipient: Address; // the bidder can specify a recipient for the NFT if their bid is successful
|
|
66
|
+
beneficiary: Address; // the address of the beneficiary
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
trait NFTAuctionMarketStandard {
|
|
70
|
+
owner: Address;
|
|
71
|
+
// Check whether nft is transfered to NFT Auction Market Contract
|
|
72
|
+
auctionTransferCheck: map<Int, Int>; // key => hash(sellerAddress and nftAddress), vlaue => 1: set, 0: not set
|
|
73
|
+
// Check whether nft auction is over or not
|
|
74
|
+
auctionOverCheck: map<Address, Address>; // key => nft auction contract address, value => 1: not over, 0: over
|
|
75
|
+
|
|
76
|
+
// @dev Default parameters for setting up an NFT auction
|
|
77
|
+
virtual const defaultBidIncreasePercentage: Int = 100;
|
|
78
|
+
virtual const defaultAuctionBidPeriod: Int = 86400; // 1 day
|
|
79
|
+
virtual const minimumSettableIncreasePercentage: Int = 100;
|
|
80
|
+
virtual const maximumMinPricePercentage: Int = 8000;
|
|
81
|
+
|
|
82
|
+
//********************************************//
|
|
83
|
+
// Messages //
|
|
84
|
+
//********************************************//
|
|
85
|
+
|
|
86
|
+
// Default receive function to receive funds
|
|
87
|
+
receive() {}
|
|
88
|
+
|
|
89
|
+
// @dev Processes the OwnershipAssigned message and updates auction mappings,
|
|
90
|
+
// and confirms NFT transfer to the Auction Market.
|
|
91
|
+
receive(msg: OwnershipAssigned) {
|
|
92
|
+
let ctx: Context = context();
|
|
93
|
+
let prev_owner: Address = msg.prev_owner; // Seller Address
|
|
94
|
+
let nftAddress: Address = ctx.sender;
|
|
95
|
+
let hashSellerNftAddress: Int = self.get_hash_seller_nft_address(prev_owner, nftAddress);
|
|
96
|
+
// Set nft transfer checking to 1
|
|
97
|
+
self.auctionTransferCheck.set(hashSellerNftAddress, 1);
|
|
98
|
+
let payload: Slice = msg.forward_payload;
|
|
99
|
+
|
|
100
|
+
if(payload.empty() == false) {
|
|
101
|
+
self._parse_forward_payload(prev_owner, nftAddress, payload);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// @dev Handles the receipt of a SetUpAuction message.
|
|
106
|
+
// First, it verifies if the NFT has been transferred to the NFT Auction Market Contract.
|
|
107
|
+
// Upon successful validation, it sets up the auction for the specified NFT and deploys
|
|
108
|
+
// a new NFT Auction Contract instance for it.
|
|
109
|
+
receive(msg: SetUpAuction) {
|
|
110
|
+
let ctx: Context = context();
|
|
111
|
+
let sellerAddress: Address = ctx.sender;
|
|
112
|
+
let hashSellerNftAddress: Int = self.get_hash_seller_nft_address(sellerAddress, msg.nftAddress);
|
|
113
|
+
self._auction_transfer_validate(hashSellerNftAddress);
|
|
114
|
+
if(msg.beneficiary == null) {
|
|
115
|
+
msg.beneficiary = sellerAddress;
|
|
116
|
+
}
|
|
117
|
+
// Set up auction info
|
|
118
|
+
let auctionInfo: AuctionInfo = self._set_up_auction(sellerAddress, msg.nftAddress, msg.reservePrice, msg.buyNowPrice, msg.auctionPeriod, msg.beneficiary!!); // set up auction
|
|
119
|
+
self._set_price_validate(msg.buyNowPrice, msg.reservePrice);
|
|
120
|
+
let nftAuctionInit: StateInit = self._nft_auction_init(msg.nftAddress, sellerAddress);
|
|
121
|
+
let nftAuctionAddress: Address = self.get_nft_auction_address(msg.nftAddress, sellerAddress);
|
|
122
|
+
self._auction_set_validate(nftAuctionAddress);
|
|
123
|
+
self.auctionOverCheck.set(nftAuctionAddress, msg.nftAddress);
|
|
124
|
+
|
|
125
|
+
// Deploy a new NFT Auction Contract
|
|
126
|
+
self._build_auction(nftAuctionAddress, auctionInfo, nftAuctionInit);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// @dev Handles the TransferNFT message and facilitates NFT transfer to the auction's winning bidder.
|
|
130
|
+
receive(msg: TransferNFT) {
|
|
131
|
+
let ctx: Context = context();
|
|
132
|
+
let nftAuctionAddress: Address = ctx.sender;
|
|
133
|
+
self._auction_not_set_validate(nftAuctionAddress);
|
|
134
|
+
send(SendParameters{
|
|
135
|
+
to: msg.nftAddress,
|
|
136
|
+
value: 0,
|
|
137
|
+
bounce: false,
|
|
138
|
+
mode: SendRemainingValue,
|
|
139
|
+
body: Transfer {
|
|
140
|
+
query_id: msg.query_id,
|
|
141
|
+
new_owner: msg.new_owner,
|
|
142
|
+
response_destination: msg.response_destination,
|
|
143
|
+
custom_payload: msg.custom_payload,
|
|
144
|
+
forward_amount: msg.forward_amount,
|
|
145
|
+
forward_payload: msg.forward_payload
|
|
146
|
+
}.toCell()
|
|
147
|
+
});
|
|
148
|
+
self.auctionOverCheck.set(ctx.sender, null);
|
|
149
|
+
let hashSellerNftAddress: Int = self.get_hash_seller_nft_address(msg.seller, msg.nftAddress);
|
|
150
|
+
self.auctionTransferCheck.set(hashSellerNftAddress, null);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// @dev Updates auction details after verifying NFT transfer and previous auction setup, then communicates the update to the nftAuctionAddress.
|
|
154
|
+
receive(msg: ReviseSetUpAuction) {
|
|
155
|
+
let ctx: Context = context();
|
|
156
|
+
let sellerAddress: Address = ctx.sender;
|
|
157
|
+
let hashSellerNftAddress: Int = self.get_hash_seller_nft_address(sellerAddress, msg.nftAddress);
|
|
158
|
+
self._auction_transfer_validate(hashSellerNftAddress);
|
|
159
|
+
// Get nft auction address
|
|
160
|
+
let nftAuctionAddress: Address = self.get_nft_auction_address(msg.nftAddress, sellerAddress);
|
|
161
|
+
self._auction_not_set_validate(nftAuctionAddress);
|
|
162
|
+
if(msg.beneficiary == null) {
|
|
163
|
+
msg.beneficiary = sellerAddress;
|
|
164
|
+
}
|
|
165
|
+
// Set up auction info
|
|
166
|
+
let auctionInfo: AuctionInfo = self._set_up_auction(sellerAddress, msg.nftAddress, msg.reservePrice, msg.buyNowPrice, msg.auctionPeriod, msg.beneficiary!!); // set up auction
|
|
167
|
+
self.auctionOverCheck.set(nftAuctionAddress, msg.nftAddress);
|
|
168
|
+
let newAuctionInfo: AuctionInfo = self._set_up_auction(sellerAddress, msg.nftAddress, msg.reservePrice, msg.buyNowPrice, msg.auctionPeriod, msg.beneficiary!!); // set up auction
|
|
169
|
+
send(SendParameters{
|
|
170
|
+
to: nftAuctionAddress,
|
|
171
|
+
value: 0,
|
|
172
|
+
mode: SendRemainingValue,
|
|
173
|
+
bounce: false,
|
|
174
|
+
body: ReviseAuction {
|
|
175
|
+
reviseAuctionInfo: newAuctionInfo
|
|
176
|
+
}.toCell()
|
|
177
|
+
}
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// @dev Allows the seller to terminate an auction.
|
|
182
|
+
// It verifies if the auction was previously set and then sends a message to the nftAuctionAddress to conclude the auction.
|
|
183
|
+
// It will transfer the NFT to the highest bidder or back to the seller(If autcion not started).
|
|
184
|
+
receive(msg: EndAuction) {
|
|
185
|
+
// Seller can end auction.
|
|
186
|
+
let ctx: Context = context();
|
|
187
|
+
let sellerAddress: Address = ctx.sender;
|
|
188
|
+
let hashSellerNftAddress: Int = self.get_hash_seller_nft_address(sellerAddress, msg.nftAddress);
|
|
189
|
+
let nftAuctionAddress: Address = self.get_nft_auction_address(msg.nftAddress, sellerAddress);
|
|
190
|
+
self._auction_not_set_validate(nftAuctionAddress);
|
|
191
|
+
send(SendParameters{
|
|
192
|
+
to: nftAuctionAddress,
|
|
193
|
+
value: 0,
|
|
194
|
+
mode: SendRemainingValue,
|
|
195
|
+
bounce: false,
|
|
196
|
+
body: "EndAuction".asComment()
|
|
197
|
+
}
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
//********************************************//
|
|
202
|
+
// Internal functions //
|
|
203
|
+
//********************************************//
|
|
204
|
+
|
|
205
|
+
virtual inline fun _parse_forward_payload(seller: Address, nftAddress: Address, payload: Slice) {
|
|
206
|
+
// Parse payload
|
|
207
|
+
let beneficiary: Address = payload.loadAddress();
|
|
208
|
+
let reservePrice: Int = payload.loadCoins();
|
|
209
|
+
let buyNowPrice: Int = payload.loadCoins();
|
|
210
|
+
let auctionPeriod: Int = payload.loadUint(256);
|
|
211
|
+
|
|
212
|
+
// Set up auction info
|
|
213
|
+
let auctionInfo: AuctionInfo = self._set_up_auction(seller, nftAddress, reservePrice, buyNowPrice, auctionPeriod, beneficiary); // set up auction
|
|
214
|
+
self._set_price_validate(buyNowPrice, reservePrice);
|
|
215
|
+
let nftAuctionInit: StateInit = self._nft_auction_init(nftAddress, seller);
|
|
216
|
+
let nftAuctionAddress: Address = self.get_nft_auction_address(nftAddress, seller);
|
|
217
|
+
self._auction_set_validate(nftAuctionAddress);
|
|
218
|
+
self.auctionOverCheck.set(nftAuctionAddress, nftAddress);
|
|
219
|
+
|
|
220
|
+
// Deploy a new NFT Auction Contract
|
|
221
|
+
self._build_auction(nftAuctionAddress, auctionInfo, nftAuctionInit);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
virtual inline fun _build_auction(nftAuctionAddress: Address, auctionInfo: AuctionInfo, nftAuctionInit: StateInit) {
|
|
225
|
+
send(SendParameters{
|
|
226
|
+
to: nftAuctionAddress,
|
|
227
|
+
value: 0,
|
|
228
|
+
mode: SendRemainingValue,
|
|
229
|
+
bounce: false,
|
|
230
|
+
body: BuildNftAuction {
|
|
231
|
+
auctionInfo: auctionInfo
|
|
232
|
+
}.toCell(),
|
|
233
|
+
code: nftAuctionInit.code,
|
|
234
|
+
data: nftAuctionInit.data
|
|
235
|
+
}
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
virtual inline fun _set_price_validate(buyNowPrice: Int, reservePrice: Int) {
|
|
240
|
+
require(buyNowPrice > reservePrice, "BuyNowPrice must be greater than reservePrice.");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
virtual inline fun _auction_set_validate(nftAuctionAddress: Address) {
|
|
244
|
+
require(self.auctionOverCheck.get(nftAuctionAddress) == null, "Auction was already set for this NFT.");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
virtual inline fun _auction_not_set_validate(nftAuctionAddress: Address) {
|
|
248
|
+
require(self.auctionOverCheck.get(nftAuctionAddress) != null, "Auction was not set before.");
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
virtual inline fun _auction_transfer_validate(hashSellerNftAddress: Int) {
|
|
252
|
+
require(self.get_is_auction_transfer_check(hashSellerNftAddress) == 1, "This NFT didn't transfer to NFT Auction Market Contract yet.");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// @dev Initializes an auction for a specified NFT with given parameters such as reserve price, buy now price, and auction duration.
|
|
256
|
+
// @note If you want to use custom auction parameters or logic, consider overriding this function and AuctionInfo struct in a derived contract.
|
|
257
|
+
virtual inline fun _set_up_auction(sellerAddress: Address, nftAddress: Address, reservePrice: Int, buyNowPrice: Int, auctionPeriod: Int, beneficiary: Address): AuctionInfo {
|
|
258
|
+
let hashSellerNftAddress: Int = self.get_hash_seller_nft_address(sellerAddress, nftAddress);
|
|
259
|
+
return AuctionInfo {
|
|
260
|
+
bidIncreasePercentage: self.defaultBidIncreasePercentage,
|
|
261
|
+
auctionBidPeriod: self.defaultAuctionBidPeriod,
|
|
262
|
+
auctionPeriod: auctionPeriod,
|
|
263
|
+
reservePrice: reservePrice,
|
|
264
|
+
buyNowPrice: buyNowPrice,
|
|
265
|
+
nftHighestBid: 0,
|
|
266
|
+
nftHighestBidder: sellerAddress,
|
|
267
|
+
nftSeller: sellerAddress,
|
|
268
|
+
whitelistedBuyer: sellerAddress,
|
|
269
|
+
nftRecipient: sellerAddress,
|
|
270
|
+
beneficiary: beneficiary
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// @dev Retrieves the initial state for the NFT auction contract.
|
|
275
|
+
// @note one MUST override this function to provide NFT Auction initCode
|
|
276
|
+
abstract fun _nft_auction_init(nftAddress: Address, seller: Address): StateInit;
|
|
277
|
+
|
|
278
|
+
//*********************************//
|
|
279
|
+
// Getters //
|
|
280
|
+
//*********************************//
|
|
281
|
+
|
|
282
|
+
// @dev Determines the NFT auction contract address.
|
|
283
|
+
get fun get_nft_auction_address(nftAddress: Address, seller: Address): Address {
|
|
284
|
+
let nftAuctionInit: StateInit = self._nft_auction_init(nftAddress, seller);
|
|
285
|
+
return contractAddress(nftAuctionInit);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// @dev Generates a hash value based on the seller and NFT address.
|
|
289
|
+
get fun get_hash_seller_nft_address(seller: Address, nftAddress: Address): Int {
|
|
290
|
+
return beginCell().storeAddress(seller).storeAddress(nftAddress).endCell().asSlice().hash();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// @dev Checks if the auction transfer for a given hash is valid.
|
|
294
|
+
get fun get_is_auction_transfer_check(hashSellerNftAddress: Int): Int {
|
|
295
|
+
if(self.auctionTransferCheck.get(hashSellerNftAddress) == null) {
|
|
296
|
+
return 0;
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
return 1;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file provides traits for NFT collections which follows the TEP-0062 standard.
|
|
3
|
+
https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
struct CollectionData {
|
|
8
|
+
next_item_index: Int; // collection should issue NFT with sequential indexes starting from 1. -1 value implies non-sequential collections.
|
|
9
|
+
collection_content: Cell; // collection content format should comply with TEP64
|
|
10
|
+
owner_address: Address; // collection owner address, zero address if no owner
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
trait NFTCollectionStandard {
|
|
14
|
+
virtual const NFT_COLLECTION_STANDARD_METADATA: String = "meta.json";
|
|
15
|
+
|
|
16
|
+
next_item_index: Int;
|
|
17
|
+
collection_content: Cell;
|
|
18
|
+
owner_address: Address;
|
|
19
|
+
|
|
20
|
+
//********************************************//
|
|
21
|
+
// Internal functions //
|
|
22
|
+
//********************************************//
|
|
23
|
+
|
|
24
|
+
// @dev _get_nft_item_state_init calculates init code for NFT item contract by item index
|
|
25
|
+
// @note one MUST override this function to provide custom NFT item initCode
|
|
26
|
+
abstract inline fun _get_nft_item_state_init(index: Int): StateInit;
|
|
27
|
+
|
|
28
|
+
// @dev _get_nft_content gets the serial number of the NFT item of this collection and the individual content of this NFT item.
|
|
29
|
+
// returns the full content of the NFT item in format that complies with standard TEP-64.
|
|
30
|
+
// @note one MUST override this function to provide full NFT item content
|
|
31
|
+
virtual inline fun _get_nft_content(index: Int, individual_content: Cell): Cell {
|
|
32
|
+
let builder: StringBuilder = beginString();
|
|
33
|
+
let idvStr: String = individual_content.asSlice().asString();
|
|
34
|
+
builder.append(idvStr);
|
|
35
|
+
return builder.toCell();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// @dev get_collection_data returns nft collection data
|
|
39
|
+
virtual inline fun _get_collection_data(): CollectionData {
|
|
40
|
+
let builder: StringBuilder = beginString();
|
|
41
|
+
let urlPrefix: String = self.collection_content.asSlice().asString();
|
|
42
|
+
builder.append(urlPrefix);
|
|
43
|
+
builder.append(self.NFT_COLLECTION_STANDARD_METADATA);
|
|
44
|
+
return CollectionData {
|
|
45
|
+
next_item_index: self.next_item_index,
|
|
46
|
+
collection_content: builder.toCell(),
|
|
47
|
+
owner_address: self.owner_address
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
//*********************************//
|
|
52
|
+
// Getters //
|
|
53
|
+
//*********************************//
|
|
54
|
+
|
|
55
|
+
get fun get_collection_data(): CollectionData {
|
|
56
|
+
return self._get_collection_data();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// @dev get_nft_address_by_index gets the serial number of NFT item of this collection and returns its address
|
|
60
|
+
get fun get_nft_address_by_index(index: Int): Address {
|
|
61
|
+
let initCode: StateInit = self._get_nft_item_state_init(index);
|
|
62
|
+
return contractAddress(initCode);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// @dev get_nft_content calls _get_nft_content and returns the full content of the NFT item in format that complies with standard TEP-64.
|
|
66
|
+
get fun get_nft_content(index: Int, individual_content: Cell): Cell {
|
|
67
|
+
return self._get_nft_content(index, individual_content);
|
|
68
|
+
}
|
|
69
|
+
}
|