@zoralabs/coins-sdk 0.2.3 → 0.2.5

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 (55) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/actions/createCoin.d.ts +11 -4
  3. package/dist/actions/createCoin.d.ts.map +1 -1
  4. package/dist/actions/updateCoinURI.d.ts +1 -1
  5. package/dist/actions/updateCoinURI.d.ts.map +1 -1
  6. package/dist/actions/updatePayoutRecipient.d.ts +1 -1
  7. package/dist/actions/updatePayoutRecipient.d.ts.map +1 -1
  8. package/dist/api/api-key.d.ts +2 -1
  9. package/dist/api/api-key.d.ts.map +1 -1
  10. package/dist/api/index.d.ts.map +1 -1
  11. package/dist/api/internal.d.ts +2 -2
  12. package/dist/api/internal.d.ts.map +1 -1
  13. package/dist/index.cjs +301 -103
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +3 -4
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +320 -122
  18. package/dist/index.js.map +1 -1
  19. package/dist/metadata/cleanAndValidateMetadataURI.d.ts +1 -1
  20. package/dist/metadata/cleanAndValidateMetadataURI.d.ts.map +1 -1
  21. package/dist/metadata/index.d.ts +1 -1
  22. package/dist/metadata/index.d.ts.map +1 -1
  23. package/dist/metadata/validateMetadataURIContent.d.ts +1 -1
  24. package/dist/metadata/validateMetadataURIContent.d.ts.map +1 -1
  25. package/dist/uploader/index.d.ts +10 -0
  26. package/dist/uploader/index.d.ts.map +1 -0
  27. package/dist/uploader/metadata.d.ts +44 -0
  28. package/dist/uploader/metadata.d.ts.map +1 -0
  29. package/dist/uploader/providers/zora.d.ts +18 -0
  30. package/dist/uploader/providers/zora.d.ts.map +1 -0
  31. package/dist/uploader/types.d.ts +21 -0
  32. package/dist/uploader/types.d.ts.map +1 -0
  33. package/dist/utils/getPrepurchaseHook.d.ts +16 -0
  34. package/dist/utils/getPrepurchaseHook.d.ts.map +1 -0
  35. package/package.json +1 -1
  36. package/src/actions/createCoin.ts +33 -7
  37. package/src/actions/updateCoinURI.ts +1 -1
  38. package/src/actions/updatePayoutRecipient.ts +1 -1
  39. package/src/api/api-key.ts +5 -1
  40. package/src/api/index.ts +1 -0
  41. package/src/api/internal.ts +3 -3
  42. package/src/index.ts +9 -9
  43. package/src/metadata/cleanAndValidateMetadataURI.ts +1 -5
  44. package/src/metadata/index.ts +1 -4
  45. package/src/metadata/validateMetadataURIContent.ts +2 -4
  46. package/src/uploader/index.ts +16 -0
  47. package/src/uploader/metadata.ts +214 -0
  48. package/src/uploader/providers/zora.ts +86 -0
  49. package/src/uploader/tests/metadata.test.ts +331 -0
  50. package/src/uploader/tests/providers.test.ts +131 -0
  51. package/src/uploader/types.ts +27 -0
  52. package/src/utils/getPrepurchaseHook.ts +59 -0
  53. package/dist/actions/tradeCoin.d.ts +0 -75
  54. package/dist/actions/tradeCoin.d.ts.map +0 -1
  55. package/src/actions/tradeCoin.ts +0 -182
@@ -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
  });
package/src/index.ts CHANGED
@@ -3,16 +3,12 @@ export {
3
3
  createCoinCall,
4
4
  getCoinCreateFromLogs,
5
5
  DeployCurrency,
6
+ InitialPurchaseCurrency,
7
+ } from "./actions/createCoin";
8
+ export type {
9
+ CreateCoinArgs,
10
+ CoinDeploymentLogArgs,
6
11
  } from "./actions/createCoin";
7
- export type { CreateCoinArgs } from "./actions/createCoin";
8
-
9
- export {
10
- simulateBuy,
11
- tradeCoin,
12
- tradeCoinCall,
13
- getTradeFromLogs,
14
- } from "./actions/tradeCoin";
15
- export type { TradeParams } from "./actions/tradeCoin";
16
12
 
