@zoralabs/protocol-sdk 0.6.0 → 0.7.1

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 (86) hide show
  1. package/.turbo/turbo-build.log +6 -6
  2. package/CHANGELOG.md +12 -0
  3. package/dist/constants.d.ts +0 -1
  4. package/dist/constants.d.ts.map +1 -1
  5. package/dist/index.cjs +2023 -431
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.ts +1 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +2001 -412
  10. package/dist/index.js.map +1 -1
  11. package/dist/ipfs/arweave.d.ts +3 -0
  12. package/dist/ipfs/arweave.d.ts.map +1 -0
  13. package/dist/ipfs/gateway.d.ts +4 -0
  14. package/dist/ipfs/gateway.d.ts.map +1 -0
  15. package/dist/ipfs/index.d.ts +4 -0
  16. package/dist/ipfs/index.d.ts.map +1 -0
  17. package/dist/ipfs/ipfs.d.ts +5 -0
  18. package/dist/ipfs/ipfs.d.ts.map +1 -0
  19. package/dist/ipfs/mimeTypes.d.ts +25 -0
  20. package/dist/ipfs/mimeTypes.d.ts.map +1 -0
  21. package/dist/ipfs/text-metadata.d.ts +5 -0
  22. package/dist/ipfs/text-metadata.d.ts.map +1 -0
  23. package/dist/ipfs/token-metadata.d.ts +14 -0
  24. package/dist/ipfs/token-metadata.d.ts.map +1 -0
  25. package/dist/ipfs/types.d.ts +49 -0
  26. package/dist/ipfs/types.d.ts.map +1 -0
  27. package/dist/mint/mint-client.d.ts +28 -4071
  28. package/dist/mint/mint-client.d.ts.map +1 -1
  29. package/dist/mint/mint-queries.d.ts +38 -0
  30. package/dist/mint/mint-queries.d.ts.map +1 -0
  31. package/dist/mint/mint-transactions.d.ts +20 -0
  32. package/dist/mint/mint-transactions.d.ts.map +1 -0
  33. package/dist/mint/subgraph-mint-getter.d.ts +14 -7
  34. package/dist/mint/subgraph-mint-getter.d.ts.map +1 -1
  35. package/dist/mint/subgraph-queries.d.ts +55 -0
  36. package/dist/mint/subgraph-queries.d.ts.map +1 -0
  37. package/dist/mint/types.d.ts +117 -19
  38. package/dist/mint/types.d.ts.map +1 -1
  39. package/dist/mint/utils.d.ts +2 -0
  40. package/dist/mint/utils.d.ts.map +1 -0
  41. package/dist/mints/mints-contracts.d.ts +3 -4494
  42. package/dist/mints/mints-contracts.d.ts.map +1 -1
  43. package/dist/premint/conversions.d.ts +12 -15
  44. package/dist/premint/conversions.d.ts.map +1 -1
  45. package/dist/premint/premint-api-client.d.ts +14 -11
  46. package/dist/premint/premint-api-client.d.ts.map +1 -1
  47. package/dist/premint/premint-client.d.ts +17 -6
  48. package/dist/premint/premint-client.d.ts.map +1 -1
  49. package/dist/premint/preminter.d.ts +1 -1
  50. package/dist/premint/preminter.d.ts.map +1 -1
  51. package/dist/sdk.d.ts +4 -2
  52. package/dist/sdk.d.ts.map +1 -1
  53. package/dist/types.d.ts +3 -0
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/utils.d.ts +10 -2
  56. package/dist/utils.d.ts.map +1 -1
  57. package/package.json +2 -1
  58. package/src/constants.ts +0 -36
  59. package/src/create/1155-create-helper.test.ts +9 -7
  60. package/src/index.ts +2 -0
  61. package/src/ipfs/arweave.ts +5 -0
  62. package/src/ipfs/gateway.ts +48 -0
  63. package/src/ipfs/index.ts +7 -0
  64. package/src/ipfs/ipfs.ts +82 -0
  65. package/src/ipfs/mimeTypes.ts +141 -0
  66. package/src/ipfs/text-metadata.ts +128 -0
  67. package/src/ipfs/token-metadata.ts +99 -0
  68. package/src/ipfs/types.ts +54 -0
  69. package/src/mint/mint-client.test.ts +96 -47
  70. package/src/mint/mint-client.ts +75 -343
  71. package/src/mint/mint-queries.ts +320 -0
  72. package/src/mint/mint-transactions.ts +253 -0
  73. package/src/mint/subgraph-mint-getter.ts +216 -123
  74. package/src/mint/subgraph-queries.ts +170 -0
  75. package/src/mint/types.ts +140 -23
  76. package/src/mint/utils.ts +14 -0
  77. package/src/premint/conversions.ts +26 -2
  78. package/src/premint/premint-api-client.ts +48 -16
  79. package/src/premint/premint-client.test.ts +29 -23
  80. package/src/premint/premint-client.ts +73 -37
  81. package/src/premint/preminter.ts +2 -3
  82. package/src/sdk.ts +7 -4
  83. package/src/types.ts +18 -0
  84. package/src/utils.ts +29 -28
  85. package/test-integration/setup-test-contracts.ts +96 -0
  86. package/test-integration/premint-client.test.ts +0 -148
