@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.
Files changed (56) hide show
  1. package/README.md +30 -0
  2. package/blueprint.config.ts +13 -0
  3. package/contracts/buyer_profile.tact +73 -0
  4. package/contracts/imports/stdlib.fc +625 -0
  5. package/contracts/jetton_eq.tact +79 -0
  6. package/contracts/message.tact +189 -0
  7. package/contracts/nft_item.tact +98 -0
  8. package/contracts/nft_item_free.tact +98 -0
  9. package/contracts/packages/math/float.fc +95 -0
  10. package/contracts/packages/misc/distributor_messages.tact +154 -0
  11. package/contracts/packages/nap/errcodes.tact +5 -0
  12. package/contracts/packages/nap/messages.tact +34 -0
  13. package/contracts/packages/token/jetton/JettonMaster.tact +127 -0
  14. package/contracts/packages/token/jetton/JettonWallet.tact +248 -0
  15. package/contracts/packages/token/nft/AuctionErrorCode.tact +55 -0
  16. package/contracts/packages/token/nft/NFTAuction.tact +226 -0
  17. package/contracts/packages/token/nft/NFTAuctionMarket.tact +302 -0
  18. package/contracts/packages/token/nft/NFTCollection.tact +69 -0
  19. package/contracts/packages/token/nft/NFTItem.tact +190 -0
  20. package/contracts/packages/token/nft/extensions/NFTEditable.tact +3 -0
  21. package/contracts/packages/token/nft/extensions/NFTRoyalty.tact +63 -0
  22. package/contracts/packages/traits/taxable.tact +31 -0
  23. package/contracts/packages/utils/Estimatable.tact +11 -0
  24. package/contracts/packages/utils/Lockable.tact +23 -0
  25. package/contracts/sbt_item.tact +70 -0
  26. package/contracts/simple_nft_collection.tact +177 -0
  27. package/contracts/simple_nft_collection_v2.tact +304 -0
  28. package/contracts/simple_nft_master.tact +102 -0
  29. package/dist/api.d.ts +10 -0
  30. package/dist/api.js +58 -0
  31. package/dist/backend-service.d.ts +13 -0
  32. package/dist/backend-service.js +29 -0
  33. package/dist/backend-types.d.ts +60 -0
  34. package/dist/backend-types.js +2 -0
  35. package/dist/content.d.ts +3 -0
  36. package/dist/content.js +30 -0
  37. package/dist/contracts/tact_NftItem.d.ts +619 -0
  38. package/dist/contracts/tact_NftItem.js +2312 -0
  39. package/dist/contracts/tact_SimpleNftCollectionV2.d.ts +658 -0
  40. package/dist/contracts/tact_SimpleNftCollectionV2.js +2427 -0
  41. package/dist/contracts/tact_SimpleNftMaster.d.ts +624 -0
  42. package/dist/contracts/tact_SimpleNftMaster.js +2350 -0
  43. package/dist/index.d.ts +57 -0
  44. package/dist/index.js +254 -0
  45. package/dist/types.d.ts +53 -0
  46. package/dist/types.js +2 -0
  47. package/package.json +56 -0
  48. package/src/api.ts +62 -0
  49. package/src/backend-service.ts +40 -0
  50. package/src/backend-types.ts +72 -0
  51. package/src/content.ts +36 -0
  52. package/src/contracts/tact_NftItem.ts +2718 -0
  53. package/src/contracts/tact_SimpleNftCollectionV2.ts +2843 -0
  54. package/src/contracts/tact_SimpleNftMaster.ts +2759 -0
  55. package/src/index.ts +361 -0
  56. package/src/types.ts +62 -0
