@zoralabs/protocol-sdk 0.8.0 → 0.9.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 (75) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/CHANGELOG.md +11 -0
  3. package/dist/allow-list/allow-list-client.d.ts +26 -0
  4. package/dist/allow-list/allow-list-client.d.ts.map +1 -0
  5. package/dist/allow-list/types.d.ts +14 -0
  6. package/dist/allow-list/types.d.ts.map +1 -0
  7. package/dist/apis/generated/allow-list-api-types.d.ts +288 -0
  8. package/dist/apis/generated/allow-list-api-types.d.ts.map +1 -0
  9. package/dist/apis/http-api-base.d.ts.map +1 -1
  10. package/dist/apis/subgraph-querier.d.ts +18 -0
  11. package/dist/apis/subgraph-querier.d.ts.map +1 -0
  12. package/dist/create/contract-setup.d.ts +1 -0
  13. package/dist/create/contract-setup.d.ts.map +1 -1
  14. package/dist/create/minter-defaults.d.ts +5 -0
  15. package/dist/create/minter-defaults.d.ts.map +1 -0
  16. package/dist/create/minter-setup.d.ts +14 -0
  17. package/dist/create/minter-setup.d.ts.map +1 -0
  18. package/dist/create/token-setup.d.ts +4 -19
  19. package/dist/create/token-setup.d.ts.map +1 -1
  20. package/dist/create/types.d.ts +32 -7
  21. package/dist/create/types.d.ts.map +1 -1
  22. package/dist/create/update.d.ts +15 -0
  23. package/dist/create/update.d.ts.map +1 -0
  24. package/dist/index.cjs +2249 -1936
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.ts +2 -0
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +2284 -1966
  29. package/dist/index.js.map +1 -1
  30. package/dist/ipfs/token-metadata.d.ts +1 -0
  31. package/dist/ipfs/token-metadata.d.ts.map +1 -1
  32. package/dist/mint/mint-client.d.ts +2 -6
  33. package/dist/mint/mint-client.d.ts.map +1 -1
  34. package/dist/mint/mint-queries.d.ts +3 -1
  35. package/dist/mint/mint-queries.d.ts.map +1 -1
  36. package/dist/mint/mint-transactions.d.ts +9 -7
  37. package/dist/mint/mint-transactions.d.ts.map +1 -1
  38. package/dist/mint/subgraph-mint-getter.d.ts +5 -4
  39. package/dist/mint/subgraph-mint-getter.d.ts.map +1 -1
  40. package/dist/mint/subgraph-queries.d.ts +42 -15
  41. package/dist/mint/subgraph-queries.d.ts.map +1 -1
  42. package/dist/mint/types.d.ts +32 -13
  43. package/dist/mint/types.d.ts.map +1 -1
  44. package/dist/sparks/sparks-contracts.d.ts +96 -96
  45. package/dist/types.d.ts +1 -1
  46. package/dist/types.d.ts.map +1 -1
  47. package/dist/utils.d.ts +1 -8
  48. package/dist/utils.d.ts.map +1 -1
  49. package/package.json +2 -2
  50. package/src/allow-list/allow-list-client.ts +105 -0
  51. package/src/allow-list/types.ts +15 -0
  52. package/src/apis/generated/allow-list-api-types.ts +288 -0
  53. package/src/apis/http-api-base.ts +12 -0
  54. package/src/apis/subgraph-querier.ts +38 -0
  55. package/src/create/1155-create-helper.test.ts +216 -66
  56. package/src/create/1155-create-helper.ts +4 -4
  57. package/src/create/contract-setup.ts +8 -0
  58. package/src/create/minter-defaults.test.ts +21 -0
  59. package/src/create/minter-defaults.ts +134 -0
  60. package/src/create/minter-setup.ts +293 -0
  61. package/src/create/token-setup.ts +14 -190
  62. package/src/create/types.ts +56 -9
  63. package/src/create/update.ts +93 -0
  64. package/src/index.ts +4 -0
  65. package/src/ipfs/token-metadata.ts +18 -0
  66. package/src/mint/mint-client.test.ts +219 -15
  67. package/src/mint/mint-client.ts +2 -34
  68. package/src/mint/mint-queries.ts +34 -13
  69. package/src/mint/mint-transactions.ts +104 -17
  70. package/src/mint/subgraph-mint-getter.ts +107 -50
  71. package/src/mint/subgraph-queries.ts +67 -37
  72. package/src/mint/types.ts +55 -16
  73. package/src/premint/premint-client.test.ts +6 -5
  74. package/src/types.ts +1 -1
  75. package/src/utils.ts +1 -25