@@ -5,39 +5,23 @@ import {
5
5
  } from "../apis/http-api-base";
6
6
  import { NetworkConfig, networkConfigByChain } from "src/apis/chain-constants";
7
7
  import { GenericTokenIdTypes } from "src/types";
8
- import { IMintGetter, SalesConfigAndTokenInfo, SaleType } from "./types";
9
- import { NFT_SALE_QUERY } from "src/constants";
10
-
11
- type FixedPriceSaleStrategyResult = {
12
- address: Address;
13
- pricePerToken: string;
14
- saleEnd: string;
15
- saleStart: string;
16
- maxTokensPerAddress: string;
17
- };
18
-
19
- type ERC20SaleStrategyResult = FixedPriceSaleStrategyResult & {
20
- currency: Address;
21
- };
22
-
23
- type SalesStrategyResult =
24
- | {
25
- type: "FIXED_PRICE";
26
- fixedPrice: FixedPriceSaleStrategyResult;
27
- }
28
- | {
29
- type: "ERC_20_MINTER";
30
- erc20Minter: ERC20SaleStrategyResult;
31
- };
32
-
33
- type TokenQueryResult = {
34
- tokenId?: string;
35
- salesStrategies?: SalesStrategyResult[];
36
- contract: {
37
- mintFeePerQuantity: "string";
38
- salesStrategies: SalesStrategyResult[];
39
- };
40
- };
8
+ import {
9
+ IOnchainMintGetter,
10
+ SaleType,
11
+ OnchainMintable,
12
+ OnchainSalesConfigAndTokenInfo,
13
+ OnchainSalesStrategies,
14
+ isErc20SaleStrategy,
15
+ } from "./types";
16
+ import { querySubgraphWithRetries } from "src/utils";
17
+ import {
18
+ buildContractTokensQuery,
19
+ buildNftTokenSalesQuery,
20
+ buildPremintsOfContractQuery,
21
+ ISubgraphQuery,
22
+ SalesStrategyResult,
23
+ TokenQueryResult,
24
+ } from "./subgraph-queries";
41
25
 
