@zoralabs/coins-sdk 0.2.10 → 0.3.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 (44) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/actions/createCoin.d.ts +52 -21
  3. package/dist/actions/createCoin.d.ts.map +1 -1
  4. package/dist/api/create.d.ts +8 -0
  5. package/dist/api/create.d.ts.map +1 -0
  6. package/dist/api/index.d.ts +4 -0
  7. package/dist/api/index.d.ts.map +1 -1
  8. package/dist/api/pool-config.d.ts +5 -0
  9. package/dist/api/pool-config.d.ts.map +1 -0
  10. package/dist/api/queries.d.ts +9 -1
  11. package/dist/api/queries.d.ts.map +1 -1
  12. package/dist/client/sdk.gen.d.ts +307 -46
  13. package/dist/client/sdk.gen.d.ts.map +1 -1
  14. package/dist/client/types.gen.d.ts +874 -93
  15. package/dist/client/types.gen.d.ts.map +1 -1
  16. package/dist/index.cjs +419 -424
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.d.ts +2 -3
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +413 -418
  21. package/dist/index.js.map +1 -1
  22. package/dist/uploader/metadata.d.ts.map +1 -1
  23. package/dist/uploader/types.d.ts +4 -1
  24. package/dist/uploader/types.d.ts.map +1 -1
  25. package/dist/utils/rethrowDecodedRevert.d.ts +3 -0
  26. package/dist/utils/rethrowDecodedRevert.d.ts.map +1 -0
  27. package/package.json +1 -1
  28. package/src/actions/createCoin.ts +154 -112
  29. package/src/api/create.ts +24 -0
  30. package/src/api/index.ts +8 -0
  31. package/src/api/pool-config.ts +17 -0
  32. package/src/api/queries.ts +36 -0
  33. package/src/client/sdk.gen.ts +98 -0
  34. package/src/client/types.gen.ts +898 -93
  35. package/src/index.ts +4 -6
  36. package/src/uploader/metadata.ts +4 -1
  37. package/src/uploader/types.ts +4 -1
  38. package/src/utils/rethrowDecodedRevert.ts +50 -0
  39. package/dist/actions/getOnchainCoinDetails.d.ts +0 -32
  40. package/dist/actions/getOnchainCoinDetails.d.ts.map +0 -1
  41. package/dist/utils/getPrepurchaseHook.d.ts +0 -16
  42. package/dist/utils/getPrepurchaseHook.d.ts.map +0 -1
  43. package/src/actions/getOnchainCoinDetails.ts +0 -68
  44. package/src/utils/getPrepurchaseHook.ts +0 -59
@@ -1,138 +1,113 @@
1
- import { coinFactoryABI as zoraFactoryImplABI } from "@zoralabs/protocol-deployments";
1
+ import {
2
+ coinFactoryAddress,
3
+ coinFactoryABI as zoraFactoryImplABI,
4
+ } from "@zoralabs/protocol-deployments";
2
5
  import {
3
6
  Address,
4
7
  TransactionReceipt,
5
8
  WalletClient,
6
- SimulateContractParameters,
7
9
  ContractEventArgsFromTopics,
8
10
  parseEventLogs,
9
- zeroAddress,
10
- keccak256,
11
- toBytes,
12
11
  Hex,
13
12
  Account,
13
+ isAddressEqual,
14
14
  } from "viem";
15
- import { base, baseSepolia } from "viem/chains";
16
- import { COIN_FACTORY_ADDRESS } from "../constants";
15
+ import { base } from "viem/chains";
17
16
  import { validateClientNetwork } from "../utils/validateClientNetwork";
18
17
  import { GenericPublicClient } from "../utils/genericPublicClient";
19
18
  import { validateMetadataURIContent } from "../metadata";
20
19
  import { ValidMetadataURI } from "../uploader/types";
21
- import { getAttribution } from "../utils/attribution";
22
- import {
23
- COIN_ETH_PAIR_POOL_CONFIG,
24
- COIN_ZORA_PAIR_POOL_CONFIG,
25
- } from "../utils/poolConfigUtils";
26
- import { getPrepurchaseHook } from "../utils/getPrepurchaseHook";
27
20
  import { getChainFromId } from "../utils/getChainFromId";