@@ -4,18 +4,28 @@ import {
4
4
  getTokenIdFromCreateReceipt,
5
5
  } from "./1155-create-helper";
6
6
  import { createCreatorClient } from "src/sdk";
7
- import { zoraCreator1155ImplABI } from "@zoralabs/protocol-deployments";
7
+ import {
8
+ zoraCreator1155ImplABI,
9
+ zoraTimedSaleStrategyAddress,
10
+ } from "@zoralabs/protocol-deployments";
8
11
  import { waitForSuccess } from "src/test-utils";
9
- import { parseEther } from "viem";
10
12
  import {
11
- MintableParameters,
12
- makePrepareMint1155TokenParams,
13
- } from "src/mint/mint-transactions";
13
+ Address,
14
+ parseEther,
15
+ PublicClient,
16
+ TransactionReceipt,
17
+ zeroAddress,
18
+ } from "viem";
19
+ import { makePrepareMint1155TokenParams } from "src/mint/mint-transactions";
14
20
  import { forkUrls, makeAnvilTest } from "src/anvil";
15
21
  import { zora } from "viem/chains";
22
+ import { AllowList } from "src/allow-list/types";
23
+ import { createAllowList } from "src/allow-list/allow-list-client";
24
+ import { NewContractParams } from "./types";
16
25
 
17
- const demoTokenMetadataURI = "ipfs://DUMMY/token.json";
18
- const demoContractMetadataURI = "ipfs://DUMMY/contract.json";
26
+ export const demoTokenMetadataURI =
27
+ "ipfs://bafkreice23maski3x52tsfqgxstx3kbiifnt5jotg3a5ynvve53c4soi2u";
28
+ export const demoContractMetadataURI = "ipfs://DUMMY/contract.json";
19
29
 