42
26
  export const getApiNetworkConfigForChain = (chainId: number): NetworkConfig => {
43
27
  if (!networkConfigByChain[chainId]) {
@@ -46,7 +30,83 @@ export const getApiNetworkConfigForChain = (chainId: number): NetworkConfig => {
46
30
  return networkConfigByChain[chainId]!;
47
31
  };
48
32
 
49
- export class SubgraphMintGetter implements IMintGetter {
33
+ function parseSalesConfig(
34
+ targetStrategy: SalesStrategyResult,
35
+ ): OnchainSalesStrategies {
36
+ if (targetStrategy.type === "FIXED_PRICE")
37
+ return {
38
+ saleType: "fixedPrice",
39
+ ...targetStrategy.fixedPrice,
40
+ maxTokensPerAddress: BigInt(
41
+ targetStrategy.fixedPrice.maxTokensPerAddress,
42
+ ),
43
+ pricePerToken: BigInt(targetStrategy.fixedPrice.pricePerToken),
44
+ };
45
+
46
+ if (targetStrategy.type === "ERC_20_MINTER") {
47
+ return {
48
+ saleType: "erc20",
49
+ ...targetStrategy.erc20Minter,
50
+ maxTokensPerAddress: BigInt(
51
+ targetStrategy.erc20Minter.maxTokensPerAddress,
52
+ ),
53
+ pricePerToken: BigInt(targetStrategy.erc20Minter.pricePerToken),
54
+ };
55
+ }
56
+
57
+ throw new Error("Unknown saleType");
58
+ }
59
+
60
+ function getSaleEnd(a: SalesStrategyResult) {
61
+ return BigInt(
62
+ a.type === "ERC_20_MINTER" ? a.erc20Minter.saleEnd : a.fixedPrice.saleEnd,
63
+ );
64
+ }
65
+
66
+ function getTargetStrategy({
67
+ tokenId,
68
+ preferredSaleType,
69
+ token,
70
+ }: {
71
+ tokenId?: GenericTokenIdTypes;
72
+ preferredSaleType?: SaleType;
73
+ token: TokenQueryResult;
74
+ }) {
75
+ const allStrategies =
76
+ (typeof tokenId !== "undefined"
77
+ ? token.salesStrategies
78
+ : token.contract.salesStrategies) || [];
79
+
80
+ const saleStrategies = allStrategies.sort((a, b) =>
81
+ getSaleEnd(a) > getSaleEnd(b) ? 1 : -1,
82
+ );
83
+
84
+ let targetStrategy: SalesStrategyResult | undefined;
85
+
86
+ if (!preferredSaleType) {
87
+ targetStrategy = saleStrategies[0];
88
+ if (!targetStrategy) {
89
+ throw new Error("Cannot find sale strategy");
90
+ }
91
+ } else {
92
+ const mappedSaleType =
93
+ preferredSaleType === "erc20" ? "ERC_20_MINTER" : "FIXED_PRICE";
94
+ targetStrategy = saleStrategies.find(
95
+ (strategy: SalesStrategyResult) => strategy.type === mappedSaleType,
96
+ );
97
+ if (!targetStrategy) {
98
+ const targetStrategy = saleStrategies.find(
99
+ (strategy: SalesStrategyResult) =>
100
+ strategy.type === "FIXED_PRICE" || strategy.type === "ERC_20_MINTER",
101
+ );
102
+ if (!targetStrategy) throw new Error("Cannot find valid sale strategy");
103
+ return targetStrategy;
104
+ }
105
+ }
106
+
107
+ return targetStrategy;
108
+ }
109
+ export class SubgraphMintGetter implements IOnchainMintGetter {
50
110
  httpClient: IHttpClient;
51
111
  networkConfig: NetworkConfig;
52
112
 
@@ -55,100 +115,133 @@ export class SubgraphMintGetter implements IMintGetter {
55
115
  this.networkConfig = getApiNetworkConfigForChain(chainId);
56
116
  }
57
117
 
58
- async getSalesConfigAndTokenInfo({
118
+ async querySubgraphWithRetries<T>({
119
+ query,
120
+ variables,
121
+ parseResponseData,
122
+ }: ISubgraphQuery<T>) {
123
+ const responseData = await querySubgraphWithRetries({
124
+ httpClient: this.httpClient,
125
+ subgraphUrl: this.networkConfig.subgraphUrl,
126
+ query,
127
+ variables,
128
+ });
129
+
130
+ return parseResponseData(responseData);
131
+ }
132
+
133
+ getMintable: IOnchainMintGetter["getMintable"] = async ({
59
134
  tokenAddress,
60
135
  tokenId,
61
- saleType,
136
+ preferredSaleType: saleType,
137
+ }) => {
138
+ const token = await this.querySubgraphWithRetries(
139
+ buildNftTokenSalesQuery({
140
+ tokenId,
141
+ tokenAddress,
142
+ }),
143
+ );
144
+
145
+ if (!token) {
146
+ throw new Error("Cannot find token");
147
+ }
148
+
149
+ return parseTokenQueryResult({
150
+ token,
151
+ tokenId,
152
+ preferredSaleType: saleType,
153
+ });
154
+ };
155
+
156
+ async getContractMintable({
157
+ tokenAddress,
158
+ preferredSaleType,
62
159
  }: {
63
160
  tokenAddress: Address;
64
- tokenId?: GenericTokenIdTypes;
65
- saleType?: SaleType;
66
- }): Promise<SalesConfigAndTokenInfo> {
67
- const { retries, post } = this.httpClient;
68
- return retries(async () => {
69
- const response = await post<any>(this.networkConfig.subgraphUrl, {
70
- query: NFT_SALE_QUERY,
71
- variables: {
72
- id:
73
- tokenId !== undefined
74
- ? // Generic Token ID types all stringify down to the base numeric equivalent.
75
- `${tokenAddress.toLowerCase()}-${tokenId}`
76
- : `${tokenAddress.toLowerCase()}-0`,
77
- },
78
- });
79
-
80
- const token = response.data?.zoraCreateToken as TokenQueryResult;
81
-
82
- if (!token) {
83
- throw new Error("Cannot find a token to mint");
84
- }
85
-
86
- const allStrategies =
87
- (typeof tokenId !== "undefined"
88
- ? token.salesStrategies
89
- : token.contract.salesStrategies) || [];
90
-
91
- const saleStrategies = allStrategies.sort((a, b) =>
92
- BigInt(
93
- a.type === "ERC_20_MINTER"
94
- ? a.erc20Minter.saleEnd
95
- : a.fixedPrice.saleEnd,
96
- ) >
97
- BigInt(
98
- b.type === "FIXED_PRICE"
99
- ? b.fixedPrice.saleEnd
100
- : b.erc20Minter.saleEnd,
101
- )
102
- ? 1
103
- : -1,
161
+ preferredSaleType?: SaleType;
162
+ }) {
163
+ const tokens = await this.querySubgraphWithRetries(
164
+ buildContractTokensQuery({
165
+ tokenAddress,
166
+ }),
167
+ );
168
+
169
+ if (!tokens) return [];
170
+
171
+ return tokens
172
+ .filter((x) => x.tokenId !== "0")
173
+ .map((token) =>
174
+ parseTokenQueryResult({
175
+ token,
176
+ tokenId: token.tokenId,
177
+ preferredSaleType,
178
+ }),
104
179
  );
180
+ }
105
181
 
106
- let targetStrategy: SalesStrategyResult | undefined;
107
-
108
- if (!saleType) {
109
- targetStrategy = saleStrategies[0];
110
- if (!targetStrategy) {
111
- throw new Error("Cannot find sale strategy");
112
- }
113
- } else {
114
- const mappedSaleType =
115
- saleType === "erc20" ? "ERC_20_MINTER" : "FIXED_PRICE";
116
- targetStrategy = saleStrategies.find(
117
- (strategy: SalesStrategyResult) => strategy.type === mappedSaleType,
118
- );
119
- if (!targetStrategy) {
120
- throw new Error(`Cannot find sale strategy for ${mappedSaleType}`);
121
- }
122
- }
123
-
124
- if (targetStrategy.type === "FIXED_PRICE") {
125
- return {
126
- salesConfig: {
127
- saleType: "fixedPrice",
128
- ...targetStrategy.fixedPrice,
129
- maxTokensPerAddress: BigInt(
130
- targetStrategy.fixedPrice.maxTokensPerAddress,
131
- ),
132
- pricePerToken: BigInt(targetStrategy.fixedPrice.pricePerToken),
133
- },
134
- mintFeePerQuantity: BigInt(token.contract.mintFeePerQuantity),
135
- };
136
- }
137
- if (targetStrategy.type === "ERC_20_MINTER") {
138
- return {
139
- salesConfig: {
140
- saleType: "erc20",
141
- ...targetStrategy.erc20Minter,
142
- maxTokensPerAddress: BigInt(
143
- targetStrategy.erc20Minter.maxTokensPerAddress,
144
- ),
145
- pricePerToken: BigInt(targetStrategy.erc20Minter.pricePerToken),
146
- },
147
- mintFeePerQuantity: BigInt(token.contract.mintFeePerQuantity),
148
- };
149
- }
150
-
151
- throw new Error("Invalid saleType");
152
- });
182
+ async getContractPremintTokenIds({
183
+ tokenAddress,
184
+ }: {
185
+ tokenAddress: Address;
186
+ }) {
187
+ const premints = await this.querySubgraphWithRetries(
188
+ buildPremintsOfContractQuery({
189
+ tokenAddress,
190
+ }),
191
+ );
192
+
193
+ return (
194
+ premints?.map((premint) => ({
195
+ tokenId: BigInt(premint.tokenId),
196
+ uid: +premint.uid,
197
+ })) || []
198
+ );
153
199
  }
154
200
  }
201
+
202
+ function parseTokenQueryResult({
203
+ token,
204
+ tokenId,
205
+ preferredSaleType,
206
+ }: {
207
+ token: TokenQueryResult;
208
+ tokenId?: GenericTokenIdTypes;
209
+ preferredSaleType?: SaleType;
210
+ }): OnchainSalesConfigAndTokenInfo {
211
+ const targetStrategy = getTargetStrategy({
212
+ tokenId,
213
+ preferredSaleType: preferredSaleType,
214
+ token,
215
+ });
216
+
217
+ const tokenInfo = parseTokenInfo(token);
218
+
219
+ const salesConfig = parseSalesConfig(targetStrategy);
220
+
221
+ if (isErc20SaleStrategy(salesConfig)) {
222
+ tokenInfo.mintFeePerQuantity = 0n;
223
+ }
224
+
225
+ return {
226
+ ...tokenInfo,
227
+ salesConfig,
228
+ };
229
+ }
230
+
231
+ function parseTokenInfo(token: TokenQueryResult): OnchainMintable {
232
+ return {
233
+ contract: {
234
+ address: token.contract.address,
235
+ name: token.contract.name,
236
+ URI: token.contract.contractURI,
237
+ },
238
+ tokenURI: token.uri,
239
+ tokenId: token.tokenId ? BigInt(token.tokenId) : undefined,
240
+ mintType: token.tokenStandard === "ERC721" ? "721" : "1155",
241
+ creator: token.creator,
242
+ totalMinted: BigInt(token.totalMinted),
243
+ maxSupply: BigInt(token.maxSupply),
244
+ mintFeePerQuantity: BigInt(token.contract.mintFeePerQuantity),
245
+ contractVersion: token.contract.contractVersion,
246
+ };
247
+ }
@@ -0,0 +1,170 @@
1
+ import { GenericTokenIdTypes } from "src/types";
2
+ import { Address } from "viem";
3
+
4
+ export type FixedPriceSaleStrategyResult = {
5
+ address: Address;
6
+ pricePerToken: string;
7
+ saleEnd: string;
8
+ saleStart: string;
9
+ maxTokensPerAddress: string;
10
+ };
11
+
12
+ export type ERC20SaleStrategyResult = FixedPriceSaleStrategyResult & {
13
+ currency: Address;
14
+ };
15
+
16
+ export type SalesStrategyResult =
17
+ | {
18
+ type: "FIXED_PRICE";
19
+ fixedPrice: FixedPriceSaleStrategyResult;
20
+ }
21
+ | {
22
+ type: "ERC_20_MINTER";
23
+ erc20Minter: ERC20SaleStrategyResult;
24
+ };
25
+
26
+ export type TokenQueryResult = {
27
+ tokenId?: string;
28
+ creator: Address;
29
+ uri: string;
30
+ totalMinted: string;
31
+ maxSupply: string;
32
+ salesStrategies?: SalesStrategyResult[];
33
+ tokenStandard: "ERC1155" | "ERC721";
34
+ contract: {
35
+ mintFeePerQuantity: "string";
36
+ salesStrategies: SalesStrategyResult[];
37
+ address: Address;
38
+ contractVersion: string;
39
+ contractURI: string;
40
+ name: string;
41
+ };
42
+ };
43
+
44
+ const NFT_SALE_STRATEGY_FRAGMENT = `
45
+ fragment SaleStrategy on SalesStrategyConfig {
46
+ type
47
+ fixedPrice {
48
+ address
49
+ pricePerToken
50
+ saleEnd
51
+ saleStart
52
+ maxTokensPerAddress
53
+ }
54
+ erc20Minter {
55
+ address
56
+ pricePerToken
57
+ currency
58
+ saleEnd
59
+ saleStart
60
+ maxTokensPerAddress
61
+ }
62
+ }`;
63
+
64
+ const TOKEN_FRAGMENT = `
65
+ fragment Token on ZoraCreateToken {
66
+ creator
67
+ tokenId
68
+ uri
69
+ totalMinted
70
+ maxSupply
71
+ tokenStandard
72
+ salesStrategies(where: {type_in: ["FIXED_PRICE", "ERC_20_MINTER"]}) {
73
+ ...SaleStrategy
74
+ }
75
+ contract {
76
+ address
77
+ mintFeePerQuantity
78
+ contractVersion
79
+ contractURI
80
+ name
81
+ salesStrategies(where: {type_in: ["FIXED_PRICE", "ERC_20_MINTER"]}) {
82
+ ...SaleStrategy
83
+ }
84
+ }
85
+ }`;
86
+
87
+ const FRAGMENTS = `
88
+ ${NFT_SALE_STRATEGY_FRAGMENT}
89
+ ${TOKEN_FRAGMENT}
90
+ `;
91
+
92
+ export function buildNftTokenSalesQuery({
93
+ tokenId,
94
+ tokenAddress,
95
+ }: {
96
+ tokenId?: GenericTokenIdTypes;
97
+ tokenAddress: Address;
98
+ }): ISubgraphQuery<TokenQueryResult> {
99
+ return {
100
+ query: `
101
+ ${FRAGMENTS}
102
+ query ($id: ID!) {
103
+ zoraCreateToken(id: $id) {
104
+ ...Token
105
+ }
106
+ }
107
+ `,
108
+ variables: {
109
+ id:
110
+ tokenId !== undefined
111
+ ? // Generic Token ID types all stringify down to the base numeric equivalent.
112
+ `${tokenAddress.toLowerCase()}-${tokenId}`
113
+ : `${tokenAddress.toLowerCase()}-0`,
114
+ },
115
+ parseResponseData: (responseData: any | undefined) =>
116
+ responseData?.zoraCreateToken,
117
+ };
118
+ }
119
+
120
+ export function buildContractTokensQuery({
121
+ tokenAddress,
122
+ }: {
123
+ tokenAddress: Address;
124
+ }): ISubgraphQuery<TokenQueryResult[]> {
125
+ return {
126
+ query: `
127
+ ${FRAGMENTS}
128
+ query ($contract: Bytes!) {
129
+ zoraCreateTokens(
130
+ where: {address: $contract}
131
+ ) {
132
+ ...Token
133
+ }
134
+ }
135
+ `,
136
+ variables: {
137
+ contract: tokenAddress.toLowerCase(),
138
+ },
139
+ parseResponseData: (responseData: any | undefined) =>
140
+ responseData?.zoraCreateTokens,
141
+ };
142
+ }
143
+
144
+ export type ISubgraphQuery<T> = {
145
+ query: string;
146
+ variables: Record<string, any>;
147
+ parseResponseData: (data: any | undefined) => T | undefined;
148
+ };
149
+
150
+ export function buildPremintsOfContractQuery({
151
+ tokenAddress,
152
+ }: {
153
+ tokenAddress: Address;
154
+ }): ISubgraphQuery<{ uid: string; tokenId: string }[]> {
155
+ return {
156
+ query: `
157
+ query ($contractAddress: Bytes!) {
158
+ premints(where:{contractAddress:$contractAddress}) {
159
+ uid
160
+ tokenId
161
+ }
162
+ }
163
+ `,
164
+ variables: {
165
+ contractAddress: tokenAddress.toLowerCase(),
166
+ },
167
+ parseResponseData: (responseData: any | undefined) =>
168
+ responseData?.premints,
169
+ };
170
+ }