21
+ import { postCreateContent } from "../api";
22
+ import { rethrowDecodedRevert } from "../utils/rethrowDecodedRevert";
28
23
 
29
24
  export type CoinDeploymentLogArgs = ContractEventArgsFromTopics<
30
25
  typeof zoraFactoryImplABI,
31
26
  "CoinCreatedV4"
32
27
  >;
33
28
 
34
- export enum DeployCurrency {
35
- ZORA = 1,
36
- ETH = 2,
37
- }
29
+ const STARTING_MARKET_CAPS = {
30
+ LOW: "LOW",
31
+ HIGH: "HIGH",
32
+ } as const;
33
+ export type StartingMarketCap = keyof typeof STARTING_MARKET_CAPS;
38
34
 
39
- export enum InitialPurchaseCurrency {
40
- ETH = 1,
41
- // TODO: Add USDC and ZORA support with signature approvals
35
+ export interface RawUriMetadata {
36
+ type: "RAW_URI";
37
+ uri: string;
42
38
  }
43
39
 
40
+ const CONTENT_COIN_CURRENCIES = {
41
+ CREATOR_COIN: "CREATOR_COIN",
42
+ ZORA: "ZORA",
43
+ ETH: "ETH",
44
+ CREATOR_COIN_OR_ZORA: "CREATOR_COIN_OR_ZORA",
45
+ } as const;
46
+ export type ContentCoinCurrency = keyof typeof CONTENT_COIN_CURRENCIES;
47
+
48
+ export const CreateConstants = {
49
+ StartingMarketCaps: STARTING_MARKET_CAPS,
50
+ ContentCoinCurrencies: CONTENT_COIN_CURRENCIES,
51
+ } as const;
52
+
44
53
  export type CreateCoinArgs = {
54
+ creator: string;
45
55
  name: string;
46
56
  symbol: string;
47
- uri: ValidMetadataURI;
57
+ metadata: RawUriMetadata;
58
+ currency: ContentCoinCurrency;
48
59
  chainId?: number;
49
- owners?: Address[];
50
- payoutRecipient: Address;
51
- platformReferrer?: Address;
52
- currency?: DeployCurrency;
53
- initialPurchase?: {
54
- currency: InitialPurchaseCurrency;
55
- amount: bigint;
56
- };
60
+ startingMarketCap?: StartingMarketCap;
61
+ platformReferrer?: string;
62
+ additionalOwners?: Address[];
63
+ payoutRecipientOverride?: Address;
64
+ skipMetadataValidation?: boolean;
57
65
  };
58
66
 
59
- function getPoolConfig(currency: DeployCurrency, chainId: number) {
60
- if (currency === DeployCurrency.ZORA && chainId == baseSepolia.id) {
61
- throw new Error("ZORA is not supported on Base Sepolia");
62
- }
63
-
64
- switch (currency) {
65
- case DeployCurrency.ZORA:
66
- return COIN_ZORA_PAIR_POOL_CONFIG[
67
- chainId as keyof typeof COIN_ZORA_PAIR_POOL_CONFIG
68
- ];
69
- case DeployCurrency.ETH:
70
- return COIN_ETH_PAIR_POOL_CONFIG[
71
- chainId as keyof typeof COIN_ETH_PAIR_POOL_CONFIG
72
- ];
73
- default:
74
- throw new Error("Invalid currency");
75
- }
76
- }
67
+ type TransactionParameters = {
68
+ to: Address;
69
+ data: Hex;
70
+ value: bigint;
71
+ };
77
72
 
78
73
  export async function createCoinCall({
74
+ creator,
79
75
  name,
80
76
  symbol,
81
- uri,
82
- owners,
83
- payoutRecipient,
77
+ metadata,
84
78
  currency,
85
79
  chainId = base.id,
86
- platformReferrer = "0x0000000000000000000000000000000000000000",
87
- initialPurchase,
88
- }: CreateCoinArgs): Promise<
89
- SimulateContractParameters<typeof zoraFactoryImplABI, "deploy">
90
- > {
91
- if (!owners) {
92
- owners = [payoutRecipient];
93
- }
94
-
95
- if (!currency) {
96
- currency = chainId !== base.id ? DeployCurrency.ETH : DeployCurrency.ZORA;
80
+ payoutRecipientOverride,
81
+ additionalOwners,
82
+ platformReferrer,
83
+ skipMetadataValidation = false,
84
+ }: CreateCoinArgs): Promise<TransactionParameters[]> {
85
+ // Validate metadata URI
86
+ if (!skipMetadataValidation) {
87
+ await validateMetadataURIContent(metadata.uri as ValidMetadataURI);
97
88
  }
98
89
 
99
- const poolConfig = getPoolConfig(currency, chainId);
100
-
101
- // This will throw an error if the metadata is not valid
102
- await validateMetadataURIContent(uri);
90
+ const createContentRequest = await postCreateContent({
91
+ currency,
92
+ chainId,
93
+ metadata,
94
+ creator,
95
+ name,
96
+ symbol,
97
+ platformReferrer,
98
+ additionalOwners,
99
+ payoutRecipientOverride,
100
+ });
103
101
 
104
- let deployHook = {
105
- hook: zeroAddress as Address,
106
- hookData: "0x" as Hex,
107
- value: 0n,
108
- };
109
- if (initialPurchase) {
110
- deployHook = await getPrepurchaseHook({
111
- initialPurchase,
112
- payoutRecipient,
113
- chainId,
114
- });
102
+ if (!createContentRequest.data?.calls) {
103
+ throw new Error("Failed to create content calldata");
115
104
  }
116
105
 
117
- return {
118
- abi: zoraFactoryImplABI,
119
- functionName: "deploy",
120
- address: COIN_FACTORY_ADDRESS,
121
- args: [
122
- payoutRecipient,
123
- owners,
124
- uri,
125
- name,
126
- symbol,
127
- poolConfig,
128
- platformReferrer,
129
- deployHook.hook,
130
- deployHook.hookData,
131
- keccak256(toBytes(Math.random().toString())), // coinSalt
132
- ],
133
- value: deployHook.value,
134
- dataSuffix: getAttribution(),
135
- } as const;
106
+ return createContentRequest.data.calls.map((data) => ({
107
+ to: data.to as Address,
108
+ data: data.data as Hex,
109
+ value: BigInt(data.value),
110
+ }));
136
111
  }
137
112
 
138
113
  /**
@@ -152,30 +127,97 @@ export function getCoinCreateFromLogs(
152
127
  }
153
128
 
154
129
  // Update createCoin to return both receipt and coin address
155
- export async function createCoin(
156
- call: CreateCoinArgs,
157
- walletClient: WalletClient,
158
- publicClient: GenericPublicClient,
130
+ export async function createCoin({
131
+ call,
132
+ walletClient,
133
+ publicClient,
134
+ options,
135
+ }: {
136
+ call: CreateCoinArgs;
137
+ walletClient: WalletClient;
138
+ publicClient: GenericPublicClient;
159
139
  options?: {
160
140
  gasMultiplier?: number;
161
141
  account?: Account | Address;
162
- },
163
- ) {
142
+ skipValidateTransaction?: boolean;
143
+ };
144
+ }) {
164
145
  validateClientNetwork(publicClient);
165
146
 
166
- const createCoinRequest = await createCoinCall(call);
167
- const { request } = await publicClient.simulateContract({
168
- ...createCoinRequest,
169
- account: options?.account ?? walletClient.account,
170
- });
147
+ const callRequest = await createCoinCall(call);
171
148
 
172
- // Add a 2/5th buffer on gas.
173
- if (request.gas) {
174
- // Gas limit multiplier is a percentage argument.
175
- request.gas = (request.gas * BigInt(options?.gasMultiplier ?? 100)) / 100n;
149
+ if (callRequest.length !== 1) {
150
+ throw new Error("Only one call is supported for this SDK version");
176
151
  }
177
- const hash = await walletClient.writeContract(request);
178
- const receipt = await publicClient.waitForTransactionReceipt({ hash });
152
+
153
+ const createContentCall = callRequest[0];
154
+
155
+ if (!createContentCall) {
156
+ throw new Error("Failed to load create content calldata from API");
157
+ }
158
+
159
+ const coinFactoryAddressForChain =
160
+ coinFactoryAddress[call.chainId as keyof typeof coinFactoryAddress];
161
+
162
+ // Sanity check that the call is for the correct factory contract
163
+ if (!isAddressEqual(createContentCall.to, coinFactoryAddressForChain)) {
164
+ throw new Error("Creator coin is not supported for this SDK version");
165
+ }
166
+
167
+ // Sanity check to ensure no buy orders are sent with there parameters
168
+ if (createContentCall.value !== 0n) {
169
+ throw new Error(
170
+ "Creator coin and purchase is not supported for this SDK version.",
171
+ );
172
+ }
173
+
174
+ // Prefer a LocalAccount from the wallet client when available to ensure
175
+ // offline signing (eth_sendRawTransaction) instead of wallet_sendTransaction
176
+ // which can error when a `from` field is present.
177
+ const selectedAccount =
178
+ (typeof options?.account === "string" ? undefined : options?.account) ??
179
+ walletClient.account;
180
+
181
+ if (!selectedAccount) {
182
+ throw new Error("Account is required");
183
+ }
184
+
185
+ const viemCall = {
186
+ ...createContentCall,
187
+ account: selectedAccount,
188
+ };
189
+
190
+ // simulate call
191
+ if (!options?.skipValidateTransaction) {
192
+ try {
193
+ await publicClient.call(viemCall);
194
+ } catch (err) {
195
+ rethrowDecodedRevert(err, zoraFactoryImplABI);
196
+ }
197
+ }
198
+
199
+ const gasEstimate = options?.skipValidateTransaction
200
+ ? 10_000_000n
201
+ : await publicClient.estimateGas(viemCall);
202
+ const gasPrice = await publicClient.getGasPrice();
203
+
204
+ const hash = await (async () => {
205
+ try {
206
+ return await walletClient.sendTransaction({
207
+ ...viemCall,
208
+ gasPrice,
209
+ gas: gasEstimate,
210
+ chain: publicClient.chain,
211
+ });
212
+ } catch (err) {
213
+ rethrowDecodedRevert(err, zoraFactoryImplABI);
214
+ }
215
+ })();
216
+
217
+ const receipt = await publicClient.waitForTransactionReceipt({
218
+ hash,
219
+ });
220
+
179
221
  const deployment = getCoinCreateFromLogs(receipt);
180
222
 
181
223
  return {
@@ -0,0 +1,24 @@
1
+ import {
2
+ PostCreateContentData,
3
+ PostCreateContentResponse,
4
+ } from "../client/types.gen";
5
+ import { postCreateContent as postCreateContentSDK } from "../client/sdk.gen";
6
+ import { getApiKeyMeta } from "./api-key";
7
+ import { RequestOptionsType } from "./query-types";
8
+ import { RequestResult } from "@hey-api/client-fetch";
9
+
10
+ type PostCreateContentQuery = PostCreateContentData["body"];
11
+ export type { PostCreateContentQuery, PostCreateContentResponse };
12
+
13
+ export type CoinCreateData = NonNullable<PostCreateContentResponse>;
14
+
15
+ export const postCreateContent = async (
16
+ body: PostCreateContentQuery,
17
+ options?: RequestOptionsType<PostCreateContentData>,
18
+ ): Promise<RequestResult<PostCreateContentResponse>> => {
19
+ return await postCreateContentSDK({
20
+ ...options,
21
+ body,
22
+ ...getApiKeyMeta(),
23
+ });
24
+ };
package/src/api/index.ts CHANGED
@@ -6,6 +6,14 @@ export type * from "./explore";
6
6
  export * from "./queries";
7
7
  export type * from "./queries";
8
8
 
9
+ // Export all of the pool config queries
10
+ export * from "./pool-config";
11
+ export type * from "./pool-config";
12
+
13
+ // Export all of the create queries
14
+ export * from "./create";
15
+ export type * from "./create";
16
+
9
17
  // Only export the set function for external use.
10
18
  // All other exports are for internal use.
11
19
  export { setApiKey } from "./api-key";
@@ -0,0 +1,17 @@
1
+ import {
2
+ getCreateContentPoolConfig as getCreateContentPoolConfigSDK,
3
+ GetCreateContentPoolConfigData,
4
+ GetCreateContentPoolConfigResponse,
5
+ } from "../client";
6
+ import { RequestOptionsType } from "./query-types";
7
+ import { RequestResult } from "@hey-api/client-fetch";
8
+
9
+ export const getCreateContentPoolConfig = async (
10
+ query: GetCreateContentPoolConfigData["query"],
11
+ options?: RequestOptionsType<GetCreateContentPoolConfigData>,
12
+ ): Promise<RequestResult<GetCreateContentPoolConfigResponse>> => {
13
+ return getCreateContentPoolConfigSDK({
14
+ query,
15
+ ...options,
16
+ });
17
+ };
@@ -2,9 +2,13 @@ import {
2
2
  GetCoinCommentsData,
3
3
  GetCoinCommentsResponse,
4
4
  GetCoinData,
5
+ GetCoinHoldersData,
6
+ GetCoinHoldersResponse,
5
7
  GetCoinResponse,
6
8
  GetCoinsData,
7
9
  GetCoinsResponse,
10
+ GetCoinSwapsData,
11
+ GetCoinSwapsResponse,
8
12
  GetProfileBalancesData,
9
13
  GetProfileBalancesResponse,
10
14
  GetProfileCoinsData,
@@ -16,6 +20,8 @@ import {
16
20
  getCoin as getCoinSDK,
17
21
  getCoins as getCoinsSDK,
18
22
  getCoinComments as getCoinCommentsSDK,
23
+ getCoinHolders as getCoinHoldersSDK,
24
+ getCoinSwaps as getCoinSwapsSDK,
19
25
  getProfile as getProfileSDK,
20
26
  getProfileBalances as getProfileBalancesSDK,
21
27
  getProfileCoins as getProfileCoinsSDK,
@@ -60,6 +66,36 @@ export const getCoins = async (
60
66
  });
61
67
  };
62
68
 
69
+ type GetCoinHoldersQuery = GetCoinHoldersData["query"];
70
+ export type { GetCoinHoldersQuery, GetCoinHoldersData };
71
+ export type { GetCoinHoldersResponse } from "../client/types.gen";
72
+
73
+ export const getCoinHolders = async (
74
+ query: GetCoinHoldersQuery,
75
+ options?: RequestOptionsType<GetCoinHoldersData>,
76
+ ): Promise<RequestResult<GetCoinHoldersResponse>> => {
77
+ return await getCoinHoldersSDK({
78
+ query,
79
+ ...getApiKeyMeta(),
80
+ ...options,
81
+ });
82
+ };
83
+
84
+ type GetCoinSwapsQuery = GetCoinSwapsData["query"];
85
+ export type { GetCoinSwapsQuery, GetCoinSwapsData };
86
+ export type { GetCoinSwapsResponse } from "../client/types.gen";
87
+
88
+ export const getCoinSwaps = async (
89
+ query: GetCoinSwapsQuery,
90
+ options?: RequestOptionsType<GetCoinSwapsData>,
91
+ ): Promise<RequestResult<GetCoinSwapsResponse>> => {
92
+ return await getCoinSwapsSDK({
93
+ query,
94
+ ...getApiKeyMeta(),
95
+ ...options,
96
+ });
97
+ };
98
+
63
99
  type GetCoinCommentsQuery = GetCoinCommentsData["query"];
64
100
  export type { GetCoinCommentsQuery, GetCoinCommentsData };
65
101
  export type { GetCoinCommentsResponse } from "../client/types.gen";
@@ -12,6 +12,10 @@ import type {
12
12
  GetCoinResponse,
13
13
  GetCoinCommentsData,
14
14
  GetCoinCommentsResponse,
15
+ GetCoinHoldersData,
16
+ GetCoinHoldersResponse,
17
+ GetCoinSwapsData,
18
+ GetCoinSwapsResponse,
15
19
  GetCoinsData,
16
20
  GetCoinsResponse,
17
21
  SetCreateUploadJwtData,
@@ -27,6 +31,12 @@ import type {
27
31
  PostQuoteData,
28
32
  PostQuoteResponse,
29
33
  PostQuoteError,
34
+ GetCreateContentPoolConfigData,
35
+ GetCreateContentPoolConfigResponse,
36
+ GetCreateContentPoolConfigError,
37
+ PostCreateContentData,
38
+ PostCreateContentResponse,
39
+ PostCreateContentError,
30
40
  } from "./types.gen";
31
41
  import { client as _heyApiClient } from "./client.gen";
32
42
 
@@ -113,6 +123,50 @@ export const getCoinComments = <ThrowOnError extends boolean = false>(
113
123
  });
114
124
  };
115
125
 
126
+ /**
127
+ * zoraSDK_coinHolders query
128
+ */
129
+ export const getCoinHolders = <ThrowOnError extends boolean = false>(
130
+ options: Options<GetCoinHoldersData, ThrowOnError>,
131
+ ) => {
132
+ return (options.client ?? _heyApiClient).get<
133
+ GetCoinHoldersResponse,
134
+ unknown,
135
+ ThrowOnError
136
+ >({
137
+ security: [
138
+ {
139
+ name: "api-key",
140
+ type: "apiKey",
141
+ },
142
+ ],
143
+ url: "/coinHolders",
144
+ ...options,
145
+ });
146
+ };
147
+
148
+ /**
149
+ * zoraSDK_coinSwaps query
150
+ */
151
+ export const getCoinSwaps = <ThrowOnError extends boolean = false>(
152
+ options: Options<GetCoinSwapsData, ThrowOnError>,
153
+ ) => {
154
+ return (options.client ?? _heyApiClient).get<
155
+ GetCoinSwapsResponse,
156
+ unknown,
157
+ ThrowOnError
158
+ >({
159
+ security: [
160
+ {
161
+ name: "api-key",
162
+ type: "apiKey",
163
+ },
164
+ ],
165
+ url: "/coinSwaps",
166
+ ...options,
167
+ });
168
+ };
169
+
116
170
  /**
117
171
  * zoraSDK_coins query
118
172
  */
@@ -271,3 +325,47 @@ export const postQuote = <ThrowOnError extends boolean = false>(
271
325
  },
272
326
  });
