@zoralabs/coins-sdk 0.2.4 → 0.2.6

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 (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/actions/createCoin.d.ts +1 -1
  3. package/dist/actions/createCoin.d.ts.map +1 -1
  4. package/dist/actions/tradeCoin.d.ts +52 -0
  5. package/dist/actions/tradeCoin.d.ts.map +1 -0
  6. package/dist/api/api-key.d.ts +2 -1
  7. package/dist/api/api-key.d.ts.map +1 -1
  8. package/dist/api/internal.d.ts +2 -2
  9. package/dist/api/internal.d.ts.map +1 -1
  10. package/dist/client/sdk.gen.d.ts +77 -1
  11. package/dist/client/sdk.gen.d.ts.map +1 -1
  12. package/dist/client/types.gen.d.ts +156 -0
  13. package/dist/client/types.gen.d.ts.map +1 -1
  14. package/dist/index.cjs +405 -1
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +404 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/metadata/cleanAndValidateMetadataURI.d.ts +1 -1
  21. package/dist/metadata/cleanAndValidateMetadataURI.d.ts.map +1 -1
  22. package/dist/metadata/index.d.ts +1 -1
  23. package/dist/metadata/index.d.ts.map +1 -1
  24. package/dist/metadata/validateMetadataURIContent.d.ts +1 -1
  25. package/dist/metadata/validateMetadataURIContent.d.ts.map +1 -1
  26. package/dist/uploader/index.d.ts +10 -0
  27. package/dist/uploader/index.d.ts.map +1 -0
  28. package/dist/uploader/metadata.d.ts +44 -0
  29. package/dist/uploader/metadata.d.ts.map +1 -0
  30. package/dist/uploader/providers/zora.d.ts +18 -0
  31. package/dist/uploader/providers/zora.d.ts.map +1 -0
  32. package/dist/uploader/types.d.ts +21 -0
  33. package/dist/uploader/types.d.ts.map +1 -0
  34. package/package.json +1 -1
  35. package/src/actions/createCoin.ts +2 -1
  36. package/src/actions/tradeCoin.ts +230 -0
  37. package/src/api/api-key.ts +5 -1
  38. package/src/api/internal.ts +3 -3
  39. package/src/client/sdk.gen.ts +26 -0
  40. package/src/client/types.gen.ts +161 -0
  41. package/src/index.ts +6 -0
  42. package/src/metadata/cleanAndValidateMetadataURI.ts +1 -5
  43. package/src/metadata/index.ts +1 -4
  44. package/src/metadata/validateMetadataURIContent.ts +2 -4
  45. package/src/uploader/index.ts +16 -0
  46. package/src/uploader/metadata.ts +214 -0
  47. package/src/uploader/providers/zora.ts +86 -0
  48. package/src/uploader/tests/metadata.test.ts +331 -0
  49. package/src/uploader/tests/providers.test.ts +131 -0
  50. package/src/uploader/types.ts +27 -0
@@ -0,0 +1,230 @@
1
+ import { permit2ABI, permit2Address } from "@zoralabs/protocol-deployments";
2
+ import { postQuote, PostQuoteResponse } from "src/client";
3
+ import { GenericPublicClient } from "src/utils/genericPublicClient";
4
+ import {
5
+ Account,
6
+ Address,
7
+ erc20Abi,
8
+ WalletClient,
9
+ maxUint256,
10
+ Hex,
11
+ } from "viem";
12
+ import { base } from "viem/chains";
13
+
14
+ type TradeERC20 = {
15
+ type: "erc20";
16
+ address: Address;
17
+ };
18
+
19
+ type TradeETH = {
20
+ type: "eth";
21
+ };
22
+
23
+ type PermitDetails = {
24
+ token: Address;
25
+ amount: bigint;
26
+ expiration: number;
27
+ nonce: number;
28
+ };
29
+
30
+ type Permit = {
31
+ details: PermitDetails;
32
+ spender: Address;
33
+ sigDeadline: bigint;
34
+ };
35
+
36
+ type PermitDetailsStringAmounts = {
37
+ token: Address;
38
+ amount: string;
39
+ expiration: number;
40
+ nonce: number;
41
+ };
42
+
43
+ type PermitStringAmounts = {
44
+ details: PermitDetailsStringAmounts;
45
+ spender: Address;
46
+ sigDeadline: string;
47
+ };
48
+
49
+ type SignatureWithPermit<TPermit = Permit> = {
50
+ signature: Hex;
51
+ permit: TPermit;
52
+ };
53
+
54
+ function convertBigIntToString(permit: Permit): PermitStringAmounts {
55
+ return {
56
+ ...permit,
57
+ details: {
58
+ ...permit.details,
59
+ amount: `${permit.details.amount}`,
60
+ },
61
+ sigDeadline: `${permit.sigDeadline}`,
62
+ };
63
+ }
64
+
65
+ const PERMIT_SINGLE_TYPES = {
66
+ PermitSingle: [
67
+ { name: "details", type: "PermitDetails" },
68
+ { name: "spender", type: "address" },
69
+ { name: "sigDeadline", type: "uint256" },
70
+ ],
71
+ PermitDetails: [
72
+ { name: "token", type: "address" },
73
+ { name: "amount", type: "uint160" },
74
+ { name: "expiration", type: "uint48" },
75
+ { name: "nonce", type: "uint48" },
76
+ ],
77
+ };
78
+
79
+ type TradeCurrency = TradeERC20 | TradeETH;
80
+
81
+ export type TradeParameters = {
82
+ sell: TradeCurrency;
83
+ buy: TradeCurrency;
84
+ amountIn: bigint;
85
+ slippage?: number;
86
+ // can be smart wallet or EOA here.
87
+ sender: Address;
88
+ // needs to be EOA, if signer is blank assumes EOA in sender.
89
+ signer?: Address;
90
+ recipient?: Address;
91
+ signatures?: SignatureWithPermit<PermitStringAmounts>[];
92
+ permitActiveSeconds?: number;
93
+ };
94
+
95
+ export async function tradeCoin(
96
+ tradeParameters: TradeParameters,
97
+ walletClient: WalletClient,
98
+ account: Account,
99
+ publicClient: GenericPublicClient,
100
+ validateTransaction = true,
101
+ ) {
102
+ const quote = await createTradeCall(tradeParameters);
103
+
104
+ // todo replace any
105
+ const signatures: { signature: Hex; permit: any }[] = [];
106
+ if (quote.permits) {
107
+ for (const permit of quote.permits) {
108
+ const [, nonce] = await publicClient.readContract({
109
+ abi: permit2ABI,
110
+ address: permit2Address[base.id],
111
+ functionName: "allowance",
112
+ args: [
113
+ permit.permit.details.token as Address,
114
+ account.address,
115
+ permit.permit.spender as Address,
116
+ ],
117
+ });
118
+ const permitToken = permit.permit.details.token as Address;
119
+ const allowance = await publicClient.readContract({
120
+ abi: erc20Abi,
121
+ address: permitToken,
122
+ functionName: "allowance",
123
+ args: [permitToken, permit2Address[base.id]],
124
+ });
125
+ if (allowance < BigInt(permit.permit.details.amount)) {
126
+ const approvalTx = await walletClient.writeContract({
127
+ abi: erc20Abi,
128
+ address: permitToken,
129
+ functionName: "approve",
130
+ chain: base,
131
+ args: [permit2Address[base.id], maxUint256],
132
+ account,
133
+ });
134
+ await publicClient.waitForTransactionReceipt({
135
+ hash: approvalTx,
136
+ });
137
+ }
138
+ const message = {
139
+ details: {
140
+ token: permit.permit.details.token as Address,
141
+ amount: BigInt(permit.permit.details.amount!),
142
+ expiration: Number(permit.permit.details.expiration!),
143
+ nonce: nonce,
144
+ },
145
+ spender: permit.permit.spender as Address,
146
+ sigDeadline: BigInt(permit.permit.sigDeadline!),
147
+ };
148
+ const signature = await walletClient.signTypedData({
149
+ domain: {
150
+ name: "Permit2",
151
+ chainId: base.id,
152
+ verifyingContract: permit2Address[base.id],
153
+ },
154
+ primaryType: "PermitSingle",
155
+ types: PERMIT_SINGLE_TYPES,
156
+ message,
157
+ account,
158
+ });
159
+ signatures.push({
160
+ signature,
161
+ permit: convertBigIntToString(message),
162
+ });
163
+ }
164
+ }
165
+
166
+ const newQuote = await createTradeCall({
167
+ ...tradeParameters,
168
+ signatures,
169
+ });
170
+
171
+ const call = {
172
+ to: newQuote.call.target as Address,
173
+ data: newQuote.call.data as Hex,
174
+ value: BigInt(newQuote.call.value),
175
+ chain: base,
176
+ account,
177
+ };
178
+
179
+ // simulate call
180
+ if (validateTransaction) {
181
+ await publicClient.call(call);
182
+ }
183
+
184
+ const gasEstimate = validateTransaction
185
+ ? await publicClient.estimateGas(call)
186
+ : 10_000_000n;
187
+ const gasPrice = await publicClient.getGasPrice();
188
+
189
+ const tx = await walletClient.sendTransaction({
190
+ ...call,
191
+ gasPrice,
192
+ gas: gasEstimate,
193
+ });
194
+
195
+ const receipt = await publicClient.waitForTransactionReceipt({
196
+ hash: tx,
197
+ });
198
+
199
+ return receipt;
200
+ }
201
+
202
+ export async function createTradeCall(
203
+ tradeParameters: TradeParameters,
204
+ ): Promise<PostQuoteResponse> {
205
+ if (tradeParameters.slippage && tradeParameters.slippage > 1) {
206
+ throw new Error("Slippage must be less than 1, max 0.99");
207
+ }
208
+ if (tradeParameters.amountIn === BigInt(0)) {
209
+ throw new Error("Amount in must be greater than 0");
210
+ }
211
+
212
+ const quote = await postQuote({
213
+ body: {
214
+ tokenIn: tradeParameters.sell,
215
+ tokenOut: tradeParameters.buy,
216
+ amountIn: tradeParameters.amountIn.toString(),
217
+ slippage: tradeParameters.slippage,
218
+ chainId: base.id,
219
+ sender: tradeParameters.sender,
220
+ recipient: tradeParameters.recipient || tradeParameters.sender,
221
+ signatures: tradeParameters.signatures,
222
+ },
223
+ });
224
+
225
+ if (!quote.data) {
226
+ throw new Error("Quote failed");
227
+ }
228
+
229
+ return quote.data;
230
+ }
@@ -1,8 +1,12 @@
1
1
  let apiKey: string | undefined;
2
- export function setApiKey(key: string) {
2
+ export function setApiKey(key: string | undefined) {
3
3
  apiKey = key;
4
4
  }
5
5
 
6
+ export function getApiKey() {
7
+ return apiKey;
8
+ }
9
+
6
10
  export function getApiKeyMeta() {
7
11
  if (!apiKey) {
8
12
  return {};
@@ -7,16 +7,16 @@ import { getApiKeyMeta } from "./api-key";
7
7
  import { RequestOptionsType } from "./query-types";
8
8
  import { RequestResult } from "@hey-api/client-fetch";
9
9
 
10
- type SetCreateUploadJwtQuery = SetCreateUploadJwtData["query"];
10
+ type SetCreateUploadJwtQuery = SetCreateUploadJwtData["body"];
11
11
  export type { SetCreateUploadJwtQuery, SetCreateUploadJwtData };
12
12
  export type { SetCreateUploadJwtResponse } from "../client/types.gen";
13
13
 
14
14
  export const setCreateUploadJwt = async (
15
- query: SetCreateUploadJwtQuery,
15
+ body: SetCreateUploadJwtQuery,
16
16
  options?: RequestOptionsType<SetCreateUploadJwtData>,
17
17
  ): Promise<RequestResult<SetCreateUploadJwtResponse>> => {
18
18
  return await setCreateUploadJwtSDK({
19
- query,
19
+ body,
20
20
  ...getApiKeyMeta(),
21
21
  ...options,
22
22
  });
@@ -24,6 +24,9 @@ import type {
24
24
  GetProfileBalancesResponse,
25
25
  GetProfileCoinsData,
26
26
  GetProfileCoinsResponse,
27
+ PostQuoteData,
28
+ PostQuoteResponse,
29
+ PostQuoteError,
27
30
  } from "./types.gen";
28
31
  import { client as _heyApiClient } from "./client.gen";
29
32
 
@@ -245,3 +248,26 @@ export const getProfileCoins = <ThrowOnError extends boolean = false>(
245
248
  ...options,
246
249
  });
247
250
  };
251
+
252
+ export const postQuote = <ThrowOnError extends boolean = false>(
253
+ options?: Options<PostQuoteData, ThrowOnError>,
254
+ ) => {
255
+ return (options?.client ?? _heyApiClient).post<
256
+ PostQuoteResponse,
257
+ PostQuoteError,
258
+ ThrowOnError
259
+ >({
260
+ security: [
261
+ {
262
+ name: "api-key",
263
+ type: "apiKey",
264
+ },
265
+ ],
266
+ url: "/quote",
267
+ ...options,
268
+ headers: {
269
+ "Content-Type": "application/json",
270
+ ...options?.headers,
271
+ },
272
+ });
273
+ };
@@ -204,6 +204,20 @@ export type GetCoinResponses = {
204
204
  * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
205
205
  */
206
206
  uniqueHolders: number;
207
+ uniswapV4PoolKey: {
208
+ token0Address: string;
209
+ token1Address: string;
210
+ /**
211
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
212
+ */
213
+ fee: number;
214
+ /**
215
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
216
+ */
217
+ tickSpacing: number;
218
+ hookAddress: string;
219
+ };
220
+ uniswapV3PoolAddress: string;
207
221
  zoraComments: {
208
222
  pageInfo: {
209
223
  /**
@@ -582,6 +596,20 @@ export type GetCoinsResponses = {
582
596
  * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
583
597
  */
584
598
  uniqueHolders: number;
599
+ uniswapV4PoolKey: {
600
+ token0Address: string;
601
+ token1Address: string;
602
+ /**
603
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
604
+ */
605
+ fee: number;
606
+ /**
607
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
608
+ */
609
+ tickSpacing: number;
610
+ hookAddress: string;
611
+ };
612
+ uniswapV3PoolAddress: string;
585
613
  zoraComments: {
586
614
  pageInfo: {
587
615
  /**
@@ -858,6 +886,20 @@ export type GetExploreResponses = {
858
886
  * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
859
887
  */
860
888
  uniqueHolders: number;
889
+ uniswapV4PoolKey: {
890
+ token0Address: string;
891
+ token1Address: string;
892
+ /**
893
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
894
+ */
895
+ fee: number;
896
+ /**
897
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
898
+ */
899
+ tickSpacing: number;
900
+ hookAddress: string;
901
+ };
902
+ uniswapV3PoolAddress: string;
861
903
  };
862
904
  /**
863
905
  * The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.
@@ -1187,6 +1229,20 @@ export type GetProfileBalancesResponses = {
1187
1229
  * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
1188
1230
  */
1189
1231
  uniqueHolders: number;
1232
+ uniswapV4PoolKey: {
1233
+ token0Address: string;
1234
+ token1Address: string;
1235
+ /**
1236
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
1237
+ */
1238
+ fee: number;
1239
+ /**
1240
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
1241
+ */
1242
+ tickSpacing: number;
1243
+ hookAddress: string;
1244
+ };
1245
+ uniswapV3PoolAddress: string;
1190
1246
  };
1191
1247
  };
1192
1248
  }>;
@@ -1406,6 +1462,20 @@ export type GetProfileCoinsResponses = {
1406
1462
  * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
1407
1463
  */
1408
1464
  uniqueHolders: number;
1465
+ uniswapV4PoolKey: {
1466
+ token0Address: string;
1467
+ token1Address: string;
1468
+ /**
1469
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
1470
+ */
1471
+ fee: number;
1472
+ /**
1473
+ * The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.
1474
+ */
1475
+ tickSpacing: number;
1476
+ hookAddress: string;
1477
+ };
1478
+ uniswapV3PoolAddress: string;
1409
1479
  zoraComments: {
1410
1480
  pageInfo: {
1411
1481
  /**
@@ -1485,6 +1555,97 @@ export type GetProfileCoinsResponses = {
1485
1555
  export type GetProfileCoinsResponse =
1486
1556
  GetProfileCoinsResponses[keyof GetProfileCoinsResponses];
1487
1557
 
1558
+ export type PostQuoteData = {
1559
+ body?: {
1560
+ signatures?: Array<{
1561
+ permit: {
1562
+ details: {
1563
+ token: string;
1564
+ amount: string;
1565
+ expiration: number;
1566
+ nonce: number;
1567
+ };
1568
+ spender: string;
1569
+ sigDeadline: string;
1570
+ };
1571
+ signature: string;
1572
+ }>;
1573
+ permitActiveSeconds?: number;
1574
+ chainId?: number;
1575
+ tokenOut?: {
1576
+ type: "eth" | "erc20";
1577
+ address?: string;
1578
+ };
1579
+ tokenIn?: {
1580
+ type: "eth" | "erc20";
1581
+ address?: string;
1582
+ };
1583
+ amountIn?: string;
1584
+ slippage?: number;
1585
+ recipient?: string;
1586
+ sender?: string;
1587
+ };
1588
+ path?: never;
1589
+ query?: never;
1590
+ url: "/quote";
1591
+ };
1592
+
1593
+ export type PostQuoteErrors = {
1594
+ /**
1595
+ * Error
1596
+ */
1597
+ 500: {
1598
+ error?: string;
1599
+ };
1600
+ };
1601
+
1602
+ export type PostQuoteError = PostQuoteErrors[keyof PostQuoteErrors];
1603
+
1604
+ export type PostQuoteResponses = {
1605
+ /**
1606
+ * Success
1607
+ */
1608
+ 200: {
1609
+ success: boolean;
1610
+ call: {
1611
+ data: string;
1612
+ value: string;
1613
+ target: string;
1614
+ };
1615
+ /**
1616
+ * The approval information for the token
1617
+ */
1618
+ permits?: Array<{
1619
+ signature: string;
1620
+ permit: {
1621
+ sigDeadline: string;
1622
+ spender: string;
1623
+ details: {
1624
+ token: string;
1625
+ amount: string;
1626
+ expiration: number;
1627
+ nonce: number;
1628
+ };
1629
+ };
1630
+ }>;
1631
+ trade?: {
1632
+ commands: Array<string>;
1633
+ value: string;
1634
+ inputs: Array<string>;
1635
+ };
1636
+ quote: {
1637
+ amountOut: string;
1638
+ slippage: number;
1639
+ tokenIn?: {
1640
+ type?: string;
1641
+ address?: string;
1642
+ };
1643
+ };
1644
+ };
1645
+ };
1646
+
1647
+ export type PostQuoteResponse = PostQuoteResponses[keyof PostQuoteResponses];
1648
+
1488
1649
  export type ClientOptions = {
1489
1650
  baseUrl:
1490
1651
  | "https://api-sdk.zora.engineering/"
package/src/index.ts CHANGED
@@ -23,6 +23,9 @@ export {
23
23
  } from "./actions/updatePayoutRecipient";
24
24
  export type { UpdatePayoutRecipientArgs } from "./actions/updatePayoutRecipient";
25
25
 
26
+ export { tradeCoin, createTradeCall } from "./actions/tradeCoin";
27
+ export type { TradeParameters } from "./actions/tradeCoin";
28
+
26
29
  // API Read Actions
27
30
  export * from "./api/queries";
28
31
  export type * from "./api/queries";
@@ -36,3 +39,6 @@ export { setApiKey } from "./api/api-key";
36
39
 
37
40
  // Metadata Validation Utils
38
41
  export * from "./metadata";
42
+
43
+ // Uploader
44
+ export * from "./uploader";
@@ -1,8 +1,4 @@
1
- export type ValidMetadataURI =
2
- | `ipfs://${string}`
3
- | `ar://${string}`
4
- | `data:${string}`
5
- | `https://${string}`;
1
+ import { ValidMetadataURI } from "../uploader/types";
6
2
 
7
3
  /**
8
4
  * Clean the metadata URI to HTTPS format
@@ -1,7 +1,4 @@
1
- export {
2
- cleanAndValidateMetadataURI,
3
- type ValidMetadataURI,
4
- } from "./cleanAndValidateMetadataURI";
1
+ export { cleanAndValidateMetadataURI } from "./cleanAndValidateMetadataURI";
5
2
  export { validateMetadataJSON } from "./validateMetadataJSON";
6
3
  export type { ValidMetadataJSON } from "./validateMetadataJSON";
7
4
  export { validateMetadataURIContent } from "./validateMetadataURIContent";
@@ -1,7 +1,5 @@
1
- import {
2
- cleanAndValidateMetadataURI,
3
- ValidMetadataURI,
4
- } from "./cleanAndValidateMetadataURI";
1
+ import { cleanAndValidateMetadataURI } from "./cleanAndValidateMetadataURI";
2
+ import { ValidMetadataURI } from "../uploader/types";
5
3
  import { validateMetadataJSON } from "./validateMetadataJSON";
6
4
 
7
5
  /**
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Uploader package for storing files on IPFS and other decentralized storage
3
+ * @packageDocumentation
4
+ */
5
+
6
+ // Export core types
7
+ export * from "./types";
8
+
9
+ // Export the metadata builder
10
+ export * from "./metadata";
11
+
12
+ // Export all providers
13
+ export * from "./providers/zora";
14
+
15
+ export { createMetadataBuilder } from "./metadata";
16
+ export { createZoraUploaderForCreator } from "./providers/zora";