@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,190 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file provides traits for NFT item which follows the TEP-0062 standard.
|
|
3
|
+
https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md
|
|
4
|
+
|
|
5
|
+
Reference:
|
|
6
|
+
[Official Implementation](https://github.com/ton-blockchain/token-contract/blob/991bdb4925653c51b0b53ab212c53143f71f5476/nft/nft-item.fc#L17)
|
|
7
|
+
[NFT Template](https://github.com/howardpen9/nft-template-in-tact/blob/tutorial/sources/contract.tact)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
message(0x5fcc3d14) Transfer {
|
|
11
|
+
query_id: Int as uint64; // arbitrary request number
|
|
12
|
+
new_owner: Address; // new owner of the NFT item
|
|
13
|
+
response_destination: Address; // address where to send a response with confirmation of a successful transfer and the rest of the incoming message coins if not empty address
|
|
14
|
+
custom_payload: Cell?; // optional custom data
|
|
15
|
+
forward_amount: Int as coins; // the amount of nanotons to be sent to the new owner
|
|
16
|
+
forward_payload: Slice as remaining; // optional custom data that should be sent to the new owner
|
|
17
|
+
}
|
|
18
|
+
message(0x05138d91) OwnershipAssigned {
|
|
19
|
+
query_id: Int as uint64;
|
|
20
|
+
prev_owner: Address;
|
|
21
|
+
forward_payload: Slice as remaining;
|
|
22
|
+
}
|
|
23
|
+
message(0xd53276db) Excesses {
|
|
24
|
+
query_id: Int as uint64;
|
|
25
|
+
}
|
|
26
|
+
message(0x2fcb26a2) GetStaticData {
|
|
27
|
+
query_id: Int as uint64;
|
|
28
|
+
}
|
|
29
|
+
message(0x8b771735) ReportStaticData {
|
|
30
|
+
query_id: Int as uint64;
|
|
31
|
+
index: Int as uint256;
|
|
32
|
+
collection: Address;
|
|
33
|
+
}
|
|
34
|
+
struct NftData {
|
|
35
|
+
is_initialized: Bool; // it should be `init`, but it is a reserved keyword in tact. if not zero, then this NFT is fully initialized and ready for interaction.
|
|
36
|
+
index: Int; // numerical index of this NFT in the collection. For collection-less NFT - arbitrary but constant value.
|
|
37
|
+
collection_address: Address; // address of the smart contract of the collection to which this NFT belongs. For collection-less NFT this parameter should be addr_none
|
|
38
|
+
owner_address: Address; // address of the current owner of this NFT
|
|
39
|
+
individual_content: Cell; // if NFT has collection - individual NFT content in any format; if NFT has no collection - NFT content in format that complies with standard TEP-64
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
trait NFTItemStandard {
|
|
43
|
+
virtual const minTonsForStorage: Int = ton("0.05");
|
|
44
|
+
virtual const gasConsumption: Int = ton("0.05");
|
|
45
|
+
collection_address: Address;
|
|
46
|
+
index: Int;
|
|
47
|
+
owner: Address;
|
|
48
|
+
individual_content: Cell;
|
|
49
|
+
is_initialized: Bool;
|
|
50
|
+
|
|
51
|
+
//********************************************//
|
|
52
|
+
// Messages //
|
|
53
|
+
//********************************************//
|
|
54
|
+
|
|
55
|
+
receive(msg: Transfer){
|
|
56
|
+
dump("Inside transfer");
|
|
57
|
+
let ctx: Context = context();
|
|
58
|
+
let remain: Int = self._transfer_estimate_rest_value(ctx);
|
|
59
|
+
self._transfer_validate(ctx, msg, remain);
|
|
60
|
+
if (self.is_initialized == false) {
|
|
61
|
+
dump("Inside uniniti");
|
|
62
|
+
self.mint(ctx, msg);
|
|
63
|
+
} else {
|
|
64
|
+
dump("Inside correct");
|
|
65
|
+
self.transfer(ctx, msg, remain)
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
receive(msg: GetStaticData){
|
|
70
|
+
self._report_static_data(msg);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//********************************************//
|
|
74
|
+
// Internal functions //
|
|
75
|
+
//********************************************//
|
|
76
|
+
|
|
77
|
+
// @dev _transfer_estimate_rest_value calculates the remain amount of nanotons that should be sent back to the old owner
|
|
78
|
+
inline fun _transfer_estimate_rest_value(ctx: Context): Int {
|
|
79
|
+
let remain: Int = ctx.value;
|
|
80
|
+
let tonBalanceBeforeMsg: Int = myBalance() - remain;
|
|
81
|
+
let storageFee: Int = self.minTonsForStorage - min(tonBalanceBeforeMsg, self.minTonsForStorage);
|
|
82
|
+
return remain - (storageFee + self.gasConsumption);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// @dev _transfer_validate checks if the request is valid (e.g. the sender is the current owner of the NFT item)
|
|
86
|
+
// throws error if the request is invalid
|
|
87
|
+
// @note one may override this function to implement custom validation logicTransferked
|
|
88
|
+
|
|
89
|
+
virtual inline fun _transfer_validate(ctx: Context, msg: Transfer, remain: Int) {
|
|
90
|
+
require(ctx.sender == self.owner || ctx.sender == self.collection_address,
|
|
91
|
+
"NFTItemStandard: Only the owner or collection can transfer the NFT item"
|
|
92
|
+
);
|
|
93
|
+
dump("validated succ");
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// @dev mint usually performs the minting of the NFT item
|
|
97
|
+
// @note one may override this function to implement custom minting logic
|
|
98
|
+
|
|
99
|
+
virtual inline fun mint(ctx: Context, msg: Transfer) {
|
|
100
|
+
require(ctx.sender == self.collection_address,
|
|
101
|
+
"NFTItemStandard: Only the collection can initialize the NFT item"
|
|
102
|
+
);
|
|
103
|
+
self.is_initialized = true;
|
|
104
|
+
self.owner = msg.new_owner;
|
|
105
|
+
send(SendParameters{
|
|
106
|
+
to: msg.response_destination,
|
|
107
|
+
value: 0,
|
|
108
|
+
mode: SendIgnoreErrors + SendRemainingValue,
|
|
109
|
+
body: Excesses{query_id: msg.query_id}.toCell()
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// @dev transfer performs the transfer of the NFT item
|
|
115
|
+
// if available, it will send OwnershipAssigned message to the new owner
|
|
116
|
+
// then, it will send Excesses message to the response_destination (old owner) if needed.
|
|
117
|
+
// @note one may override this function to implement custom transfer logic
|
|
118
|
+
|
|
119
|
+
virtual inline fun transfer(ctx: Context, msg: Transfer, remain: Int) {
|
|
120
|
+
self.owner = msg.new_owner;
|
|
121
|
+
if (msg.forward_amount > 0) {
|
|
122
|
+
send(SendParameters{
|
|
123
|
+
to: msg.new_owner,
|
|
124
|
+
value: msg.forward_amount,
|
|
125
|
+
mode: SendIgnoreErrors,
|
|
126
|
+
bounce: false,
|
|
127
|
+
body: OwnershipAssigned{
|
|
128
|
+
query_id: msg.query_id,
|
|
129
|
+
prev_owner: ctx.sender,
|
|
130
|
+
forward_payload: msg.forward_payload
|
|
131
|
+
}.toCell()
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
remain = remain - ctx.readForwardFee();
|
|
136
|
+
if (msg.response_destination != newAddress(0, 0) && remain > msg.forward_amount) {
|
|
137
|
+
send(SendParameters{
|
|
138
|
+
to: msg.response_destination,
|
|
139
|
+
value: remain - msg.forward_amount,
|
|
140
|
+
mode: SendPayGasSeparately,
|
|
141
|
+
body: Excesses{query_id: msg.query_id}.toCell()
|
|
142
|
+
}
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// @dev _report_static_data sends a response with the static data of the NFT item
|
|
148
|
+
|
|
149
|
+
virtual inline fun _report_static_data(msg: GetStaticData) {
|
|
150
|
+
let ctx: Context = context();
|
|
151
|
+
send(SendParameters{
|
|
152
|
+
to: ctx.sender,
|
|
153
|
+
value: 0,
|
|
154
|
+
mode: SendRemainingValue,
|
|
155
|
+
bounce: false,
|
|
156
|
+
body: ReportStaticData{
|
|
157
|
+
query_id: msg.query_id,
|
|
158
|
+
index: self.index,
|
|
159
|
+
collection: self.collection_address
|
|
160
|
+
}.toCell()
|
|
161
|
+
}
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// @dev _get_nft_data returns the NFT data in the format that complies with TEP-64
|
|
166
|
+
|
|
167
|
+
virtual inline fun _get_nft_data(): NftData {
|
|
168
|
+
let builder: StringBuilder = beginString();
|
|
169
|
+
let collectionData: String = self.individual_content.asSlice().asString();
|
|
170
|
+
builder.append(collectionData);
|
|
171
|
+
builder.append(self.index.toString());
|
|
172
|
+
builder.append(".json");
|
|
173
|
+
return
|
|
174
|
+
NftData{
|
|
175
|
+
is_initialized: self.is_initialized,
|
|
176
|
+
index: self.index,
|
|
177
|
+
collection_address: self.collection_address,
|
|
178
|
+
owner_address: self.owner,
|
|
179
|
+
individual_content: builder.toCell()
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
//*********************************//
|
|
184
|
+
// Getters //
|
|
185
|
+
//*********************************//
|
|
186
|
+
|
|
187
|
+
get fun get_nft_data(): NftData {
|
|
188
|
+
return self._get_nft_data();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This file provides traits for NFT royalty standard is TEP-0066
|
|
3
|
+
https://github.com/ton-blockchain/TEPs/blob/master/text/0066-nft-royalty-standard.md
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
struct RoyaltyParams {
|
|
7
|
+
numerator: Int as uint16; // numerator to calculate royalty percentage
|
|
8
|
+
denominator: Int as uint16; // denominator to calculate royalty percentage
|
|
9
|
+
destination: Address; // Address to receive royalty
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
message(0x693d3950) GetRoyaltyParams {
|
|
13
|
+
query_id: Int as uint64; // arbitrary request number for sending ReportRoyaltyParams back with send mode 64
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
message(0xa8cb00ad) ReportRoyaltyParams {
|
|
17
|
+
query_id: Int as uint64; // corresponding request number
|
|
18
|
+
numerator: Int as uint16; // numerator to calculate royalty percentage, maximum 65535
|
|
19
|
+
denominator: Int as uint16; // denominator to calculate royalty percentage, maximum 65535
|
|
20
|
+
destination: Address; // Address to receive royalty
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
trait NFTRoyaltyStandard {
|
|
24
|
+
royalty_params: RoyaltyParams;
|
|
25
|
+
owner_address: Address;
|
|
26
|
+
|
|
27
|
+
//********************************************//
|
|
28
|
+
// Messages //
|
|
29
|
+
//********************************************//
|
|
30
|
+
|
|
31
|
+
receive(msg: GetRoyaltyParams) {
|
|
32
|
+
self.report_royalty_params(msg);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//********************************************//
|
|
36
|
+
// Internal functions //
|
|
37
|
+
//********************************************//
|
|
38
|
+
|
|
39
|
+
virtual inline fun report_royalty_params(msg: GetRoyaltyParams) {
|
|
40
|
+
let ctx: Context = context();
|
|
41
|
+
send(SendParameters{
|
|
42
|
+
to: ctx.sender,
|
|
43
|
+
value: 0,
|
|
44
|
+
mode: SendRemainingValue,
|
|
45
|
+
bounce: false,
|
|
46
|
+
body: ReportRoyaltyParams {
|
|
47
|
+
query_id: msg.query_id,
|
|
48
|
+
numerator: self.royalty_params.numerator,
|
|
49
|
+
denominator: self.royalty_params.denominator,
|
|
50
|
+
destination: self.owner_address
|
|
51
|
+
}.toCell()
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
//*********************************//
|
|
57
|
+
// Getters //
|
|
58
|
+
//*********************************//
|
|
59
|
+
|
|
60
|
+
get fun royalty_params(): RoyaltyParams {
|
|
61
|
+
return self.royalty_params;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
|
|
2
|
+
const codeUnauthorized: Int = 51003;
|
|
3
|
+
|
|
4
|
+
// SetStaticTax is the message that used to set the static tax fee.
|
|
5
|
+
message(0x1509a420) SetStaticTax {
|
|
6
|
+
staticTax: Int as coins;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
trait Taxable {
|
|
10
|
+
owner: Address;
|
|
11
|
+
staticTax: Int;
|
|
12
|
+
|
|
13
|
+
receive(msg: SetStaticTax) {
|
|
14
|
+
self.receiveSetStaticTax(msg);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
get fun staticTax(): Int {
|
|
18
|
+
return self.staticTax;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
virtual fun receiveSetStaticTax(msg: SetStaticTax) {
|
|
22
|
+
nativeThrowUnless(codeUnauthorized, sender() == self.owner);
|
|
23
|
+
|
|
24
|
+
self.staticTax = msg.staticTax;
|
|
25
|
+
let answer = beginString()
|
|
26
|
+
.concat("set static tax fee to ")
|
|
27
|
+
.concat(msg.staticTax.toString())
|
|
28
|
+
.toString();
|
|
29
|
+
self.reply(answer.asComment());
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
trait Estimatable {
|
|
2
|
+
virtual const gasConsumption: Int = ton("0.05");
|
|
3
|
+
virtual const minTonsForStorage: Int = ton("0.05");
|
|
4
|
+
|
|
5
|
+
virtual fun estimate_rest_value(ctx: Context): Int {
|
|
6
|
+
let restValue: Int = ctx.value;
|
|
7
|
+
let tonBalanceBeforeMsg: Int = myBalance() - restValue;
|
|
8
|
+
let storageFee: Int = self.minTonsForStorage - min(tonBalanceBeforeMsg, self.minTonsForStorage);
|
|
9
|
+
return restValue - (storageFee + self.gasConsumption);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
trait Lockable {
|
|
2
|
+
is_locked: Bool;
|
|
3
|
+
|
|
4
|
+
virtual inline fun lock() {
|
|
5
|
+
self.is_locked = true;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
virtual inline fun unlock() {
|
|
9
|
+
self.is_locked = false;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
virtual inline fun ensureLocked() {
|
|
13
|
+
require(self.is_locked, "Lockable: Object is not locked");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
virtual inline fun ensureUnlocked() {
|
|
17
|
+
require(!self.is_locked, "Lockable: Object is locked");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get fun is_locked(): Bool {
|
|
21
|
+
return self.is_locked;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import "@stdlib/deploy";
|
|
2
|
+
import "./message.tact";
|
|
3
|
+
|
|
4
|
+
contract SbtItem {
|
|
5
|
+
collection_address: Address;
|
|
6
|
+
item_index: Int as uint32;
|
|
7
|
+
is_initialized: Bool;
|
|
8
|
+
|
|
9
|
+
owner: Address?;
|
|
10
|
+
individual_content: Cell?;
|
|
11
|
+
|
|
12
|
+
init(collection_address: Address, item_index: Int){
|
|
13
|
+
require(sender() == collection_address, "not from collection");
|
|
14
|
+
self.collection_address = collection_address;
|
|
15
|
+
self.item_index = item_index;
|
|
16
|
+
self.is_initialized = false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
receive(msg: Transfer){
|
|
20
|
+
let ctx: Context = context(); // Reference: https://docs.tact-lang.org/language/ref/common#context
|
|
21
|
+
let msgValue: Int = self.msgValue(ctx.value);
|
|
22
|
+
require(self.is_initialized == false, "Sbt transfer for init only");
|
|
23
|
+
require(ctx.sender == self.collection_address, "initialized tx need from collection");
|
|
24
|
+
self.is_initialized = true;
|
|
25
|
+
self.owner = msg.new_owner;
|
|
26
|
+
self.individual_content = msg.custom_payload;
|
|
27
|
+
send(SendParameters{
|
|
28
|
+
to: msg.response_destination!!,
|
|
29
|
+
value: msgValue,
|
|
30
|
+
mode: SendPayGasSeparately,
|
|
31
|
+
body: Excesses { query_id: msg.query_id }.toCell()
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
receive(msg: GetStaticData){
|
|
36
|
+
let ctx: Context = context();
|
|
37
|
+
send(SendParameters {
|
|
38
|
+
to: ctx.sender,
|
|
39
|
+
value: 0,
|
|
40
|
+
mode: 64, // (return msg amount except gas fees)
|
|
41
|
+
bounce: true,
|
|
42
|
+
body: ReportStaticData{
|
|
43
|
+
query_id: msg.query_id,
|
|
44
|
+
index_id: self.item_index,
|
|
45
|
+
collection: self.collection_address
|
|
46
|
+
}.toCell()
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fun msgValue(value: Int): Int {
|
|
51
|
+
let tonBalanceBeforeMsg: Int = myBalance() - value;
|
|
52
|
+
let storageFee: Int = minTonsForStorage - min(tonBalanceBeforeMsg, minTonsForStorage);
|
|
53
|
+
return value - (storageFee + gasConsumption);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// --------- Get Function --------- //
|
|
57
|
+
get fun get_nft_data(): GetNftData {
|
|
58
|
+
let b: StringBuilder = beginString();
|
|
59
|
+
let collectionData: String = (self.individual_content!!).asSlice().asString();
|
|
60
|
+
b.append(collectionData);
|
|
61
|
+
|
|
62
|
+
return GetNftData {
|
|
63
|
+
is_initialized: self.is_initialized,
|
|
64
|
+
index: self.item_index,
|
|
65
|
+
collection_address: self.collection_address,
|
|
66
|
+
owner_address: self.owner!!,
|
|
67
|
+
individual_content: b.toCell()
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import "@stdlib/deploy";
|
|
2
|
+
import "./message.tact";
|
|
3
|
+
import "./nft_item.tact";
|
|
4
|
+
import "./sbt_item.tact";
|
|
5
|
+
|
|
6
|
+
const nftBalanceConst: Int = ton("0.1");
|
|
7
|
+
|
|
8
|
+
contract SimpleNftCollection with Deployable {
|
|
9
|
+
|
|
10
|
+
next_item_index: Int as uint32 = 0;
|
|
11
|
+
collection_index: Int as uint32 = 0;
|
|
12
|
+
owner_address: Address?;
|
|
13
|
+
master_address: Address;
|
|
14
|
+
royalty_params: RoyaltyParams?;
|
|
15
|
+
collection_content: Cell?;
|
|
16
|
+
individual_content_url: Cell?;
|
|
17
|
+
mint_limit: Int = 0;
|
|
18
|
+
price: Int = 0;
|
|
19
|
+
is_setup: Bool;
|
|
20
|
+
is_sbt: Int = 0;
|
|
21
|
+
mint_time_limit: Int = 0;
|
|
22
|
+
|
|
23
|
+
init(
|
|
24
|
+
master_address: Address,
|
|
25
|
+
collection_index: Int){
|
|
26
|
+
require(sender() == master_address, "Not from master");
|
|
27
|
+
self.collection_index = collection_index;
|
|
28
|
+
self.master_address = master_address;
|
|
29
|
+
self.is_setup = false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
receive(msg: CollectionSetupParams) {
|
|
33
|
+
require(sender() == self.master_address, "Not from master");
|
|
34
|
+
require(self.is_setup == false, "Collection settings is already setup");
|
|
35
|
+
self.price = msg.nft_price;
|
|
36
|
+
self.mint_limit = msg.mint_limit;
|
|
37
|
+
self.owner_address = msg.owner_address; // msg.owner_address;
|
|
38
|
+
self.royalty_params = msg.royalty_params;
|
|
39
|
+
self.collection_content = msg.collection_content;
|
|
40
|
+
self.individual_content_url = msg.nft_individual_content_url;
|
|
41
|
+
self.is_setup = true;
|
|
42
|
+
self.is_sbt = msg.is_sbt;
|
|
43
|
+
self.mint_time_limit = msg.mint_time_limit;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
receive("Mint"){
|
|
47
|
+
require(self.is_setup == true, "Collection settings is not setup");
|
|
48
|
+
let ctx: Context = context(); // get sender Info
|
|
49
|
+
if (self.mint_limit > 0) {
|
|
50
|
+
require(self.next_item_index <= self.mint_limit, "Mint limit reached");
|
|
51
|
+
}
|
|
52
|
+
if (self.mint_time_limit > 0) {
|
|
53
|
+
require(now() <= self.mint_time_limit, "Mint time limit reached");
|
|
54
|
+
}
|
|
55
|
+
require(ctx.value >= self.price, "NFT creation underpriced");
|
|
56
|
+
let msgValue: Int = ctx.value;
|
|
57
|
+
let tonBalanceBeforeMsg: Int = myBalance() - msgValue;
|
|
58
|
+
let storageFee: Int = minTonsForStorage - min(tonBalanceBeforeMsg, minTonsForStorage);
|
|
59
|
+
msgValue = msgValue - (storageFee + gasConsumption);
|
|
60
|
+
|
|
61
|
+
self.mint(ctx.sender, msgValue);
|
|
62
|
+
|
|
63
|
+
emit(LogEventMintRecord{ minter: sender(), item_id: self.next_item_index, generate_number: nativeRandom() }.toCell());
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ===== Private Methods ===== //
|
|
67
|
+
|
|
68
|
+
fun mint(sender: Address, msgValue: Int) {
|
|
69
|
+
require(self.next_item_index >= 0, "non-sequential NFTs");
|
|
70
|
+
let nft_init: StateInit = self.getNftItemInit(self.next_item_index);
|
|
71
|
+
let feeValue = msgValue - nftBalanceConst;
|
|
72
|
+
send(SendParameters{
|
|
73
|
+
to: contractAddress(nft_init),
|
|
74
|
+
value: nftBalanceConst,
|
|
75
|
+
bounce: false,
|
|
76
|
+
mode: SendIgnoreErrors,
|
|
77
|
+
body: Transfer {
|
|
78
|
+
query_id: 0,
|
|
79
|
+
new_owner: sender,
|
|
80
|
+
response_destination: self.owner_address,
|
|
81
|
+
custom_payload: self.individual_content_url,
|
|
82
|
+
forward_amount: 0,
|
|
83
|
+
forward_payload: emptySlice()
|
|
84
|
+
}.toCell(),
|
|
85
|
+
code: nft_init.code,
|
|
86
|
+
data: nft_init.data
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
send(SendParameters{
|
|
90
|
+
to: self.master_address,
|
|
91
|
+
value: (feeValue * 3) / 100,
|
|
92
|
+
bounce: false,
|
|
93
|
+
mode: SendIgnoreErrors
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// let balanceRemaining: Int = myBalance();
|
|
97
|
+
send(SendParameters{
|
|
98
|
+
to: self.owner_address!!,
|
|
99
|
+
value: (feeValue * 97) / 100, // (feeValue * 97) / 100,
|
|
100
|
+
bounce: false,
|
|
101
|
+
mode: SendIgnoreErrors
|
|
102
|
+
});
|
|
103
|
+
dump(self.next_item_index); // Reference at: https://tact-by-example.org/03-emit
|
|
104
|
+
self.next_item_index = self.next_item_index + 1;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
receive(msg: GetRoyaltyParams) {
|
|
108
|
+
let ctx: Context = context(); // get sender Info
|
|
109
|
+
send(SendParameters{
|
|
110
|
+
to: ctx.sender,
|
|
111
|
+
value: 0,
|
|
112
|
+
mode: 64,
|
|
113
|
+
bounce: false,
|
|
114
|
+
body: ReportRoyaltyParams {
|
|
115
|
+
query_id: msg.query_id,
|
|
116
|
+
numerator: (self.royalty_params!!).numerator,
|
|
117
|
+
denominator: (self.royalty_params!!).denominator,
|
|
118
|
+
destination: (self.royalty_params!!).destination
|
|
119
|
+
}.toCell()
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get fun get_master_data(): CollectionMasterData {
|
|
124
|
+
return CollectionMasterData{
|
|
125
|
+
master: self.master_address,
|
|
126
|
+
mint_limit: self.mint_limit,
|
|
127
|
+
mint_time_limit: self.mint_time_limit,
|
|
128
|
+
is_sbt: self.is_sbt,
|
|
129
|
+
price: self.price,
|
|
130
|
+
index_in_collection: self.collection_index
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ------------------ Get Function ------------------ //
|
|
135
|
+
get fun get_collection_data(): CollectionData {
|
|
136
|
+
let b: StringBuilder = beginString();
|
|
137
|
+
let collectionDataString: String = (self.collection_content!!).asSlice().asString();
|
|
138
|
+
b.append(collectionDataString);
|
|
139
|
+
return CollectionData{
|
|
140
|
+
next_item_index: self.next_item_index,
|
|
141
|
+
collection_content: b.toCell(),
|
|
142
|
+
owner_address: self.owner_address!!
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
get fun get_nft_address_by_index(item_index: Int): Address?{
|
|
147
|
+
let initCode: StateInit = self.getNftItemInit(item_index);
|
|
148
|
+
return contractAddress(initCode);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
get fun getNftItemInit(item_index: Int): StateInit {
|
|
152
|
+
// return initOf NftItem(myAddress(), item_index);
|
|
153
|
+
if (self.is_sbt == 0) {
|
|
154
|
+
return initOf NftItem(myAddress(), item_index);
|
|
155
|
+
} else {
|
|
156
|
+
return initOf SbtItem(myAddress(), item_index);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
get fun get_nft_content(index: Int, individual_content: Cell): Cell {
|
|
161
|
+
let b: StringBuilder = beginString();
|
|
162
|
+
let ic: String = individual_content.asSlice().asString();
|
|
163
|
+
b.append(ic);
|
|
164
|
+
return b.toCell();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
get fun get_sample_nft_content (): Cell? {
|
|
168
|
+
let b: StringBuilder = beginString();
|
|
169
|
+
let ic: String = (self.individual_content_url!!).asSlice().asString();
|
|
170
|
+
b.append(ic);
|
|
171
|
+
return b.toCell();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
get fun royalty_params(): RoyaltyParams {
|
|
175
|
+
return self.royalty_params!!;
|
|
176
|
+
}
|
|
177
|
+
}
|