273
327
  };
328
+
329
+ export const getCreateContentPoolConfig = <
330
+ ThrowOnError extends boolean = false,
331
+ >(
332
+ options?: Options<GetCreateContentPoolConfigData, ThrowOnError>,
333
+ ) => {
334
+ return (options?.client ?? _heyApiClient).get<
335
+ GetCreateContentPoolConfigResponse,
336
+ GetCreateContentPoolConfigError,
337
+ ThrowOnError
338
+ >({
339
+ security: [
340
+ {
341
+ name: "api-key",
342
+ type: "apiKey",
343
+ },
344
+ ],
345
+ url: "/create/content/pool-config",
346
+ ...options,
347
+ });
348
+ };
349
+
350
+ export const postCreateContent = <ThrowOnError extends boolean = false>(
351
+ options?: Options<PostCreateContentData, ThrowOnError>,
352
+ ) => {
353
+ return (options?.client ?? _heyApiClient).post<
354
+ PostCreateContentResponse,
355
+ PostCreateContentError,
356
+ ThrowOnError
357
+ >({
358
+ security: [
359
+ {
360
+ name: "api-key",
361
+ type: "apiKey",
362
+ },
363
+ ],
364
+ url: "/create/content",
365
+ ...options,
366
+ headers: {
367
+ "Content-Type": "application/json",
368
+ ...options?.headers,
369
+ },
370
+ });
371
+ };