20
30
  const anvilTest = makeAnvilTest({
21
31
  forkUrl: forkUrls.zoraMainnet,
@@ -23,9 +33,46 @@ const anvilTest = makeAnvilTest({
23
33
  anvilChainId: zora.id,
24
34
  });
25
35
 
36
+ const PERMISSION_BITS = {
37
+ MINTER: 2n ** 2n,
38
+ };
39
+
40
+ const minterIsMinterOnToken = async ({
41
+ publicClient,
42
+ contractAddress,
43
+ tokenId,
44
+ minter,
45
+ }: {
46
+ publicClient: Pick<PublicClient, "readContract">;
47
+ contractAddress: Address;
48
+ tokenId: bigint;
49
+ minter: Address;
50
+ }) => {
51
+ return await publicClient.readContract({
52
+ abi: zoraCreator1155ImplABI,
53
+ address: contractAddress,
54
+ functionName: "isAdminOrRole",
55
+ args: [minter, tokenId, PERMISSION_BITS.MINTER],
56
+ });
57
+ };
58
+
59
+ function logFailure(receipt: TransactionReceipt) {
60
+ if (receipt.status !== "success") {
61
+ console.log("transaction failed");
62
+ console.log(receipt.logs);
63
+ }
64
+ }
65
+
66
+ function randomNewContract(): NewContractParams {
67
+ return {
68
+ name: `testContract-${Math.round(Math.random() * 1_000_000)}`,
69
+ uri: demoContractMetadataURI,
70
+ };
71
+ }
72
+
26
73
  describe("create-helper", () => {
27
74
  anvilTest(
28
- "creates a new 1155 contract and token",
75
+ "when no sales config is provided, it creates a new 1155 contract and token using the timed sale strategy",
29
76
  async ({ viemClients: { publicClient, walletClient, chain } }) => {
30
77
  const addresses = await walletClient.getAddresses();
31
78
  const creatorAddress = addresses[0]!;
@@ -34,32 +81,89 @@ describe("create-helper", () => {
34
81
  chainId: chain.id,
35
82
  publicClient: publicClient,
36
83
  });
37
- const { parameters, contractAddress: collectionAddress } =
84
+ const { parameters, contractAddress, newTokenId } =
38
85
  await creatorClient.create1155({
39
- contract: {
40
- name: "testContract",
41
- uri: demoContractMetadataURI,
42
- },
86
+ contract: randomNewContract(),
43
87
  token: {
44
88
  tokenMetadataURI: demoTokenMetadataURI,
45
89
  mintToCreatorCount: 1,
46
90
  },
47
91
  account: creatorAddress,
48
92
  });
93
+
49
94
  const { request } = await publicClient.simulateContract(parameters);
50
95
  const hash = await walletClient.writeContract(request);
51
96
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
97
+ logFailure(receipt);
98
+ expect(receipt.status).toBe("success");
52
99
  expect(receipt).not.toBeNull();
53
100
  expect(receipt.to).to.equal("0x777777c338d93e2c7adf08d102d45ca7cc4ed021");
54
101
  expect(getTokenIdFromCreateReceipt(receipt)).to.be.equal(1n);
55
102
  expect(getContractAddressFromReceipt(receipt)).to.be.equal(
56
- collectionAddress,
103
+ contractAddress,
57
104
  );
105
+
106
+ expect(
107
+ await minterIsMinterOnToken({
108
+ contractAddress,
109
+ tokenId: newTokenId,
110
+ publicClient,
111
+ minter:
112
+ zoraTimedSaleStrategyAddress[
113
+ chain.id as keyof typeof zoraTimedSaleStrategyAddress
114
+ ],
115
+ }),
116
+ ).toBe(true);
117
+ },
118
+ 20 * 1000,
119
+ );
120
+ anvilTest(
121
+ "when price is set to 0, it creates a new 1155 contract and token using the timed sale strategy",
122
+ async ({ viemClients: { publicClient, walletClient, chain } }) => {
123
+ const addresses = await walletClient.getAddresses();
124
+ const creatorAddress = addresses[0]!;
125
+
126
+ const creatorClient = createCreatorClient({
127
+ chainId: chain.id,
128
+ publicClient: publicClient,
129
+ });
130
+ const { parameters, contractAddress, newTokenId } =
131
+ await creatorClient.create1155({
132
+ contract: randomNewContract(),
133
+ token: {
134
+ tokenMetadataURI: demoTokenMetadataURI,
135
+ salesConfig: {
136
+ pricePerToken: 0n,
137
+ },
138
+ },
139
+ account: creatorAddress,
140
+ });
141
+
142
+ const { request } = await publicClient.simulateContract(parameters);
143
+ const hash = await walletClient.writeContract(request);
144
+ const receipt = await publicClient.waitForTransactionReceipt({ hash });
145
+
146
+ logFailure(receipt);
147
+
148
+ expect(receipt.status).toBe("success");
149
+
150
+ expect(
151
+ await minterIsMinterOnToken({
152
+ contractAddress,
153
+ tokenId: newTokenId,
154
+ minter:
155
+ zoraTimedSaleStrategyAddress[
156
+ chain.id as keyof typeof zoraTimedSaleStrategyAddress
157
+ ],
158
+ publicClient,
159
+ }),
160
+ ).toBe(true);
58
161
  },
59
162
  20 * 1000,
60
163
  );
164
+
61
165
  anvilTest(
62
- "creates a new contract, then can create a new token on this existing contract",
166
+ "can create a new contract, then can create a new token on this existing contract",
63
167
  async ({ viemClients: { publicClient, walletClient, chain } }) => {
64
168
  const addresses = await walletClient.getAddresses();
65
169
  const creatorAccount = addresses[0]!;
@@ -71,10 +175,7 @@ describe("create-helper", () => {
71
175
 
72
176
  const { parameters: request, contractAddress: contractAddress } =
73
177
  await creatorClient.create1155({
74
- contract: {
75
- name: "testContract2",
76
- uri: demoContractMetadataURI,
77
- },
178
+ contract: randomNewContract(),
78
179
  token: {
79
180
  tokenMetadataURI: demoTokenMetadataURI,
80
181
  mintToCreatorCount: 3,
@@ -85,6 +186,8 @@ describe("create-helper", () => {
85
186
  await publicClient.simulateContract(request);
86
187
  const hash = await walletClient.writeContract(simulateResponse);
87
188
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
189
+ logFailure(receipt);
190
+ expect(receipt.status).toBe("success");
88
191
  const firstTokenId = getTokenIdFromCreateReceipt(receipt);
89
192
  expect(firstTokenId).to.be.equal(1n);
90
193
  expect(receipt).not.toBeNull();
@@ -115,9 +218,12 @@ describe("create-helper", () => {
115
218
  const newReceipt = await publicClient.waitForTransactionReceipt({
116
219
  hash: newHash,
117
220
  });
221
+
222
+ logFailure(receipt);
223
+
224
+ expect(newReceipt.status).toBe("success");
118
225
  const tokenId = getTokenIdFromCreateReceipt(newReceipt);
119
226
  expect(tokenId).to.be.equal(2n);
120
- expect(newReceipt).not.toBeNull();
121
227
  },
122
228
  20 * 1000,
123
229
  );
@@ -137,10 +243,7 @@ describe("create-helper", () => {
137
243
  contractAddress: collectionAddress,
138
244
  newTokenId,
139
245
  } = await creatorClient.create1155({
140
- contract: {
141
- name: "testContract",
142
- uri: demoContractMetadataURI,
143
- },
246
+ contract: randomNewContract(),
144
247
  token: {
145
248
  tokenMetadataURI: demoTokenMetadataURI,
146
249
  createReferral,
@@ -151,7 +254,7 @@ describe("create-helper", () => {
151
254
  await publicClient.simulateContract(request);
152
255
  const hash = await walletClient.writeContract(simulationResponse);
153
256
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
154
- expect(receipt).not.toBeNull();
257
+ expect(receipt.status).toBe("success");
155
258
  expect(receipt.to).to.equal("0x777777c338d93e2c7adf08d102d45ca7cc4ed021");
156
259
  expect(getTokenIdFromCreateReceipt(receipt)).to.be.equal(newTokenId);
157
260
 
@@ -182,15 +285,11 @@ describe("create-helper", () => {
182
285
  const {
183
286
  parameters: request,
184
287
  newTokenId,
185
- newToken,
186
288
  minter,
187
289
  contractAddress: collectionAddress,
188
290
  contractVersion,
189
291
  } = await creatorClient.create1155({
190
- contract: {
191
- name: "testContract",
192
- uri: demoContractMetadataURI,
193
- },
292
+ contract: randomNewContract(),
194
293
  token: {
195
294
  tokenMetadataURI: demoTokenMetadataURI,
196
295
  },
@@ -203,20 +302,6 @@ describe("create-helper", () => {
203
302
  publicClient,
204
303
  );
205
304
 
206
- const salesConfigAndTokenInfo: MintableParameters = {
207
- mintFeePerQuantity: parseEther("0.000777"),
208
- contractVersion,
209
- salesConfig: {
210
- saleType: "fixedPrice",
211
- address: minter,
212
- pricePerToken: newToken.salesConfig.pricePerToken,
213
- // these dont matter
214
- maxTokensPerAddress: 0n,
215
- saleEnd: "",
216
- saleStart: "",
217
- },
218
- };
219
-
220
305
  const quantityToMint = 5n;
221
306
 
222
307
  // now try to mint a free mint
@@ -231,7 +316,21 @@ describe("create-helper", () => {
231
316
  tokenContract: collectionAddress,
232
317
  minterAccount: minterAddress,
233
318
  tokenId: newTokenId,
234
- salesConfigAndTokenInfo,
319
+ salesConfigAndTokenInfo: {
320
+ contractVersion,
321
+ salesConfig: {
322
+ saleType: "timed",
323
+ address: minter,
324
+ // below fields arent used when determining mint price for minting
325
+ saleEnd: "",
326
+ saleStart: "",
327
+ erc20Z: zeroAddress,
328
+ pool: zeroAddress,
329
+ secondaryActivated: false,
330
+ mintFeePerQuantity: parseEther("0.000111"),
331
+ mintFee: 0n,
332
+ },
333
+ },
235
334
  quantityToMint,
236
335
  });
237
336
 
@@ -246,7 +345,7 @@ describe("create-helper", () => {
246
345
  );
247
346
 
248
347
  anvilTest(
249
- "creates a new 1155 paid mint that can be minted",
348
+ "creates a new 1155 paid mint that can be minted using the fixed price minter",
250
349
  async ({
251
350
  viemClients: { testClient, publicClient, walletClient, chain },
252
351
  }) => {
@@ -264,14 +363,10 @@ describe("create-helper", () => {
264
363
  parameters: request,
265
364
  contractAddress: collectionAddress,
266
365
  newTokenId,
267
- newToken,
268
366
  minter,
269
367
  contractVersion,
270
368
  } = await creatorClient.create1155({
271
- contract: {
272
- name: "testContract",
273
- uri: demoContractMetadataURI,
274
- },
369
+ contract: randomNewContract(),
275
370
  token: {
276
371
  tokenMetadataURI: demoTokenMetadataURI,
277
372
  salesConfig: {
@@ -287,20 +382,6 @@ describe("create-helper", () => {
287
382
  publicClient,
288
383
  );
289
384
 
290
- const salesConfigAndTokenInfo: MintableParameters = {
291
- mintFeePerQuantity: parseEther("0.000777"),
292
- contractVersion,
293
- salesConfig: {
294
- saleType: "fixedPrice",
295
- address: minter,
296
- pricePerToken: newToken.salesConfig.pricePerToken,
297
- // these dont matter
298
- maxTokensPerAddress: 0n,
299
- saleEnd: "",
300
- saleStart: "",
301
- },
302
- };
303
-
304
385
  const quantityToMint = 5n;
305
386
 
306
387
  // now try to mint a free mint
@@ -315,7 +396,23 @@ describe("create-helper", () => {
315
396
  tokenContract: collectionAddress,
316
397
  minterAccount: minterAddress,
317
398
  tokenId: newTokenId,
318
- salesConfigAndTokenInfo,
399
+ salesConfigAndTokenInfo: {
400
+ contractVersion,
401
+ salesConfig: {
402
+ mintFeePerQuantity: await publicClient.readContract({
403
+ abi: zoraCreator1155ImplABI,
404
+ functionName: "mintFee",
405
+ address: collectionAddress,
406
+ }),
407
+ saleType: "fixedPrice",
408
+ address: minter,
409
+ pricePerToken,
410
+ // these dont matter
411
+ maxTokensPerAddress: 0n,
412
+ saleEnd: "",
413
+ saleStart: "",
414
+ },
415
+ },
319
416
  quantityToMint,
320
417
  });
321
418
 
@@ -328,4 +425,57 @@ describe("create-helper", () => {
328
425
  },
329
426
  20 * 1000,
330
427
  );
428
+
429
+ anvilTest(
430
+ "creates an allow list mint contract",
431
+ async ({ viemClients: { publicClient, walletClient, chain } }) => {
432
+ const creator = (await walletClient.getAddresses())[0]!;
433
+ const allowList: AllowList = {
434
+ entries: [
435
+ {
436
+ user: "0xf69fEc6d858c77e969509843852178bd24CAd2B6",
437
+ price: 2n,
438
+ maxCanMint: 10000,
439
+ },
440
+ {
441
+ user: "0xcD08da546414dd463C89705B5E72CE1AeebF1567",
442
+ price: 3n,
443
+ maxCanMint: 10,
444
+ },
445
+ ],
446
+ };
447
+
448
+ const root = await createAllowList({
449
+ allowList,
450
+ });
451
+
452
+ const creatorClient = createCreatorClient({
453
+ chainId: chain.id,
454
+ publicClient: publicClient,
455
+ });
456
+
457
+ const { parameters } = await creatorClient.create1155({
458
+ contract: {
459
+ name: "test allowlists",
460
+ uri: "ipfs://bafkreiainxen4b4wz4ubylvbhons6rembxdet4a262nf2lziclqvv7au3e",
461
+ },
462
+ token: {
463
+ tokenMetadataURI:
464
+ "ipfs://bafkreice23maski3x52tsfqgxstx3kbiifnt5jotg3a5ynvve53c4soi2u",
465
+ salesConfig: {
466
+ type: "allowlistMint",
467
+ presaleMerkleRoot: `0x${root}`,
468
+ },
469
+ },
470
+ account: creator,
471
+ });
472
+
473
+ const { request } = await publicClient.simulateContract(parameters);
474
+
475
+ await waitForSuccess(
476
+ await walletClient.writeContract(request),
477
+ publicClient,
478
+ );
479
+ },
480
+ );
331
481
  });
@@ -192,7 +192,7 @@ async function createNew1155ContractAndToken({
192
192
  minter,
193
193
  newToken,
194
194
  setupActions: tokenSetupActions,
195
- } = prepareSetupActions({
195
+ } = await prepareSetupActions({
196
196
  chainId,
197
197
  account,
198
198
  contractVersion,
@@ -250,7 +250,7 @@ async function createNew1155Token({
250
250
  minter,
251
251
  newToken,
252
252
  setupActions: tokenSetupActions,
253
- } = prepareSetupActions({
253
+ } = await prepareSetupActions({
254
254
  chainId,
255
255
  account,
256
256
  contractVersion,
@@ -275,7 +275,7 @@ async function createNew1155Token({
275
275
  };
276
276
  }
277
277
 
278
- function prepareSetupActions({
278
+ async function prepareSetupActions({
279
279
  chainId,
280
280
  account,
281
281
  contractVersion,
@@ -291,7 +291,7 @@ function prepareSetupActions({
291
291
  minter,
292
292
  newToken,
293
293
  setupActions: tokenSetupActions,
294
- } = constructCreate1155TokenCalls({
294
+ } = await constructCreate1155TokenCalls({
295
295
  chainId: chainId,
296
296
  ownerAddress: account,
297
297
  contractVersion,
@@ -27,6 +27,7 @@ export async function getContractInfoExistingContract({
27
27
  // Account that is the creator of the contract
28
28
  }): Promise<{
29
29
  contractVersion: string;
30
+ contractName: string;
30
31
  nextTokenId: bigint;
31
32
  }> {
32
33
  // Check if contract exists either from metadata or the static address passed in.
@@ -50,8 +51,15 @@ export async function getContractInfoExistingContract({
50
51
  functionName: "nextTokenId",
51
52
  });
52
53
 
54
+ const contractName = await publicClient.readContract({
55
+ address: contractAddress,
56
+ abi: zoraCreator1155ImplABI,
57
+ functionName: "name",
58
+ });
59
+
53
60
  return {
54
61
  contractVersion,
62
+ contractName,
55
63
  nextTokenId,
56
64
  };
57
65
  }
@@ -0,0 +1,21 @@
1
+ import { describe, it, expect, assert } from "vitest";
2
+ import { parseNameIntoSymbol } from "./minter-defaults";
3
+
4
+ describe("parseNameIntoSymbol", () => {
5
+ it("removes spaces and vowels and converts to uppercase", () => {
6
+ const symbol = parseNameIntoSymbol("My 4 To *5 @-Name");
7
+
8
+ expect(symbol).toBe("$MY4T");
9
+ });
10
+ it("works with less than 4 characters", () => {
11
+ const symbol = parseNameIntoSymbol("M4y a");
12
+
13
+ expect(symbol).toBe("$M4Y");
14
+ });
15
+
16
+ it("works with no characters", () => {
17
+ assert.throws(() => {
18
+ parseNameIntoSymbol("AEIO U");
19
+ }, "Not enough valid characters to generate a symbol");
20
+ });
21
+ });
@@ -0,0 +1,134 @@
1
+ import { Concrete } from "src/utils";
2
+ import {
3
+ SalesConfigParamsType,
4
+ AllowListParamType,
5
+ Erc20ParamsType,
6
+ FixedPriceParamsType,
7
+ SaleStartAndEnd,
8
+ MaxTokensPerAddress,
9
+ ConcreteSalesConfig,
10
+ TimedSaleParamsType,
11
+ } from "./types";
12
+ import { fetchTokenMetadata } from "src/ipfs/token-metadata";
13
+
14
+ // Sales end forever amount (uint64 max)
15
+ export const SALE_END_FOREVER = 18446744073709551615n;
16
+
17
+ const DEFAULT_SALE_START_AND_END: Concrete<SaleStartAndEnd> = {
18
+ // Sale start time – defaults to beginning of unix time
19
+ saleStart: 0n,
20
+ // This is the end of uint64, plenty of time
21
+ saleEnd: SALE_END_FOREVER,
22
+ };
23
+
24
+ const DEFAULT_MAX_TOKENS_PER_ADDRESS: Concrete<MaxTokensPerAddress> = {
25
+ maxTokensPerAddress: 0n,
26
+ };
27
+
28
+ const erc20SaleSettingsWithDefaults = (
29
+ params: Erc20ParamsType,
30
+ ): Concrete<Erc20ParamsType> => ({
31
+ ...DEFAULT_SALE_START_AND_END,
32
+ ...DEFAULT_MAX_TOKENS_PER_ADDRESS,
33
+ ...params,
34
+ });
35
+
36
+ const allowListWithDefaults = (
37
+ allowlist: AllowListParamType,
38
+ ): Concrete<AllowListParamType> => {
39
+ return {
40
+ ...DEFAULT_SALE_START_AND_END,
41
+ ...allowlist,
42
+ };
43
+ };
44
+
45
+ const fixedPriceSettingsWithDefaults = (
46
+ params: FixedPriceParamsType,
47
+ ): Concrete<FixedPriceParamsType> => ({
48
+ ...DEFAULT_SALE_START_AND_END,
49
+ ...DEFAULT_MAX_TOKENS_PER_ADDRESS,
50
+ type: "fixedPrice",
51
+ ...params,
52
+ });
53
+
54
+ export const parseNameIntoSymbol = (name: string) => {
55
+ if (name === "") {
56
+ throw new Error("Name must be provided to generate a symbol");
57
+ }
58
+ const result =
59
+ "$" +
60
+ name
61
+ // Remove all non-alphanumeric characters
62
+ .replace(/[^a-zA-Z0-9]/g, "")
63
+ // and leading dollar signs
64
+ .replace(/^\$+/, "")
65
+ .toUpperCase()
66
+ // Remove all vowels and spaces
67
+ .replace(/[AEIOU\s]/g, "")
68
+ // Strip down to 4 characters
69
+ .slice(0, 4);
70
+
71
+ if (result === "$") {
72
+ throw new Error("Not enough valid characters to generate a symbol");
73
+ }
74
+
75
+ return result;
76
+ };
77
+
78
+ async function fetchTokenNameFromMetadata(
79
+ tokenMetadataURI: string,
80
+ ): Promise<string> {
81
+ const tokenMetadata = await fetchTokenMetadata(tokenMetadataURI);
82
+
83
+ if (!tokenMetadata.name) {
84
+ throw new Error("No name found in token metadata");
85
+ }
86
+
87
+ return tokenMetadata.name;
88
+ }
89
+ const timedSaleSettingsWithDefaults = async (
90
+ params: TimedSaleParamsType,
91
+ tokenMetadataURI: string,
92
+ ): Promise<Concrete<TimedSaleParamsType>> => {
93
+ // If the name is not provided, try to fetch it from the metadata
94
+ const erc20Name =
95
+ params.erc20Name || (await fetchTokenNameFromMetadata(tokenMetadataURI));
96
+ const symbol = params.erc20Symbol || parseNameIntoSymbol(erc20Name);
97
+
98
+ return {
99
+ type: "timed",
100
+ ...DEFAULT_SALE_START_AND_END,
101
+ erc20Name,
102
+ erc20Symbol: symbol,
103
+ };
104
+ };
105
+
106
+ const isAllowList = (
107
+ salesConfig: SalesConfigParamsType,
108
+ ): salesConfig is AllowListParamType => salesConfig.type === "allowlistMint";
109
+ const isErc20 = (
110
+ salesConfig: SalesConfigParamsType,
111
+ ): salesConfig is Erc20ParamsType => salesConfig.type === "erc20Mint";
112
+ const isFixedPrice = (
113
+ salesConfig: SalesConfigParamsType,
114
+ ): salesConfig is FixedPriceParamsType => {
115
+ return (salesConfig as FixedPriceParamsType).pricePerToken > 0n;
116
+ };
117
+
118
+ export const getSalesConfigWithDefaults = async (
119
+ salesConfig: SalesConfigParamsType | undefined,
120
+ tokenMetadataURI: string,
121
+ ): Promise<ConcreteSalesConfig> => {
122
+ if (!salesConfig) return timedSaleSettingsWithDefaults({}, tokenMetadataURI);
123
+ if (isAllowList(salesConfig)) {
124
+ return allowListWithDefaults(salesConfig);
125
+ }
126
+ if (isErc20(salesConfig)) {
127
+ return erc20SaleSettingsWithDefaults(salesConfig);
128
+ }
129
+ if (isFixedPrice(salesConfig)) {
130
+ return fixedPriceSettingsWithDefaults(salesConfig);
131
+ }
132
+
133
+ return timedSaleSettingsWithDefaults(salesConfig, tokenMetadataURI);
134
+ };