@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
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Address, Cell, toNano } from '@ton/core';
|
|
2
|
+
import { TonClient } from '@ton/ton';
|
|
3
|
+
import { ProjectBackendService } from './backend-service';
|
|
4
|
+
import type { AddressLike, Coins, CollectionCreationData, CollectionInfo, CreateCollectionResult, MintNftInCollectionResult, NftData, SimpleNftNetwork, SimpleNftOptions } from './types';
|
|
5
|
+
import type { ProjectCollectionData, ProjectItemData, ProjectServerResult } from './backend-types';
|
|
6
|
+
export { Address, Cell, toNano };
|
|
7
|
+
export type { AddressLike, Coins, CollectionCreationData, CollectionInfo, CreateCollectionResult, MintNftInCollectionResult, NftData, SimpleNftNetwork, SimpleNftOptions, };
|
|
8
|
+
export type { Attribute, CollectionFileData, DeploymentStatus, ItemFileData, ProjectCollectionData, ProjectItemData, ProjectServerResult, ProjectServerStatus, } from './backend-types';
|
|
9
|
+
export { ProjectBackendService } from './backend-service';
|
|
10
|
+
export type { CollectionData, CollectionMasterData, CollectionMasterDataV2, CollectionMintParams, GetNftData, RoyaltyParams, } from './contracts/tact_SimpleNftMaster';
|
|
11
|
+
export declare class SimpleNft {
|
|
12
|
+
readonly hostname: string;
|
|
13
|
+
readonly network: SimpleNftNetwork;
|
|
14
|
+
readonly client: TonClient;
|
|
15
|
+
readonly backend: ProjectBackendService;
|
|
16
|
+
private readonly mnemonic?;
|
|
17
|
+
private walletState?;
|
|
18
|
+
readonly getters: {
|
|
19
|
+
isDeploy: (contract: Address) => Promise<boolean>;
|
|
20
|
+
parseAddress: (address: AddressLike) => string | null;
|
|
21
|
+
blockchain: {
|
|
22
|
+
collectionData: (collection: Address) => Promise<CollectionInfo>;
|
|
23
|
+
collectiondata: (collection: Address) => Promise<CollectionInfo>;
|
|
24
|
+
nftData: (address: Address) => Promise<NftData>;
|
|
25
|
+
};
|
|
26
|
+
server: {
|
|
27
|
+
collectionData: (collection: AddressLike) => Promise<ProjectServerResult<ProjectCollectionData>>;
|
|
28
|
+
collectiondata: (collection: AddressLike) => Promise<ProjectServerResult<ProjectCollectionData>>;
|
|
29
|
+
itemData: (address: AddressLike) => Promise<ProjectServerResult<ProjectItemData>>;
|
|
30
|
+
itemdata: (address: AddressLike) => Promise<ProjectServerResult<ProjectItemData>>;
|
|
31
|
+
nftData: (address: AddressLike) => Promise<ProjectServerResult<ProjectItemData>>;
|
|
32
|
+
};
|
|
33
|
+
collectionData: (collection: Address) => Promise<CollectionInfo>;
|
|
34
|
+
collectiondata: (collection: Address) => Promise<CollectionInfo>;
|
|
35
|
+
nftData: (address: Address) => Promise<NftData>;
|
|
36
|
+
};
|
|
37
|
+
readonly transactions: {
|
|
38
|
+
createCollection: (data: CollectionCreationData) => Promise<CreateCollectionResult>;
|
|
39
|
+
mintNftInCollection: (collection: Address) => Promise<MintNftInCollectionResult>;
|
|
40
|
+
};
|
|
41
|
+
constructor(options?: SimpleNftOptions);
|
|
42
|
+
version(): string;
|
|
43
|
+
private wallet;
|
|
44
|
+
private isDeploy;
|
|
45
|
+
private parseAddress;
|
|
46
|
+
private blockchainCollectionData;
|
|
47
|
+
private getCollectionMasterData;
|
|
48
|
+
private getSampleNftContent;
|
|
49
|
+
private blockchainNftData;
|
|
50
|
+
private serverCollectionData;
|
|
51
|
+
private serverItemData;
|
|
52
|
+
private skippedServerData;
|
|
53
|
+
private deploymentStatus;
|
|
54
|
+
private createCollection;
|
|
55
|
+
private mintNftInCollection;
|
|
56
|
+
}
|
|
57
|
+
export default SimpleNft;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SimpleNft = exports.ProjectBackendService = exports.toNano = exports.Cell = exports.Address = void 0;
|
|
4
|
+
const core_1 = require("@ton/core");
|
|
5
|
+
Object.defineProperty(exports, "Address", { enumerable: true, get: function () { return core_1.Address; } });
|
|
6
|
+
Object.defineProperty(exports, "Cell", { enumerable: true, get: function () { return core_1.Cell; } });
|
|
7
|
+
Object.defineProperty(exports, "toNano", { enumerable: true, get: function () { return core_1.toNano; } });
|
|
8
|
+
const crypto_1 = require("@ton/crypto");
|
|
9
|
+
const ton_1 = require("@ton/ton");
|
|
10
|
+
const tact_SimpleNftMaster_1 = require("./contracts/tact_SimpleNftMaster");
|
|
11
|
+
const tact_SimpleNftCollectionV2_1 = require("./contracts/tact_SimpleNftCollectionV2");
|
|
12
|
+
const tact_NftItem_1 = require("./contracts/tact_NftItem");
|
|
13
|
+
const backend_service_1 = require("./backend-service");
|
|
14
|
+
const content_1 = require("./content");
|
|
15
|
+
var backend_service_2 = require("./backend-service");
|
|
16
|
+
Object.defineProperty(exports, "ProjectBackendService", { enumerable: true, get: function () { return backend_service_2.ProjectBackendService; } });
|
|
17
|
+
const SDK_VERSION = '0.1.0';
|
|
18
|
+
const DEFAULT_HOSTNAME = 'https://api.simplenft.io';
|
|
19
|
+
const DEFAULT_NETWORK = 'mainnet';
|
|
20
|
+
function endpointFor(network) {
|
|
21
|
+
return `https://${network === 'testnet' ? 'testnet.' : ''}toncenter.com/api/v2/jsonRPC`;
|
|
22
|
+
}
|
|
23
|
+
function normalizeMnemonic(mnemonic) {
|
|
24
|
+
if (!mnemonic) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(mnemonic)) {
|
|
28
|
+
return mnemonic;
|
|
29
|
+
}
|
|
30
|
+
return mnemonic.trim().split(/\s+/).filter(Boolean);
|
|
31
|
+
}
|
|
32
|
+
function parseAddressOrThrow(address) {
|
|
33
|
+
return typeof address === 'string' ? core_1.Address.parse(address) : address;
|
|
34
|
+
}
|
|
35
|
+
function parseBigint(value, fallback) {
|
|
36
|
+
if (value === undefined) {
|
|
37
|
+
return fallback;
|
|
38
|
+
}
|
|
39
|
+
return BigInt(value);
|
|
40
|
+
}
|
|
41
|
+
function parseSbt(value) {
|
|
42
|
+
if (typeof value === 'boolean') {
|
|
43
|
+
return value ? 1n : 0n;
|
|
44
|
+
}
|
|
45
|
+
return parseBigint(value, 0n);
|
|
46
|
+
}
|
|
47
|
+
function parseCoins(value, fallback) {
|
|
48
|
+
return value === undefined ? (0, core_1.toNano)(fallback) : (0, core_1.toNano)(value);
|
|
49
|
+
}
|
|
50
|
+
async function waitForDeploy(client, address, attempts, intervalMs) {
|
|
51
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
52
|
+
if (await client.isContractDeployed(address)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
class SimpleNft {
|
|
60
|
+
constructor(options = {}) {
|
|
61
|
+
this.getters = {
|
|
62
|
+
isDeploy: async (contract) => this.isDeploy(contract),
|
|
63
|
+
parseAddress: (address) => this.parseAddress(address),
|
|
64
|
+
blockchain: {
|
|
65
|
+
collectionData: async (collection) => this.blockchainCollectionData(collection),
|
|
66
|
+
collectiondata: async (collection) => this.blockchainCollectionData(collection),
|
|
67
|
+
nftData: async (address) => this.blockchainNftData(address),
|
|
68
|
+
},
|
|
69
|
+
server: {
|
|
70
|
+
collectionData: async (collection) => this.serverCollectionData(collection),
|
|
71
|
+
collectiondata: async (collection) => this.serverCollectionData(collection),
|
|
72
|
+
itemData: async (address) => this.serverItemData(address),
|
|
73
|
+
itemdata: async (address) => this.serverItemData(address),
|
|
74
|
+
nftData: async (address) => this.serverItemData(address),
|
|
75
|
+
},
|
|
76
|
+
collectionData: async (collection) => this.blockchainCollectionData(collection),
|
|
77
|
+
collectiondata: async (collection) => this.blockchainCollectionData(collection),
|
|
78
|
+
nftData: async (address) => this.blockchainNftData(address),
|
|
79
|
+
};
|
|
80
|
+
this.transactions = {
|
|
81
|
+
createCollection: async (data) => this.createCollection(data),
|
|
82
|
+
mintNftInCollection: async (collection) => this.mintNftInCollection(collection),
|
|
83
|
+
};
|
|
84
|
+
this.hostname = options.hostname ?? DEFAULT_HOSTNAME;
|
|
85
|
+
this.network = options.network ?? DEFAULT_NETWORK;
|
|
86
|
+
this.mnemonic = normalizeMnemonic(options.mnemonic);
|
|
87
|
+
this.client = new ton_1.TonClient({ endpoint: endpointFor(this.network) });
|
|
88
|
+
this.backend = new backend_service_1.ProjectBackendService(this.hostname, options.serverPaths);
|
|
89
|
+
}
|
|
90
|
+
version() {
|
|
91
|
+
return SDK_VERSION;
|
|
92
|
+
}
|
|
93
|
+
async wallet() {
|
|
94
|
+
if (!this.mnemonic) {
|
|
95
|
+
throw new Error('Mnemonic is required for transaction methods');
|
|
96
|
+
}
|
|
97
|
+
if (!this.walletState) {
|
|
98
|
+
this.walletState = (async () => {
|
|
99
|
+
const keyPair = await (0, crypto_1.mnemonicToPrivateKey)(this.mnemonic);
|
|
100
|
+
const wallet = this.client.open(ton_1.WalletContractV5R1.create({
|
|
101
|
+
workchain: 0,
|
|
102
|
+
publicKey: keyPair.publicKey,
|
|
103
|
+
}));
|
|
104
|
+
return {
|
|
105
|
+
wallet,
|
|
106
|
+
secretKey: keyPair.secretKey,
|
|
107
|
+
};
|
|
108
|
+
})();
|
|
109
|
+
}
|
|
110
|
+
return this.walletState;
|
|
111
|
+
}
|
|
112
|
+
async isDeploy(contract) {
|
|
113
|
+
return this.client.isContractDeployed(contract);
|
|
114
|
+
}
|
|
115
|
+
parseAddress(address) {
|
|
116
|
+
try {
|
|
117
|
+
return parseAddressOrThrow(address).toString({ bounceable: true, urlSafe: true });
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async blockchainCollectionData(collection) {
|
|
124
|
+
const contract = this.client.open(tact_SimpleNftCollectionV2_1.SimpleNftCollectionV2.fromAddress(collection));
|
|
125
|
+
const collectionData = await contract.getGetCollectionData();
|
|
126
|
+
const masterData = await this.getCollectionMasterData(contract);
|
|
127
|
+
const sampleNftContent = await this.getSampleNftContent(contract);
|
|
128
|
+
return {
|
|
129
|
+
collectionData,
|
|
130
|
+
masterData,
|
|
131
|
+
sampleNftContent,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
async getCollectionMasterData(contract) {
|
|
135
|
+
try {
|
|
136
|
+
return await contract.getGetMasterDataV2();
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return contract.getGetMasterData();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
async getSampleNftContent(contract) {
|
|
143
|
+
try {
|
|
144
|
+
return await contract.getGetSampleNftContent();
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async blockchainNftData(address) {
|
|
151
|
+
const contract = this.client.open(tact_NftItem_1.NftItem.fromAddress(address));
|
|
152
|
+
return contract.getGetNftData();
|
|
153
|
+
}
|
|
154
|
+
async serverCollectionData(collection) {
|
|
155
|
+
return this.backend.collectionData(parseAddressOrThrow(collection));
|
|
156
|
+
}
|
|
157
|
+
async serverItemData(address) {
|
|
158
|
+
return this.backend.itemData(parseAddressOrThrow(address));
|
|
159
|
+
}
|
|
160
|
+
skippedServerData(address, resource) {
|
|
161
|
+
return {
|
|
162
|
+
ok: false,
|
|
163
|
+
status: 'request_failed',
|
|
164
|
+
url: `${this.hostname}/${resource}/${address?.toString() ?? 'unknown'}`,
|
|
165
|
+
error: 'Deployment was not confirmed; server data was not requested',
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
deploymentStatus(serverData, deployed) {
|
|
169
|
+
if (!deployed) {
|
|
170
|
+
return 'deployment_not_confirmed';
|
|
171
|
+
}
|
|
172
|
+
return serverData.ok ? 'deployed_with_server_data' : 'deployed_server_data_unavailable';
|
|
173
|
+
}
|
|
174
|
+
async createCollection(data) {
|
|
175
|
+
const { wallet, secretKey } = await this.wallet();
|
|
176
|
+
const master = this.client.open(tact_SimpleNftMaster_1.SimpleNftMaster.fromAddress(parseAddressOrThrow(data.master)));
|
|
177
|
+
if (!(await this.client.isContractDeployed(master.address))) {
|
|
178
|
+
throw new Error(`Master contract is not deployed: ${master.address.toString()}`);
|
|
179
|
+
}
|
|
180
|
+
const nextIndex = await master.getGetNextCollectionIndex();
|
|
181
|
+
const collectionAddress = await master.getGetCollectionAddressByIndex(nextIndex);
|
|
182
|
+
if (!collectionAddress) {
|
|
183
|
+
throw new Error(`Unable to resolve collection address for index ${nextIndex.toString()}`);
|
|
184
|
+
}
|
|
185
|
+
const owner = data.owner ? parseAddressOrThrow(data.owner) : wallet.address;
|
|
186
|
+
const royaltyDestination = data.royaltyParams?.destination ?? owner;
|
|
187
|
+
await master.send(wallet.sender(secretKey), {
|
|
188
|
+
value: parseCoins(data.value, '0.25'),
|
|
189
|
+
}, {
|
|
190
|
+
$$type: 'CollectionMintParams',
|
|
191
|
+
queryId: parseBigint(data.queryId, 0n),
|
|
192
|
+
owner_address: owner,
|
|
193
|
+
collection_content: (0, content_1.normalizeContent)(data.collectionContent),
|
|
194
|
+
nft_individual_content_url: (0, content_1.normalizeContent)(data.nftIndividualContent),
|
|
195
|
+
royalty_params: {
|
|
196
|
+
$$type: 'RoyaltyParams',
|
|
197
|
+
numerator: data.royaltyParams?.numerator ?? 100n,
|
|
198
|
+
denominator: data.royaltyParams?.denominator ?? 1000n,
|
|
199
|
+
destination: parseAddressOrThrow(royaltyDestination),
|
|
200
|
+
},
|
|
201
|
+
mint_limit: parseBigint(data.mintLimit, 100n),
|
|
202
|
+
mint_time_limit: parseBigint(data.mintTimeLimit, 1900000000n),
|
|
203
|
+
is_sbt: parseSbt(data.isSbt),
|
|
204
|
+
nft_price: parseCoins(data.nftPrice, '0.2'),
|
|
205
|
+
enable_profile: data.enableProfile ?? false,
|
|
206
|
+
enable_whitelist: data.enableWhitelist ?? false,
|
|
207
|
+
user_item_limit: parseBigint(data.userItemLimit, 0n),
|
|
208
|
+
});
|
|
209
|
+
const deployed = data.waitForDeploy ?? true
|
|
210
|
+
? await waitForDeploy(this.client, collectionAddress, data.waitAttempts ?? 30, data.waitIntervalMs ?? 3000)
|
|
211
|
+
: await this.client.isContractDeployed(collectionAddress);
|
|
212
|
+
const blockchainData = deployed ? await this.blockchainCollectionData(collectionAddress) : null;
|
|
213
|
+
const serverData = deployed
|
|
214
|
+
? await this.serverCollectionData(collectionAddress)
|
|
215
|
+
: this.skippedServerData(collectionAddress, 'api/collection');
|
|
216
|
+
return {
|
|
217
|
+
address: collectionAddress,
|
|
218
|
+
deployed,
|
|
219
|
+
status: this.deploymentStatus(serverData, deployed),
|
|
220
|
+
blockchainData,
|
|
221
|
+
serverData,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async mintNftInCollection(collection) {
|
|
225
|
+
const { wallet, secretKey } = await this.wallet();
|
|
226
|
+
const contract = this.client.open(tact_SimpleNftCollectionV2_1.SimpleNftCollectionV2.fromAddress(collection));
|
|
227
|
+
if (!(await this.client.isContractDeployed(collection))) {
|
|
228
|
+
throw new Error(`Collection contract is not deployed: ${collection.toString()}`);
|
|
229
|
+
}
|
|
230
|
+
const collectionData = await contract.getGetCollectionData();
|
|
231
|
+
const masterData = await this.getCollectionMasterData(contract);
|
|
232
|
+
const nftAddress = await contract.getGetNftAddressByIndex(collectionData.next_item_index);
|
|
233
|
+
await contract.send(wallet.sender(secretKey), {
|
|
234
|
+
value: masterData.price,
|
|
235
|
+
}, 'Mint');
|
|
236
|
+
const deployed = nftAddress
|
|
237
|
+
? await waitForDeploy(this.client, nftAddress, 30, 3000)
|
|
238
|
+
: false;
|
|
239
|
+
const blockchainData = deployed && nftAddress ? await this.blockchainNftData(nftAddress) : null;
|
|
240
|
+
const serverData = deployed && nftAddress
|
|
241
|
+
? await this.serverItemData(nftAddress)
|
|
242
|
+
: this.skippedServerData(nftAddress, 'api/item');
|
|
243
|
+
return {
|
|
244
|
+
address: nftAddress,
|
|
245
|
+
collection,
|
|
246
|
+
deployed,
|
|
247
|
+
status: this.deploymentStatus(serverData, deployed),
|
|
248
|
+
blockchainData,
|
|
249
|
+
serverData,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
exports.SimpleNft = SimpleNft;
|
|
254
|
+
exports.default = SimpleNft;
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Address, Cell } from '@ton/core';
|
|
2
|
+
import type { CollectionData, CollectionMasterData, CollectionMasterDataV2, GetNftData, RoyaltyParams } from './contracts/tact_SimpleNftMaster';
|
|
3
|
+
import type { DeploymentStatus, ProjectCollectionData, ProjectItemData, ProjectServerResult } from './backend-types';
|
|
4
|
+
import type { ProjectServerPaths } from './backend-service';
|
|
5
|
+
export type SimpleNftNetwork = 'mainnet' | 'testnet';
|
|
6
|
+
export type AddressLike = string | Address;
|
|
7
|
+
export type Coins = bigint | number | string;
|
|
8
|
+
export interface SimpleNftOptions {
|
|
9
|
+
hostname?: string;
|
|
10
|
+
mnemonic?: string | string[];
|
|
11
|
+
network?: SimpleNftNetwork;
|
|
12
|
+
serverPaths?: ProjectServerPaths;
|
|
13
|
+
}
|
|
14
|
+
export interface CollectionCreationData {
|
|
15
|
+
master: AddressLike;
|
|
16
|
+
owner?: AddressLike;
|
|
17
|
+
collectionContent: string | Cell;
|
|
18
|
+
nftIndividualContent: string | Cell;
|
|
19
|
+
royaltyParams?: Omit<RoyaltyParams, '$$type'> | RoyaltyParams;
|
|
20
|
+
mintLimit?: bigint | number | string;
|
|
21
|
+
mintTimeLimit?: bigint | number | string;
|
|
22
|
+
isSbt?: boolean | bigint | number | string;
|
|
23
|
+
nftPrice?: Coins;
|
|
24
|
+
enableProfile?: boolean;
|
|
25
|
+
enableWhitelist?: boolean;
|
|
26
|
+
userItemLimit?: bigint | number | string;
|
|
27
|
+
queryId?: bigint | number | string;
|
|
28
|
+
value?: Coins;
|
|
29
|
+
waitForDeploy?: boolean;
|
|
30
|
+
waitAttempts?: number;
|
|
31
|
+
waitIntervalMs?: number;
|
|
32
|
+
}
|
|
33
|
+
export interface CollectionInfo {
|
|
34
|
+
collectionData: CollectionData;
|
|
35
|
+
masterData: CollectionMasterData | CollectionMasterDataV2;
|
|
36
|
+
sampleNftContent: Cell | null;
|
|
37
|
+
}
|
|
38
|
+
export type NftData = GetNftData;
|
|
39
|
+
export interface CreateCollectionResult {
|
|
40
|
+
address: Address;
|
|
41
|
+
deployed: boolean;
|
|
42
|
+
status: DeploymentStatus;
|
|
43
|
+
blockchainData: CollectionInfo | null;
|
|
44
|
+
serverData: ProjectServerResult<ProjectCollectionData>;
|
|
45
|
+
}
|
|
46
|
+
export interface MintNftInCollectionResult {
|
|
47
|
+
address: Address | null;
|
|
48
|
+
collection: Address;
|
|
49
|
+
deployed: boolean;
|
|
50
|
+
status: DeploymentStatus;
|
|
51
|
+
blockchainData: NftData | null;
|
|
52
|
+
serverData: ProjectServerResult<ProjectItemData>;
|
|
53
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@simplenft/api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "SimpleNFT contracts SDK",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"require": "./dist/index.js",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist/index.js",
|
|
16
|
+
"dist/index.d.ts",
|
|
17
|
+
"dist/content.js",
|
|
18
|
+
"dist/content.d.ts",
|
|
19
|
+
"dist/types.js",
|
|
20
|
+
"dist/types.d.ts",
|
|
21
|
+
"dist/api.js",
|
|
22
|
+
"dist/api.d.ts",
|
|
23
|
+
"dist/backend-service.js",
|
|
24
|
+
"dist/backend-service.d.ts",
|
|
25
|
+
"dist/backend-types.js",
|
|
26
|
+
"dist/backend-types.d.ts",
|
|
27
|
+
"dist/contracts",
|
|
28
|
+
"src",
|
|
29
|
+
"contracts",
|
|
30
|
+
"blueprint.config.ts",
|
|
31
|
+
"README.md"
|
|
32
|
+
],
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc -p tsconfig.json",
|
|
35
|
+
"prepublishOnly": "npm run build"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@ton/core": "0.57.0",
|
|
39
|
+
"@ton/crypto": "3.3.0",
|
|
40
|
+
"@ton/ton": "15.0.0"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@ton/blueprint": "0.22.0",
|
|
44
|
+
"typescript": "5.5.3"
|
|
45
|
+
},
|
|
46
|
+
"keywords": [
|
|
47
|
+
"ton",
|
|
48
|
+
"nft",
|
|
49
|
+
"simplenft"
|
|
50
|
+
],
|
|
51
|
+
"license": "MIT",
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public"
|
|
54
|
+
},
|
|
55
|
+
"author": "Yuriy Berland"
|
|
56
|
+
}
|
package/src/api.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { ProjectServerResult } from './backend-types';
|
|
2
|
+
|
|
3
|
+
export class ProjectServerError extends Error {
|
|
4
|
+
public readonly url: string;
|
|
5
|
+
public readonly status?: number;
|
|
6
|
+
|
|
7
|
+
constructor(url: string, message: string, status?: number) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = 'ProjectServerError';
|
|
10
|
+
this.url = url;
|
|
11
|
+
this.status = status;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function normalizeHostname(hostname: string): string {
|
|
16
|
+
return hostname.replace(/\/+$/, '');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function buildUrl(hostname: string, path: string): string {
|
|
20
|
+
const normalizedHost = normalizeHostname(hostname);
|
|
21
|
+
const normalizedPath = path.startsWith('/') ? path : `/${path}`;
|
|
22
|
+
return `${normalizedHost}${normalizedPath}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function getJson<T>(url: string): Promise<T> {
|
|
26
|
+
const response = await fetch(url, {
|
|
27
|
+
method: 'GET',
|
|
28
|
+
headers: {
|
|
29
|
+
Accept: 'application/json',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (!response.ok) {
|
|
34
|
+
throw new ProjectServerError(url, response.statusText || `HTTP ${response.status}`, response.status);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return (await response.json()) as T;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function getJsonResult<T>(url: string): Promise<ProjectServerResult<T>> {
|
|
41
|
+
try {
|
|
42
|
+
const data = await getJson<T>(url);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
ok: true,
|
|
46
|
+
status: 'ok',
|
|
47
|
+
url,
|
|
48
|
+
data,
|
|
49
|
+
};
|
|
50
|
+
} catch (error) {
|
|
51
|
+
const serverError = error instanceof ProjectServerError ? error : undefined;
|
|
52
|
+
const httpStatus = serverError?.status;
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
status: httpStatus === 404 ? 'not_found' : 'request_failed',
|
|
57
|
+
url,
|
|
58
|
+
error: error instanceof Error ? error.message : String(error),
|
|
59
|
+
httpStatus,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Address } from '@ton/core';
|
|
2
|
+
import { buildUrl, getJsonResult } from './api';
|
|
3
|
+
import type { ProjectCollectionData, ProjectItemData, ProjectServerResult } from './backend-types';
|
|
4
|
+
|
|
5
|
+
export interface ProjectServerPaths {
|
|
6
|
+
collectionData?: (address: string) => string;
|
|
7
|
+
itemData?: (address: string) => string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const defaultPaths: Required<ProjectServerPaths> = {
|
|
11
|
+
collectionData: (address) => `/api/collection/${address}`,
|
|
12
|
+
itemData: (address) => `/api/item/${address}`,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function stringifyAddress(address: string | Address): string {
|
|
16
|
+
return typeof address === 'string' ? address : address.toString();
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class ProjectBackendService {
|
|
20
|
+
public readonly hostname: string;
|
|
21
|
+
private readonly paths: Required<ProjectServerPaths>;
|
|
22
|
+
|
|
23
|
+
constructor(hostname: string, paths: ProjectServerPaths = {}) {
|
|
24
|
+
this.hostname = hostname;
|
|
25
|
+
this.paths = {
|
|
26
|
+
...defaultPaths,
|
|
27
|
+
...paths,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public collectionData(address: string | Address): Promise<ProjectServerResult<ProjectCollectionData>> {
|
|
32
|
+
const stringAddress = stringifyAddress(address);
|
|
33
|
+
return getJsonResult<ProjectCollectionData>(buildUrl(this.hostname, this.paths.collectionData(stringAddress)));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public itemData(address: string | Address): Promise<ProjectServerResult<ProjectItemData>> {
|
|
37
|
+
const stringAddress = stringifyAddress(address);
|
|
38
|
+
return getJsonResult<ProjectItemData>(buildUrl(this.hostname, this.paths.itemData(stringAddress)));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export interface CollectionFileData {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
social_links: string[];
|
|
5
|
+
imageUrl: string;
|
|
6
|
+
backgroundUrl: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface Attribute {
|
|
10
|
+
trait_type: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ItemFileData {
|
|
15
|
+
name?: string;
|
|
16
|
+
description?: string;
|
|
17
|
+
marketplace?: string;
|
|
18
|
+
image: string;
|
|
19
|
+
attributes: Attribute[];
|
|
20
|
+
content_type?: string;
|
|
21
|
+
content_url?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ProjectCollectionData {
|
|
25
|
+
owner: string;
|
|
26
|
+
address: string;
|
|
27
|
+
mint_limit: number;
|
|
28
|
+
price: number;
|
|
29
|
+
next_item_index?: number;
|
|
30
|
+
next_item_address?: string;
|
|
31
|
+
data_url: string;
|
|
32
|
+
item_data_url?: string;
|
|
33
|
+
is_sbt?: boolean;
|
|
34
|
+
mint_time_limit?: number;
|
|
35
|
+
is_hidden_by_owner?: boolean;
|
|
36
|
+
enable_whitelist?: boolean | null;
|
|
37
|
+
user_item_limit?: number;
|
|
38
|
+
stars?: number;
|
|
39
|
+
volume?: number;
|
|
40
|
+
experience?: number;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ProjectItemData {
|
|
44
|
+
address: string;
|
|
45
|
+
collection: string;
|
|
46
|
+
data_url: string;
|
|
47
|
+
index_in_collection: string;
|
|
48
|
+
minter?: string;
|
|
49
|
+
metadata?: ItemFileData;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type ProjectServerStatus = 'ok' | 'not_found' | 'request_failed' | 'invalid_response';
|
|
53
|
+
|
|
54
|
+
export type ProjectServerResult<T> =
|
|
55
|
+
| {
|
|
56
|
+
ok: true;
|
|
57
|
+
status: 'ok';
|
|
58
|
+
url: string;
|
|
59
|
+
data: T;
|
|
60
|
+
}
|
|
61
|
+
| {
|
|
62
|
+
ok: false;
|
|
63
|
+
status: Exclude<ProjectServerStatus, 'ok'>;
|
|
64
|
+
url: string;
|
|
65
|
+
error: string;
|
|
66
|
+
httpStatus?: number;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type DeploymentStatus =
|
|
70
|
+
| 'deployed_with_server_data'
|
|
71
|
+
| 'deployed_server_data_unavailable'
|
|
72
|
+
| 'deployment_not_confirmed';
|
package/src/content.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { beginCell, Cell } from '@ton/core';
|
|
2
|
+
|
|
3
|
+
const OFFCHAIN_CONTENT_PREFIX = 0x01;
|
|
4
|
+
|
|
5
|
+
function bufferToChunks(buffer: Buffer, chunkSize: number): Buffer[] {
|
|
6
|
+
const chunks: Buffer[] = [];
|
|
7
|
+
let remaining = buffer;
|
|
8
|
+
|
|
9
|
+
while (remaining.byteLength > 0) {
|
|
10
|
+
chunks.push(remaining.subarray(0, chunkSize));
|
|
11
|
+
remaining = remaining.subarray(chunkSize);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return chunks;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function encodeOffchainContent(content: string): Cell {
|
|
18
|
+
const data = Buffer.concat([Buffer.from([OFFCHAIN_CONTENT_PREFIX]), Buffer.from(content)]);
|
|
19
|
+
const chunks = bufferToChunks(data, 127);
|
|
20
|
+
|
|
21
|
+
if (chunks.length === 0) {
|
|
22
|
+
return beginCell().endCell();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
let current = beginCell().storeBuffer(chunks[chunks.length - 1]).endCell();
|
|
26
|
+
|
|
27
|
+
for (let i = chunks.length - 2; i >= 0; i -= 1) {
|
|
28
|
+
current = beginCell().storeBuffer(chunks[i]).storeRef(current).endCell();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return current;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function normalizeContent(content: string | Cell): Cell {
|
|
35
|
+
return typeof content === 'string' ? encodeOffchainContent(content) : content;
|
|
36
|
+
}
|