package/src/index.ts ADDED
@@ -0,0 +1,361 @@
1
+ import { Address, Cell, OpenedContract, toNano } from '@ton/core';
2
+ import { mnemonicToPrivateKey } from '@ton/crypto';
3
+ import { TonClient, WalletContractV5R1 } from '@ton/ton';
4
+ import { SimpleNftMaster } from './contracts/tact_SimpleNftMaster';
5
+ import { SimpleNftCollectionV2 } from './contracts/tact_SimpleNftCollectionV2';
6
+ import { NftItem } from './contracts/tact_NftItem';
7
+ import { ProjectBackendService } from './backend-service';
8
+ import { normalizeContent } from './content';
9
+ import type {
10
+ AddressLike,
11
+ Coins,
12
+ CollectionCreationData,
13
+ CollectionInfo,
14
+ CreateCollectionResult,
15
+ MintNftInCollectionResult,
16
+ NftData,
17
+ SimpleNftNetwork,
18
+ SimpleNftOptions,
19
+ } from './types';
20
+ import type { ProjectCollectionData, ProjectItemData, ProjectServerResult } from './backend-types';
21
+
22
+ export { Address, Cell, toNano };
23
+ export type {
24
+ AddressLike,
25
+ Coins,
26
+ CollectionCreationData,
27
+ CollectionInfo,
28
+ CreateCollectionResult,
29
+ MintNftInCollectionResult,
30
+ NftData,
31
+ SimpleNftNetwork,
32
+ SimpleNftOptions,
33
+ };
34
+ export type {
35
+ Attribute,
36
+ CollectionFileData,
37
+ DeploymentStatus,
38
+ ItemFileData,
39
+ ProjectCollectionData,
40
+ ProjectItemData,
41
+ ProjectServerResult,
42
+ ProjectServerStatus,
43
+ } from './backend-types';
44
+ export { ProjectBackendService } from './backend-service';
45
+ export type {
46
+ CollectionData,
47
+ CollectionMasterData,
48
+ CollectionMasterDataV2,
49
+ CollectionMintParams,
50
+ GetNftData,
51
+ RoyaltyParams,
52
+ } from './contracts/tact_SimpleNftMaster';
53
+
54
+ const SDK_VERSION = '0.1.0';
55
+ const DEFAULT_HOSTNAME = 'https://api.simplenft.io';
56
+ const DEFAULT_NETWORK: SimpleNftNetwork = 'mainnet';
57
+
58
+ type WalletState = {
59
+ wallet: OpenedContract<WalletContractV5R1>;
60
+ secretKey: Buffer;
61
+ };
62
+
63
+ function endpointFor(network: SimpleNftNetwork): string {
64
+ return `https://${network === 'testnet' ? 'testnet.' : ''}toncenter.com/api/v2/jsonRPC`;
65
+ }
66
+
67
+ function normalizeMnemonic(mnemonic?: string | string[]): string[] | undefined {
68
+ if (!mnemonic) {
69
+ return undefined;
70
+ }
71
+
72
+ if (Array.isArray(mnemonic)) {
73
+ return mnemonic;
74
+ }
75
+
76
+ return mnemonic.trim().split(/\s+/).filter(Boolean);
77
+ }
78
+
79
+ function parseAddressOrThrow(address: AddressLike): Address {
80
+ return typeof address === 'string' ? Address.parse(address) : address;
81
+ }
82
+
83
+ function parseBigint(value: bigint | number | string | undefined, fallback: bigint): bigint {
84
+ if (value === undefined) {
85
+ return fallback;
86
+ }
87
+
88
+ return BigInt(value);
89
+ }
90
+
91
+ function parseSbt(value: CollectionCreationData['isSbt']): bigint {
92
+ if (typeof value === 'boolean') {
93
+ return value ? 1n : 0n;
94
+ }
95
+
96
+ return parseBigint(value, 0n);
97
+ }
98
+
99
+ function parseCoins(value: Coins | undefined, fallback: string): bigint {
100
+ return value === undefined ? toNano(fallback) : toNano(value);
101
+ }
102
+
103
+ async function waitForDeploy(client: TonClient, address: Address, attempts: number, intervalMs: number): Promise<boolean> {
104
+ for (let attempt = 0; attempt < attempts; attempt += 1) {
105
+ if (await client.isContractDeployed(address)) {
106
+ return true;
107
+ }
108
+
109
+ await new Promise((resolve) => setTimeout(resolve, intervalMs));
110
+ }
111
+
112
+ return false;
113
+ }
114
+
115
+ export class SimpleNft {
116
+ public readonly hostname: string;
117
+ public readonly network: SimpleNftNetwork;
118
+ public readonly client: TonClient;
119
+ public readonly backend: ProjectBackendService;
120
+
121
+ private readonly mnemonic?: string[];
122
+ private walletState?: Promise<WalletState>;
123
+
124
+ public readonly getters = {
125
+ isDeploy: async (contract: Address): Promise<boolean> => this.isDeploy(contract),
126
+ parseAddress: (address: AddressLike): string | null => this.parseAddress(address),
127
+ blockchain: {
128
+ collectionData: async (collection: Address): Promise<CollectionInfo> => this.blockchainCollectionData(collection),
129
+ collectiondata: async (collection: Address): Promise<CollectionInfo> => this.blockchainCollectionData(collection),
130
+ nftData: async (address: Address): Promise<NftData> => this.blockchainNftData(address),
131
+ },
132
+ server: {
133
+ collectionData: async (collection: AddressLike): Promise<ProjectServerResult<ProjectCollectionData>> =>
134
+ this.serverCollectionData(collection),
135
+ collectiondata: async (collection: AddressLike): Promise<ProjectServerResult<ProjectCollectionData>> =>
136
+ this.serverCollectionData(collection),
137
+ itemData: async (address: AddressLike): Promise<ProjectServerResult<ProjectItemData>> => this.serverItemData(address),
138
+ itemdata: async (address: AddressLike): Promise<ProjectServerResult<ProjectItemData>> => this.serverItemData(address),
139
+ nftData: async (address: AddressLike): Promise<ProjectServerResult<ProjectItemData>> => this.serverItemData(address),
140
+ },
141
+ collectionData: async (collection: Address): Promise<CollectionInfo> => this.blockchainCollectionData(collection),
142
+ collectiondata: async (collection: Address): Promise<CollectionInfo> => this.blockchainCollectionData(collection),
143
+ nftData: async (address: Address): Promise<NftData> => this.blockchainNftData(address),
144
+ };
145
+
146
+ public readonly transactions = {
147
+ createCollection: async (data: CollectionCreationData): Promise<CreateCollectionResult> => this.createCollection(data),
148
+ mintNftInCollection: async (collection: Address): Promise<MintNftInCollectionResult> => this.mintNftInCollection(collection),
149
+ };
150
+
151
+ constructor(options: SimpleNftOptions = {}) {
152
+ this.hostname = options.hostname ?? DEFAULT_HOSTNAME;
153
+ this.network = options.network ?? DEFAULT_NETWORK;
154
+ this.mnemonic = normalizeMnemonic(options.mnemonic);
155
+ this.client = new TonClient({ endpoint: endpointFor(this.network) });
156
+ this.backend = new ProjectBackendService(this.hostname, options.serverPaths);
157
+ }
158
+
159
+ public version(): string {
160
+ return SDK_VERSION;
161
+ }
162
+
163
+ private async wallet(): Promise<WalletState> {
164
+ if (!this.mnemonic) {
165
+ throw new Error('Mnemonic is required for transaction methods');
166
+ }
167
+
168
+ if (!this.walletState) {
169
+ this.walletState = (async () => {
170
+ const keyPair = await mnemonicToPrivateKey(this.mnemonic as string[]);
171
+ const wallet = this.client.open(
172
+ WalletContractV5R1.create({
173
+ workchain: 0,
174
+ publicKey: keyPair.publicKey,
175
+ }),
176
+ );
177
+
178
+ return {
179
+ wallet,
180
+ secretKey: keyPair.secretKey,
181
+ };
182
+ })();
183
+ }
184
+
185
+ return this.walletState;
186
+ }
187
+
188
+ private async isDeploy(contract: Address): Promise<boolean> {
189
+ return this.client.isContractDeployed(contract);
190
+ }
191
+
192
+ private parseAddress(address: AddressLike): string | null {
193
+ try {
194
+ return parseAddressOrThrow(address).toString({ bounceable: true, urlSafe: true });
195
+ } catch {
196
+ return null;
197
+ }
198
+ }
199
+
200
+ private async blockchainCollectionData(collection: Address): Promise<CollectionInfo> {
201
+ const contract = this.client.open(SimpleNftCollectionV2.fromAddress(collection));
202
+ const collectionData = await contract.getGetCollectionData();
203
+ const masterData = await this.getCollectionMasterData(contract);
204
+ const sampleNftContent = await this.getSampleNftContent(contract);
205
+
206
+ return {
207
+ collectionData,
208
+ masterData,
209
+ sampleNftContent,
210
+ };
211
+ }
212
+
213
+ private async getCollectionMasterData(contract: OpenedContract<SimpleNftCollectionV2>): Promise<CollectionInfo['masterData']> {
214
+ try {
215
+ return await contract.getGetMasterDataV2();
216
+ } catch {
217
+ return contract.getGetMasterData();
218
+ }
219
+ }
220
+
221
+ private async getSampleNftContent(contract: OpenedContract<SimpleNftCollectionV2>): Promise<Cell | null> {
222
+ try {
223
+ return await contract.getGetSampleNftContent();
224
+ } catch {
225
+ return null;
226
+ }
227
+ }
228
+
229
+ private async blockchainNftData(address: Address): Promise<NftData> {
230
+ const contract = this.client.open(NftItem.fromAddress(address));
231
+ return contract.getGetNftData();
232
+ }
233
+
234
+ private async serverCollectionData(collection: AddressLike): Promise<ProjectServerResult<ProjectCollectionData>> {
235
+ return this.backend.collectionData(parseAddressOrThrow(collection));
236
+ }
237
+
238
+ private async serverItemData(address: AddressLike): Promise<ProjectServerResult<ProjectItemData>> {
239
+ return this.backend.itemData(parseAddressOrThrow(address));
240
+ }
241
+
242
+ private skippedServerData<T>(address: Address | null, resource: string): ProjectServerResult<T> {
243
+ return {
244
+ ok: false,
245
+ status: 'request_failed',
246
+ url: `${this.hostname}/${resource}/${address?.toString() ?? 'unknown'}`,
247
+ error: 'Deployment was not confirmed; server data was not requested',
248
+ };
249
+ }
250
+
251
+ private deploymentStatus(serverData: ProjectServerResult<unknown>, deployed: boolean): CreateCollectionResult['status'] {
252
+ if (!deployed) {
253
+ return 'deployment_not_confirmed';
254
+ }
255
+
256
+ return serverData.ok ? 'deployed_with_server_data' : 'deployed_server_data_unavailable';
257
+ }
258
+
259
+ private async createCollection(data: CollectionCreationData): Promise<CreateCollectionResult> {
260
+ const { wallet, secretKey } = await this.wallet();
261
+ const master = this.client.open(SimpleNftMaster.fromAddress(parseAddressOrThrow(data.master)));
262
+
263
+ if (!(await this.client.isContractDeployed(master.address))) {
264
+ throw new Error(`Master contract is not deployed: ${master.address.toString()}`);
265
+ }
266
+
267
+ const nextIndex = await master.getGetNextCollectionIndex();
268
+ const collectionAddress = await master.getGetCollectionAddressByIndex(nextIndex);
269
+
270
+ if (!collectionAddress) {
271
+ throw new Error(`Unable to resolve collection address for index ${nextIndex.toString()}`);
272
+ }
273
+
274
+ const owner = data.owner ? parseAddressOrThrow(data.owner) : wallet.address;
275
+ const royaltyDestination = data.royaltyParams?.destination ?? owner;
276
+
277
+ await master.send(
278
+ wallet.sender(secretKey),
279
+ {
280
+ value: parseCoins(data.value, '0.25'),
281
+ },
282
+ {
283
+ $$type: 'CollectionMintParams',
284
+ queryId: parseBigint(data.queryId, 0n),
285
+ owner_address: owner,
286
+ collection_content: normalizeContent(data.collectionContent),
287
+ nft_individual_content_url: normalizeContent(data.nftIndividualContent),
288
+ royalty_params: {
289
+ $$type: 'RoyaltyParams',
290
+ numerator: data.royaltyParams?.numerator ?? 100n,
291
+ denominator: data.royaltyParams?.denominator ?? 1000n,
292
+ destination: parseAddressOrThrow(royaltyDestination),
293
+ },
294
+ mint_limit: parseBigint(data.mintLimit, 100n),
295
+ mint_time_limit: parseBigint(data.mintTimeLimit, 1900000000n),
296
+ is_sbt: parseSbt(data.isSbt),
297
+ nft_price: parseCoins(data.nftPrice, '0.2'),
298
+ enable_profile: data.enableProfile ?? false,
299
+ enable_whitelist: data.enableWhitelist ?? false,
300
+ user_item_limit: parseBigint(data.userItemLimit, 0n),
301
+ },
302
+ );
303
+
304
+ const deployed = data.waitForDeploy ?? true
305
+ ? await waitForDeploy(this.client, collectionAddress, data.waitAttempts ?? 30, data.waitIntervalMs ?? 3000)
306
+ : await this.client.isContractDeployed(collectionAddress);
307
+
308
+ const blockchainData = deployed ? await this.blockchainCollectionData(collectionAddress) : null;
309
+ const serverData = deployed
310
+ ? await this.serverCollectionData(collectionAddress)
311
+ : this.skippedServerData<ProjectCollectionData>(collectionAddress, 'api/collection');
312
+
313
+ return {
314
+ address: collectionAddress,
315
+ deployed,
316
+ status: this.deploymentStatus(serverData, deployed),
317
+ blockchainData,
318
+ serverData,
319
+ };
320
+ }
321
+
322
+ private async mintNftInCollection(collection: Address): Promise<MintNftInCollectionResult> {
323
+ const { wallet, secretKey } = await this.wallet();
324
+ const contract = this.client.open(SimpleNftCollectionV2.fromAddress(collection));
325
+
326
+ if (!(await this.client.isContractDeployed(collection))) {
327
+ throw new Error(`Collection contract is not deployed: ${collection.toString()}`);
328
+ }
329
+
330
+ const collectionData = await contract.getGetCollectionData();
331
+ const masterData = await this.getCollectionMasterData(contract);
332
+ const nftAddress = await contract.getGetNftAddressByIndex(collectionData.next_item_index);
333
+
334
+ await contract.send(
335
+ wallet.sender(secretKey),
336
+ {
337
+ value: masterData.price,
338
+ },
339
+ 'Mint',
340
+ );
341
+
342
+ const deployed = nftAddress
343
+ ? await waitForDeploy(this.client, nftAddress, 30, 3000)
344
+ : false;
345
+ const blockchainData = deployed && nftAddress ? await this.blockchainNftData(nftAddress) : null;
346
+ const serverData = deployed && nftAddress
347
+ ? await this.serverItemData(nftAddress)
348
+ : this.skippedServerData<ProjectItemData>(nftAddress, 'api/item');
349
+
350
+ return {
351
+ address: nftAddress,
352
+ collection,
353
+ deployed,
354
+ status: this.deploymentStatus(serverData, deployed),
355
+ blockchainData,
356
+ serverData,
357
+ };
358
+ }
359
+ }
360
+
361
+ export default SimpleNft;
package/src/types.ts ADDED
@@ -0,0 +1,62 @@
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
+
6
+ export type SimpleNftNetwork = 'mainnet' | 'testnet';
7
+
8
+ export type AddressLike = string | Address;
9
+
10
+ export type Coins = bigint | number | string;
11
+
12
+ export interface SimpleNftOptions {
13
+ hostname?: string;
14
+ mnemonic?: string | string[];
15
+ network?: SimpleNftNetwork;
16
+ serverPaths?: ProjectServerPaths;
17
+ }
18
+
19
+ export interface CollectionCreationData {
20
+ master: AddressLike;
21
+ owner?: AddressLike;
22
+ collectionContent: string | Cell;
23
+ nftIndividualContent: string | Cell;
24
+ royaltyParams?: Omit<RoyaltyParams, '$$type'> | RoyaltyParams;
25
+ mintLimit?: bigint | number | string;
26
+ mintTimeLimit?: bigint | number | string;
27
+ isSbt?: boolean | bigint | number | string;
28
+ nftPrice?: Coins;
29
+ enableProfile?: boolean;
30
+ enableWhitelist?: boolean;
31
+ userItemLimit?: bigint | number | string;
32
+ queryId?: bigint | number | string;
33
+ value?: Coins;
34
+ waitForDeploy?: boolean;
35
+ waitAttempts?: number;
36
+ waitIntervalMs?: number;
37
+ }
38
+
39
+ export interface CollectionInfo {
40
+ collectionData: CollectionData;
41
+ masterData: CollectionMasterData | CollectionMasterDataV2;
42
+ sampleNftContent: Cell | null;
43
+ }
44
+
45
+ export type NftData = GetNftData;
46
+
47
+ export interface CreateCollectionResult {
48
+ address: Address;
49
+ deployed: boolean;
50
+ status: DeploymentStatus;
51
+ blockchainData: CollectionInfo | null;
52
+ serverData: ProjectServerResult<ProjectCollectionData>;
53
+ }
54
+
55
+ export interface MintNftInCollectionResult {
56
+ address: Address | null;
57
+ collection: Address;
58
+ deployed: boolean;
59
+ status: DeploymentStatus;
60
+ blockchainData: NftData | null;
61
+ serverData: ProjectServerResult<ProjectItemData>;
62
+ }