17
13
  export {
18
14
  getOnchainCoinDetails,
@@ -31,6 +27,7 @@ export type { UpdatePayoutRecipientArgs } from "./actions/updatePayoutRecipient"
31
27
  export * from "./api/queries";
32
28
  export type * from "./api/queries";
33
29
 
30
+ // API Explore Actions
34
31
  export * from "./api/explore";
35
32
  export type * from "./api/explore";
36
33
 
@@ -39,3 +36,6 @@ export { setApiKey } from "./api/api-key";
39
36
 
40
37
  // Metadata Validation Utils
41
38
  export * from "./metadata";
39
+
40
+ // Uploader
41
+ 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";
@@ -0,0 +1,214 @@
1
+ import {
2
+ CreateMetadataParameters,
3
+ Uploader,
4
+ UploadResult,
5
+ ValidMetadataURI,
6
+ } from "./types";
7
+
8
+ type Metadata = {
9
+ name: string;
10
+ symbol: string;
11
+ description: string;
12
+ image: string;
13
+ properties?: Record<string, string>;
14
+ animation_url?: string;
15
+ content?: {
16
+ uri: string;
17
+ mime: string | undefined;
18
+ };
19
+ };
20
+
21
+ export function validateImageMimeType(mimeType: string) {
22
+ if (
23
+ ![
24
+ "image/png",
25
+ "image/jpeg",
26
+ "image/jpg",
27
+ "image/gif",
28
+ "image/svg+xml",
29
+ ].includes(mimeType)
30
+ ) {
31
+ throw new Error("Image must be a PNG, JPEG, JPG, GIF or SVG");
32
+ }
33
+ }
34
+
35
+ export function getURLFromUploadResult(uploadResult: UploadResult) {
36
+ return new URL(uploadResult.url);
37
+ }
38
+
39
+ export class CoinMetadataBuilder {
40
+ private name: string | undefined;
41
+ private description: string | undefined;
42
+ private symbol: string | undefined;
43
+ private imageFile: File | undefined;
44
+ private imageURL: URL | undefined;
45
+ private mediaFile: File | undefined;
46
+ private mediaURL: URL | undefined;
47
+ private mediaMimeType: string | undefined;
48
+ private properties: Record<string, string> | undefined;
49
+
50
+ withName(name: string) {
51
+ this.name = name;
52
+ if (typeof name !== "string") {
53
+ throw new Error("Name must be a string");
54
+ }
55
+
56
+ return this;
57
+ }
58
+
59
+ withSymbol(symbol: string) {
60
+ this.symbol = symbol;
61
+ if (typeof symbol !== "string") {
62
+ throw new Error("Symbol must be a string");
63
+ }
64
+
65
+ return this;
66
+ }
67
+
68
+ withDescription(description: string) {
69
+ this.description = description;
70
+ if (typeof description !== "string") {
71
+ throw new Error("Description must be a string");
72
+ }
73
+
74
+ return this;
75
+ }
76
+
77
+ withImage(image: File) {
78
+ if (this.imageURL) {
79
+ throw new Error("Image URL already set");
80
+ }
81
+ if (!(image instanceof File)) {
82
+ throw new Error("Image must be a File");
83
+ }
84
+ validateImageMimeType(image.type);
85
+ this.imageFile = image;
86
+
87
+ return this;
88
+ }
89
+
90
+ withImageURI(imageURI: string) {
91
+ if (this.imageFile) {
92
+ throw new Error("Image file already set");
93
+ }
94
+ if (typeof imageURI !== "string") {
95
+ throw new Error("Image URI must be a string");
96
+ }
97
+ const url = new URL(imageURI);
98
+ this.imageURL = url;
99
+
100
+ return this;
101
+ }
102
+
103
+ withProperties(properties: Record<string, string>) {
104
+ for (const [key, value] of Object.entries(properties)) {
105
+ if (typeof key !== "string") {
106
+ throw new Error("Property key must be a string");
107
+ }
108
+ if (typeof value !== "string") {
109
+ throw new Error("Property value must be a string");
110
+ }
111
+ }
112
+ if (!this.properties) {
113
+ this.properties = {};
114
+ }
115
+ this.properties = { ...this.properties, ...properties };
116
+
117
+ return this;
118
+ }
119
+
120
+ withMedia(media: File) {
121
+ if (this.mediaURL) {
122
+ throw new Error("Media URL already set");
123
+ }
124
+ if (!(media instanceof File)) {
125
+ throw new Error("Media must be a File");
126
+ }
127
+ this.mediaMimeType = media.type;
128
+ this.mediaFile = media;
129
+
130
+ return this;
131
+ }
132
+
133
+ withMediaURI(mediaURI: string, mediaMimeType: string | undefined) {
134
+ if (this.mediaFile) {
135
+ throw new Error("Media file already set");
136
+ }
137
+ if (typeof mediaURI !== "string") {
138
+ throw new Error("Media URI must be a string");
139
+ }
140
+ const url = new URL(mediaURI);
141
+ this.mediaURL = url;
142
+ this.mediaMimeType = mediaMimeType;
143
+
144
+ return this;
145
+ }
146
+
147
+ validate() {
148
+ if (!this.name) {
149
+ throw new Error("Name is required");
150
+ }
151
+ if (!this.symbol) {
152
+ throw new Error("Symbol is required");
153
+ }
154
+ if (!this.imageFile && !this.imageURL) {
155
+ throw new Error("Image is required");
156
+ }
157
+
158
+ return this;
159
+ }
160
+
161
+ generateMetadata(): Metadata {
162
+ return {
163
+ name: this.name!,
164
+ symbol: this.symbol!,
165
+ description: this.description!,
166
+ image: this.imageURL!.toString(),
167
+ animation_url: this.mediaURL?.toString(),
168
+ content: this.mediaURL
169
+ ? {
170
+ uri: this.mediaURL?.toString(),
171
+ mime: this.mediaMimeType,
172
+ }
173
+ : undefined,
174
+ properties: this.properties,
175
+ };
176
+ }
177
+
178
+ async upload(uploader: Uploader): Promise<{
179
+ url: ValidMetadataURI;
180
+ createMetadataParameters: CreateMetadataParameters;
181
+ metadata: Metadata;
182
+ }> {
183
+ this.validate();
184
+
185
+ if (this.imageFile) {
186
+ const uploadResult = await uploader.upload(this.imageFile);
187
+ this.imageURL = getURLFromUploadResult(uploadResult);
188
+ }
189
+ if (this.mediaFile) {
190
+ const uploadResult = await uploader.upload(this.mediaFile);
191
+ this.mediaURL = getURLFromUploadResult(uploadResult);
192
+ }
193
+ const metadata = this.generateMetadata();
194
+ const uploadResult = await uploader.upload(
195
+ new File([JSON.stringify(metadata)], "metadata.json", {
196
+ type: "application/json",
197
+ }),
198
+ );
199
+
200
+ return {
201
+ url: getURLFromUploadResult(uploadResult).toString() as ValidMetadataURI,
202
+ createMetadataParameters: {
203
+ name: this.name!,
204
+ symbol: this.symbol!,
205
+ uri: uploadResult.url as `ipfs://${string}`,
206
+ },
207
+ metadata,
208
+ };
209
+ }
210
+ }
211
+
212
+ export function createMetadataBuilder() {
213
+ return new CoinMetadataBuilder();
214
+ }
@@ -0,0 +1,86 @@
1
+ import { Address } from "viem";
2
+ import { Uploader, UploadResult } from "../types";
3
+ import { getApiKey } from "../../api/api-key";
4
+ import { setCreateUploadJwt } from "../../api/internal";
5
+
6
+ /**
7
+ * Zora IPFS uploader implementation
8
+ */
9
+ export class ZoraUploader implements Uploader {
10
+ constructor(creatorAddress: Address) {
11
+ this.creatorAddress = creatorAddress;
12
+ if (!getApiKey()) {
13
+ throw new Error("API key is required for metadata interactions");
14
+ }
15
+ }
16
+
17
+ private creatorAddress: Address;
18
+ private jwtApiKey: string | undefined;
19
+ private jwtApiKeyExpiresAt: number | undefined;
20
+
21
+ async getJWTApiKey() {
22
+ if (
23
+ this.jwtApiKey &&
24
+ this.jwtApiKeyExpiresAt &&
25
+ this.jwtApiKeyExpiresAt > Date.now()
26
+ ) {
27
+ return this.jwtApiKey;
28
+ }
29
+ // Expires in 1 hour
30
+ this.jwtApiKeyExpiresAt = Date.now() + 1000 * 60 * 60;
31
+
32
+ const response = await setCreateUploadJwt({
33
+ creatorAddress: this.creatorAddress,
34
+ });
35
+ this.jwtApiKey = response.data?.createUploadJwtFromApiKey;
36
+ if (!this.jwtApiKey) {
37
+ throw new Error("Failed to create upload JWT");
38
+ }
39
+
40
+ return this.jwtApiKey;
41
+ }
42
+
43
+ async upload(file: File): Promise<UploadResult> {
44
+ const jwtApiKey = await this.getJWTApiKey();
45
+ const formData = new FormData();
46
+ formData.append("file", file, file.name);
47
+
48
+ const response = await fetch(
49
+ "https://ipfs-uploader.zora.co/api/v0/add?cid-version=1",
50
+ {
51
+ method: "POST",
52
+ headers: {
53
+ Authorization: `Bearer ${jwtApiKey}`,
54
+ Accept: "*/*",
55
+ },
56
+ body: formData,
57
+ },
58
+ );
59
+
60
+ if (!response.ok) {
61
+ console.error(await response.text());
62
+ throw new Error(`Failed to upload file: ${response.statusText}`);
63
+ }
64
+
65
+ const data = (await response.json()) as {
66
+ cid: string;
67
+ size: number | undefined;
68
+ mimeType: string | undefined;
69
+ };
70
+
71
+ return {
72
+ url: `ipfs://${data.cid}`,
73
+ size: data.size,
74
+ mimeType: data.mimeType,
75
+ };
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Create a new Zora IPFS uploader
81
+ */
82
+ export function createZoraUploaderForCreator(
83
+ creatorAddress: Address,
84
+ ): Uploader {
85
+ return new ZoraUploader(creatorAddress);
86
+ }