@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,304 @@
|
|
|
1
|
+
import "@stdlib/deploy";
|
|
2
|
+
import "./message.tact";
|
|
3
|
+
import "./nft_item.tact";
|
|
4
|
+
import "./sbt_item.tact";
|
|
5
|
+
import "./buyer_profile.tact";
|
|
6
|
+
|
|
7
|
+
const nftBalanceConst: Int = ton("0.05");
|
|
8
|
+
const minPriceNoProfile: Int = ton("0.039");
|
|
9
|
+
const whitelistUpdatePrice: Int = ton("0.009");
|
|
10
|
+
const massUpdateMasterFee: Int = ton("0.01");
|
|
11
|
+
const singleTransactionReserve: Int = ton("0.005");
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
contract SimpleNftCollectionV2 with Deployable {
|
|
15
|
+
|
|
16
|
+
next_item_index: Int as uint32 = 0;
|
|
17
|
+
collection_index: Int as uint32 = 0;
|
|
18
|
+
owner_address: Address?;
|
|
19
|
+
master_address: Address;
|
|
20
|
+
royalty_params: RoyaltyParams?;
|
|
21
|
+
collection_content: Cell?;
|
|
22
|
+
individual_content_url: Cell?;
|
|
23
|
+
mint_limit: Int as uint32 = 0;
|
|
24
|
+
price: Int = 0;
|
|
25
|
+
is_setup: Bool;
|
|
26
|
+
is_sbt: Int = 0;
|
|
27
|
+
mint_time_limit: Int as uint32 = 0;
|
|
28
|
+
enable_profile: Bool = false;
|
|
29
|
+
user_item_limit: Int as uint8 = 0;
|
|
30
|
+
enable_whitelist: Bool = false;
|
|
31
|
+
|
|
32
|
+
init(
|
|
33
|
+
master_address: Address,
|
|
34
|
+
collection_index: Int){
|
|
35
|
+
require(sender() == master_address, "Not from master");
|
|
36
|
+
self.collection_index = collection_index;
|
|
37
|
+
self.master_address = master_address;
|
|
38
|
+
self.is_setup = false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
receive(msg: CollectionSetupParams) {
|
|
42
|
+
require(sender() == self.master_address, "Not from master");
|
|
43
|
+
require(self.is_setup == false, "Collection settings is already setup");
|
|
44
|
+
self.price = msg.nft_price;
|
|
45
|
+
self.mint_limit = msg.mint_limit;
|
|
46
|
+
self.owner_address = msg.owner_address; // msg.owner_address;
|
|
47
|
+
self.royalty_params = msg.royalty_params;
|
|
48
|
+
self.collection_content = msg.collection_content;
|
|
49
|
+
self.individual_content_url = msg.nft_individual_content_url;
|
|
50
|
+
self.is_setup = true;
|
|
51
|
+
self.is_sbt = msg.is_sbt;
|
|
52
|
+
self.enable_profile = msg.enable_profile;
|
|
53
|
+
self.user_item_limit = msg.user_item_limit;
|
|
54
|
+
self.mint_time_limit = msg.mint_time_limit;
|
|
55
|
+
self.enable_whitelist = msg.enable_whitelist;
|
|
56
|
+
}
|
|
57
|
+
// Profile
|
|
58
|
+
get fun buyer_profile_address(owner: Address): Address{
|
|
59
|
+
return contractAddress(initOf BuyerProfile(myAddress(), owner));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
receive("RequestWhitelist") {
|
|
63
|
+
require(self.enable_profile == true, "Profile not nessesary for this collection");
|
|
64
|
+
let init: StateInit = initOf BuyerProfile(myAddress(), sender());
|
|
65
|
+
send(SendParameters{
|
|
66
|
+
to: contractAddress(init),
|
|
67
|
+
body: SetupCollectionData{
|
|
68
|
+
collection_owner: self.owner_address,
|
|
69
|
+
collection_item_price: self.price,
|
|
70
|
+
enable_whitelist: self.enable_whitelist,
|
|
71
|
+
user_item_limit: self.user_item_limit
|
|
72
|
+
}.toCell(),
|
|
73
|
+
value: ton("0.02"),
|
|
74
|
+
mode: SendIgnoreErrors,
|
|
75
|
+
code: init.code,
|
|
76
|
+
data: init.data
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Classic for a free mint
|
|
81
|
+
receive("Mint"){
|
|
82
|
+
let ctx: Context = context(); // get sender Info
|
|
83
|
+
require(ctx.value >= self.price, "NFT creation underpriced");
|
|
84
|
+
require(self.enable_profile == false, "Another method for profiles");
|
|
85
|
+
let msgValue: Int = ctx.value;
|
|
86
|
+
let tonBalanceBeforeMsg: Int = myBalance() - msgValue;
|
|
87
|
+
let storageFee: Int = minTonsForStorage - min(tonBalanceBeforeMsg, minTonsForStorage);
|
|
88
|
+
msgValue = msgValue - (storageFee + gasConsumption);
|
|
89
|
+
|
|
90
|
+
self.mint(ctx.sender, msgValue);
|
|
91
|
+
|
|
92
|
+
emit(LogEventMintRecord{ minter: sender(), item_id: self.next_item_index, generate_number: nativeRandom() }.toCell());
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
receive(msg: Withdraw) {
|
|
96
|
+
require(sender() == self.owner_address, "Not from owner");
|
|
97
|
+
send(SendParameters{
|
|
98
|
+
to: msg.to,
|
|
99
|
+
value: myBalance() - masterMinStorage,
|
|
100
|
+
bounce: false,
|
|
101
|
+
mode: SendIgnoreErrors
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Mint with whitelist support
|
|
106
|
+
receive(msg: MintTo) {
|
|
107
|
+
let ownerProfile = self.get_user_profile_init(msg.owner);
|
|
108
|
+
require(sender() == contractAddress(ownerProfile), "From profile only");
|
|
109
|
+
let ctx: Context = context();
|
|
110
|
+
let msgValue: Int = ctx.value;
|
|
111
|
+
let tonBalanceBeforeMsg: Int = myBalance() - msgValue;
|
|
112
|
+
let storageFee: Int = minTonsForStorage - min(tonBalanceBeforeMsg, minTonsForStorage);
|
|
113
|
+
msgValue = msgValue - (storageFee + gasConsumption);
|
|
114
|
+
|
|
115
|
+
self.mint(msg.owner, msgValue);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ===== Private Methods ===== //
|
|
119
|
+
|
|
120
|
+
fun mint(sender: Address, msgValue: Int) {
|
|
121
|
+
require(self.next_item_index >= 0, "non-sequential NFTs");
|
|
122
|
+
require(self.is_setup == true, "Collection settings is not setup");
|
|
123
|
+
if (self.mint_limit > 0) {
|
|
124
|
+
require(self.next_item_index <= self.mint_limit, "Mint limit reached");
|
|
125
|
+
}
|
|
126
|
+
if (self.mint_time_limit > 0) {
|
|
127
|
+
require(now() <= self.mint_time_limit, "Mint time limit reached");
|
|
128
|
+
}
|
|
129
|
+
let nft_init: StateInit = self.getNftItemInit(self.next_item_index);
|
|
130
|
+
let noFeeConst = self.enable_profile ? nftBalanceConst : minPriceNoProfile;
|
|
131
|
+
let feeValue = msgValue > noFeeConst ? msgValue - noFeeConst : 0;
|
|
132
|
+
send(SendParameters{
|
|
133
|
+
to: contractAddress(nft_init),
|
|
134
|
+
value: msgValue - feeValue,
|
|
135
|
+
bounce: false,
|
|
136
|
+
mode: SendIgnoreErrors,
|
|
137
|
+
body: Transfer {
|
|
138
|
+
query_id: 0,
|
|
139
|
+
new_owner: sender,
|
|
140
|
+
response_destination: self.owner_address,
|
|
141
|
+
custom_payload: self.individual_content_url,
|
|
142
|
+
forward_amount: 0,
|
|
143
|
+
forward_payload: emptySlice()
|
|
144
|
+
}.toCell(),
|
|
145
|
+
code: nft_init.code,
|
|
146
|
+
data: nft_init.data
|
|
147
|
+
});
|
|
148
|
+
if (feeValue > 0) {
|
|
149
|
+
send(SendParameters{
|
|
150
|
+
to: self.master_address,
|
|
151
|
+
value: (feeValue * 3) / 100,
|
|
152
|
+
bounce: false,
|
|
153
|
+
mode: SendIgnoreErrors
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// let balanceRemaining: Int = myBalance();
|
|
157
|
+
send(SendParameters{
|
|
158
|
+
to: self.owner_address!!,
|
|
159
|
+
value: (feeValue * 97) / 100, // (feeValue * 97) / 100,
|
|
160
|
+
bounce: false,
|
|
161
|
+
mode: SendIgnoreErrors
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
dump(self.next_item_index); // Reference at: https://tact-by-example.org/03-emit
|
|
166
|
+
self.next_item_index = self.next_item_index + 1;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// -------------- Profile operations ----------------------- //
|
|
170
|
+
get fun get_buyer_profile_address (address: Address): Address {
|
|
171
|
+
let profile = self.get_user_profile_init(address);
|
|
172
|
+
return contractAddress(profile);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
fun update_white_list(address: Address, add: Bool, value: Int) {
|
|
176
|
+
let profile = self.get_user_profile_init(address);
|
|
177
|
+
send(SendParameters{
|
|
178
|
+
to: contractAddress(profile),
|
|
179
|
+
body: AddToWhiteList {
|
|
180
|
+
add: add
|
|
181
|
+
}.toCell(),
|
|
182
|
+
value: value,
|
|
183
|
+
mode: SendIgnoreErrors
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
receive(msg: GetRoyaltyParams) {
|
|
188
|
+
let ctx: Context = context(); // get sender Info
|
|
189
|
+
send(SendParameters{
|
|
190
|
+
to: ctx.sender,
|
|
191
|
+
value: 0,
|
|
192
|
+
mode: 64,
|
|
193
|
+
bounce: false,
|
|
194
|
+
body: ReportRoyaltyParams {
|
|
195
|
+
query_id: msg.query_id,
|
|
196
|
+
numerator: (self.royalty_params!!).numerator,
|
|
197
|
+
denominator: (self.royalty_params!!).denominator,
|
|
198
|
+
destination: (self.royalty_params!!).destination
|
|
199
|
+
}.toCell()
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
receive(msg: UpdateWhiteList) {
|
|
204
|
+
require(sender() == self.owner_address, "Not a collection owner");
|
|
205
|
+
let ctx: Context = context();
|
|
206
|
+
self.update_white_list(msg.user, msg.whitelist, whitelistUpdatePrice); // ctx.value - singleTransactionReserve
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
receive(msg: MassUpdateWhiteList) {
|
|
210
|
+
require(sender() == self.owner_address, "Not a collection owner");
|
|
211
|
+
let cell = msg.addresses;
|
|
212
|
+
let slice = cell.beginParse();
|
|
213
|
+
|
|
214
|
+
while (true) {
|
|
215
|
+
let address = slice.loadAddress();
|
|
216
|
+
self.update_white_list(address, msg.add, msg.spendPerAddress);
|
|
217
|
+
|
|
218
|
+
if (slice.refs() > 0) {
|
|
219
|
+
slice = slice.loadRef().beginParse();
|
|
220
|
+
} else {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
send(SendParameters{
|
|
226
|
+
to: self.master_address,
|
|
227
|
+
value: massUpdateMasterFee,
|
|
228
|
+
bounce: false,
|
|
229
|
+
mode: SendIgnoreErrors
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
get fun get_master_data_v2(): CollectionMasterDataV2 {
|
|
234
|
+
return CollectionMasterDataV2{
|
|
235
|
+
master: self.master_address,
|
|
236
|
+
mint_limit: self.mint_limit,
|
|
237
|
+
mint_time_limit: self.mint_time_limit,
|
|
238
|
+
is_sbt: self.is_sbt,
|
|
239
|
+
price: self.price,
|
|
240
|
+
enable_whitelist: self.enable_whitelist,
|
|
241
|
+
index_in_collection: self.collection_index,
|
|
242
|
+
user_item_limit: self.user_item_limit
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
get fun get_master_data(): CollectionMasterData {
|
|
247
|
+
return CollectionMasterData{
|
|
248
|
+
master: self.master_address,
|
|
249
|
+
mint_limit: self.mint_limit,
|
|
250
|
+
mint_time_limit: self.mint_time_limit,
|
|
251
|
+
is_sbt: self.is_sbt,
|
|
252
|
+
price: self.price,
|
|
253
|
+
index_in_collection: self.collection_index
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// ------------------ Get Function ------------------ //
|
|
258
|
+
get fun get_collection_data(): CollectionData {
|
|
259
|
+
let b: StringBuilder = beginString();
|
|
260
|
+
let collectionDataString: String = (self.collection_content!!).asSlice().asString();
|
|
261
|
+
b.append(collectionDataString);
|
|
262
|
+
return CollectionData{
|
|
263
|
+
next_item_index: self.next_item_index,
|
|
264
|
+
collection_content: b.toCell(),
|
|
265
|
+
owner_address: self.owner_address!!
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
get fun get_nft_address_by_index(item_index: Int): Address?{
|
|
270
|
+
let initCode: StateInit = self.getNftItemInit(item_index);
|
|
271
|
+
return contractAddress(initCode);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
get fun getNftItemInit(item_index: Int): StateInit {
|
|
275
|
+
// return initOf NftItem(myAddress(), item_index);
|
|
276
|
+
if (self.is_sbt == 0) {
|
|
277
|
+
return initOf NftItem(myAddress(), item_index);
|
|
278
|
+
} else {
|
|
279
|
+
return initOf SbtItem(myAddress(), item_index);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
get fun get_user_profile_init(address: Address): StateInit {
|
|
284
|
+
return initOf BuyerProfile(myAddress(), address);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
get fun get_nft_content(index: Int, individual_content: Cell): Cell {
|
|
288
|
+
let b: StringBuilder = beginString();
|
|
289
|
+
let ic: String = individual_content.asSlice().asString();
|
|
290
|
+
b.append(ic);
|
|
291
|
+
return b.toCell();
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
get fun get_sample_nft_content (): Cell? {
|
|
295
|
+
let b: StringBuilder = beginString();
|
|
296
|
+
let ic: String = (self.individual_content_url!!).asSlice().asString();
|
|
297
|
+
b.append(ic);
|
|
298
|
+
return b.toCell();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
get fun royalty_params(): RoyaltyParams {
|
|
302
|
+
return self.royalty_params!!;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import "@stdlib/deploy";
|
|
2
|
+
import "./simple_nft_collection_v2";
|
|
3
|
+
import "./message.tact";
|
|
4
|
+
|
|
5
|
+
contract SimpleNftMaster with Deployable {
|
|
6
|
+
|
|
7
|
+
/*
|
|
8
|
+
Root contract of SimpleNFT project
|
|
9
|
+
Powered by Vorpal team
|
|
10
|
+
https://github.com/VORPALTEAM
|
|
11
|
+
|
|
12
|
+
Code by Yuriy Berland
|
|
13
|
+
https://github.com/YuriyBum
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
owner: Address;
|
|
17
|
+
next_collection_index: Int = 0;
|
|
18
|
+
collection_creation_price: Int as uint32 = 0;
|
|
19
|
+
|
|
20
|
+
init(
|
|
21
|
+
owner: Address,
|
|
22
|
+
creation_price: Int,
|
|
23
|
+
) {
|
|
24
|
+
self.owner = owner;
|
|
25
|
+
self.collection_creation_price = creation_price;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
receive(msg: CollectionMintParams) {
|
|
29
|
+
require(self.next_collection_index >= 0, "non-sequential Collections");
|
|
30
|
+
let ctx: Context = context();
|
|
31
|
+
require(ctx.value >= self.collection_creation_price, "Creation underpriced");
|
|
32
|
+
let minPriceRequired = msg.enable_profile ? nftBalanceConst : minPriceNoProfile;
|
|
33
|
+
require(msg.nft_price >= minPriceRequired, "Token price too low");
|
|
34
|
+
if (msg.mint_time_limit > 0) {
|
|
35
|
+
require(msg.mint_time_limit > now(), "Collection must have time to mint");
|
|
36
|
+
}
|
|
37
|
+
let collection_init: StateInit = self.getCollectionItemInit(self.next_collection_index);
|
|
38
|
+
|
|
39
|
+
send(SendParameters{
|
|
40
|
+
to: contractAddress(collection_init),
|
|
41
|
+
value: masterMinStorage,
|
|
42
|
+
bounce: false,
|
|
43
|
+
mode: SendIgnoreErrors,
|
|
44
|
+
body: CollectionSetupParams {
|
|
45
|
+
owner_address: msg.owner_address,
|
|
46
|
+
master_address: myAddress(),
|
|
47
|
+
collection_content: msg.collection_content,
|
|
48
|
+
nft_individual_content_url: msg.nft_individual_content_url,
|
|
49
|
+
royalty_params: msg.royalty_params,
|
|
50
|
+
mint_limit: msg.mint_limit,
|
|
51
|
+
nft_price: msg.nft_price,
|
|
52
|
+
mint_time_limit: msg.mint_time_limit,
|
|
53
|
+
is_sbt: msg.is_sbt,
|
|
54
|
+
enable_whitelist: msg.enable_whitelist,
|
|
55
|
+
enable_profile: msg.enable_profile,
|
|
56
|
+
user_item_limit: msg.user_item_limit
|
|
57
|
+
}.toCell(),
|
|
58
|
+
code: collection_init.code,
|
|
59
|
+
data: collection_init.data
|
|
60
|
+
});
|
|
61
|
+
self.next_collection_index = self.next_collection_index + 1;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
receive(msg: Withdraw) {
|
|
65
|
+
require(sender() == self.owner, "Not from owner");
|
|
66
|
+
send(SendParameters{
|
|
67
|
+
to: msg.to,
|
|
68
|
+
value: myBalance() - masterMinStorage,
|
|
69
|
+
bounce: false,
|
|
70
|
+
mode: SendIgnoreErrors
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
receive(msg: TransferOwner) {
|
|
75
|
+
require(sender() == self.owner, "Not from owner");
|
|
76
|
+
self.owner = msg.new_owner;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
receive() {}
|
|
80
|
+
|
|
81
|
+
get fun getCollectionItemInit(item_index: Int): StateInit {
|
|
82
|
+
return initOf SimpleNftCollectionV2(myAddress(), item_index);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get fun get_master_data (): MasterData {
|
|
86
|
+
return MasterData{
|
|
87
|
+
master: self.owner,
|
|
88
|
+
next_collection_index: self.next_collection_index,
|
|
89
|
+
collection_creation_price: self.collection_creation_price
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
get fun get_collection_address_by_index (collection_index: Int): Address? {
|
|
94
|
+
let initCode: StateInit = self.getCollectionItemInit(collection_index);
|
|
95
|
+
return contractAddress(initCode);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
get fun get_next_collection_index (): Int {
|
|
99
|
+
return self.next_collection_index;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
}
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ProjectServerResult } from './backend-types';
|
|
2
|
+
export declare class ProjectServerError extends Error {
|
|
3
|
+
readonly url: string;
|
|
4
|
+
readonly status?: number;
|
|
5
|
+
constructor(url: string, message: string, status?: number);
|
|
6
|
+
}
|
|
7
|
+
export declare function normalizeHostname(hostname: string): string;
|
|
8
|
+
export declare function buildUrl(hostname: string, path: string): string;
|
|
9
|
+
export declare function getJson<T>(url: string): Promise<T>;
|
|
10
|
+
export declare function getJsonResult<T>(url: string): Promise<ProjectServerResult<T>>;
|
package/dist/api.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProjectServerError = void 0;
|
|
4
|
+
exports.normalizeHostname = normalizeHostname;
|
|
5
|
+
exports.buildUrl = buildUrl;
|
|
6
|
+
exports.getJson = getJson;
|
|
7
|
+
exports.getJsonResult = getJsonResult;
|
|
8
|
+
class ProjectServerError extends Error {
|
|
9
|
+
constructor(url, message, status) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = 'ProjectServerError';
|
|
12
|
+
this.url = url;
|
|
13
|
+
this.status = status;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.ProjectServerError = ProjectServerError;
|
|
17
|
+
function normalizeHostname(hostname) {
|
|
18
|
+
return hostname.replace(/\/+$/, '');
|
|
19
|
+
}
|
|
20
|
+
function buildUrl(hostname, path) {
|
|
21
|
+
const normalizedHost = normalizeHostname(hostname);
|
|
22
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
23
|
+
return `${normalizedHost}${normalizedPath}`;
|
|
24
|
+
}
|
|
25
|
+
async function getJson(url) {
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
method: 'GET',
|
|
28
|
+
headers: {
|
|
29
|
+
Accept: 'application/json',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
throw new ProjectServerError(url, response.statusText || `HTTP ${response.status}`, response.status);
|
|
34
|
+
}
|
|
35
|
+
return (await response.json());
|
|
36
|
+
}
|
|
37
|
+
async function getJsonResult(url) {
|
|
38
|
+
try {
|
|
39
|
+
const data = await getJson(url);
|
|
40
|
+
return {
|
|
41
|
+
ok: true,
|
|
42
|
+
status: 'ok',
|
|
43
|
+
url,
|
|
44
|
+
data,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
const serverError = error instanceof ProjectServerError ? error : undefined;
|
|
49
|
+
const httpStatus = serverError?.status;
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
status: httpStatus === 404 ? 'not_found' : 'request_failed',
|
|
53
|
+
url,
|
|
54
|
+
error: error instanceof Error ? error.message : String(error),
|
|
55
|
+
httpStatus,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Address } from '@ton/core';
|
|
2
|
+
import type { ProjectCollectionData, ProjectItemData, ProjectServerResult } from './backend-types';
|
|
3
|
+
export interface ProjectServerPaths {
|
|
4
|
+
collectionData?: (address: string) => string;
|
|
5
|
+
itemData?: (address: string) => string;
|
|
6
|
+
}
|
|
7
|
+
export declare class ProjectBackendService {
|
|
8
|
+
readonly hostname: string;
|
|
9
|
+
private readonly paths;
|
|
10
|
+
constructor(hostname: string, paths?: ProjectServerPaths);
|
|
11
|
+
collectionData(address: string | Address): Promise<ProjectServerResult<ProjectCollectionData>>;
|
|
12
|
+
itemData(address: string | Address): Promise<ProjectServerResult<ProjectItemData>>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProjectBackendService = void 0;
|
|
4
|
+
const api_1 = require("./api");
|
|
5
|
+
const defaultPaths = {
|
|
6
|
+
collectionData: (address) => `/api/collection/${address}`,
|
|
7
|
+
itemData: (address) => `/api/item/${address}`,
|
|
8
|
+
};
|
|
9
|
+
function stringifyAddress(address) {
|
|
10
|
+
return typeof address === 'string' ? address : address.toString();
|
|
11
|
+
}
|
|
12
|
+
class ProjectBackendService {
|
|
13
|
+
constructor(hostname, paths = {}) {
|
|
14
|
+
this.hostname = hostname;
|
|
15
|
+
this.paths = {
|
|
16
|
+
...defaultPaths,
|
|
17
|
+
...paths,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
collectionData(address) {
|
|
21
|
+
const stringAddress = stringifyAddress(address);
|
|
22
|
+
return (0, api_1.getJsonResult)((0, api_1.buildUrl)(this.hostname, this.paths.collectionData(stringAddress)));
|
|
23
|
+
}
|
|
24
|
+
itemData(address) {
|
|
25
|
+
const stringAddress = stringifyAddress(address);
|
|
26
|
+
return (0, api_1.getJsonResult)((0, api_1.buildUrl)(this.hostname, this.paths.itemData(stringAddress)));
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
exports.ProjectBackendService = ProjectBackendService;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export interface CollectionFileData {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
social_links: string[];
|
|
5
|
+
imageUrl: string;
|
|
6
|
+
backgroundUrl: string;
|
|
7
|
+
}
|
|
8
|
+
export interface Attribute {
|
|
9
|
+
trait_type: string;
|
|
10
|
+
value: string;
|
|
11
|
+
}
|
|
12
|
+
export interface ItemFileData {
|
|
13
|
+
name?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
marketplace?: string;
|
|
16
|
+
image: string;
|
|
17
|
+
attributes: Attribute[];
|
|
18
|
+
content_type?: string;
|
|
19
|
+
content_url?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ProjectCollectionData {
|
|
22
|
+
owner: string;
|
|
23
|
+
address: string;
|
|
24
|
+
mint_limit: number;
|
|
25
|
+
price: number;
|
|
26
|
+
next_item_index?: number;
|
|
27
|
+
next_item_address?: string;
|
|
28
|
+
data_url: string;
|
|
29
|
+
item_data_url?: string;
|
|
30
|
+
is_sbt?: boolean;
|
|
31
|
+
mint_time_limit?: number;
|
|
32
|
+
is_hidden_by_owner?: boolean;
|
|
33
|
+
enable_whitelist?: boolean | null;
|
|
34
|
+
user_item_limit?: number;
|
|
35
|
+
stars?: number;
|
|
36
|
+
volume?: number;
|
|
37
|
+
experience?: number;
|
|
38
|
+
}
|
|
39
|
+
export interface ProjectItemData {
|
|
40
|
+
address: string;
|
|
41
|
+
collection: string;
|
|
42
|
+
data_url: string;
|
|
43
|
+
index_in_collection: string;
|
|
44
|
+
minter?: string;
|
|
45
|
+
metadata?: ItemFileData;
|
|
46
|
+
}
|
|
47
|
+
export type ProjectServerStatus = 'ok' | 'not_found' | 'request_failed' | 'invalid_response';
|
|
48
|
+
export type ProjectServerResult<T> = {
|
|
49
|
+
ok: true;
|
|
50
|
+
status: 'ok';
|
|
51
|
+
url: string;
|
|
52
|
+
data: T;
|
|
53
|
+
} | {
|
|
54
|
+
ok: false;
|
|
55
|
+
status: Exclude<ProjectServerStatus, 'ok'>;
|
|
56
|
+
url: string;
|
|
57
|
+
error: string;
|
|
58
|
+
httpStatus?: number;
|
|
59
|
+
};
|
|
60
|
+
export type DeploymentStatus = 'deployed_with_server_data' | 'deployed_server_data_unavailable' | 'deployment_not_confirmed';
|
package/dist/content.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encodeOffchainContent = encodeOffchainContent;
|
|
4
|
+
exports.normalizeContent = normalizeContent;
|
|
5
|
+
const core_1 = require("@ton/core");
|
|
6
|
+
const OFFCHAIN_CONTENT_PREFIX = 0x01;
|
|
7
|
+
function bufferToChunks(buffer, chunkSize) {
|
|
8
|
+
const chunks = [];
|
|
9
|
+
let remaining = buffer;
|
|
10
|
+
while (remaining.byteLength > 0) {
|
|
11
|
+
chunks.push(remaining.subarray(0, chunkSize));
|
|
12
|
+
remaining = remaining.subarray(chunkSize);
|
|
13
|
+
}
|
|
14
|
+
return chunks;
|
|
15
|
+
}
|
|
16
|
+
function encodeOffchainContent(content) {
|
|
17
|
+
const data = Buffer.concat([Buffer.from([OFFCHAIN_CONTENT_PREFIX]), Buffer.from(content)]);
|
|
18
|
+
const chunks = bufferToChunks(data, 127);
|
|
19
|
+
if (chunks.length === 0) {
|
|
20
|
+
return (0, core_1.beginCell)().endCell();
|
|
21
|
+
}
|
|
22
|
+
let current = (0, core_1.beginCell)().storeBuffer(chunks[chunks.length - 1]).endCell();
|
|
23
|
+
for (let i = chunks.length - 2; i >= 0; i -= 1) {
|
|
24
|
+
current = (0, core_1.beginCell)().storeBuffer(chunks[i]).storeRef(current).endCell();
|
|
25
|
+
}
|
|
26
|
+
return current;
|
|
27
|
+
}
|
|
28
|
+
function normalizeContent(content) {
|
|
29
|
+
return typeof content === 'string' ? encodeOffchainContent(content) : content;
|
|
30
|
+
}
|