@zoralabs/coins-sdk 0.5.2 → 0.7.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 (49) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/actions/createCoin.d.ts +92 -7
  3. package/dist/actions/createCoin.d.ts.map +1 -1
  4. package/dist/actions/tradeCoin.d.ts +27 -2
  5. package/dist/actions/tradeCoin.d.ts.map +1 -1
  6. package/dist/actions/updateCoinURI.d.ts +35 -1
  7. package/dist/actions/updateCoinURI.d.ts.map +1 -1
  8. package/dist/actions/updatePayoutRecipient.d.ts +41 -1
  9. package/dist/actions/updatePayoutRecipient.d.ts.map +1 -1
  10. package/dist/api/api-raw.d.ts +1 -0
  11. package/dist/api/api-raw.d.ts.map +1 -1
  12. package/dist/api/index.d.ts +2 -0
  13. package/dist/api/index.d.ts.map +1 -1
  14. package/dist/api/queries.d.ts +9 -1
  15. package/dist/api/queries.d.ts.map +1 -1
  16. package/dist/api/social.d.ts +8 -0
  17. package/dist/api/social.d.ts.map +1 -0
  18. package/dist/client/sdk.gen.d.ts +144 -1
  19. package/dist/client/sdk.gen.d.ts.map +1 -1
  20. package/dist/client/types.gen.d.ts +411 -1
  21. package/dist/client/types.gen.d.ts.map +1 -1
  22. package/dist/index.cjs +724 -250
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.ts +11 -5
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +701 -227
  27. package/dist/index.js.map +1 -1
  28. package/dist/metadata/validateMetadataURIContent.d.ts.map +1 -1
  29. package/dist/utils/calls.d.ts +61 -0
  30. package/dist/utils/calls.d.ts.map +1 -0
  31. package/dist/utils/userOperation.d.ts +44 -0
  32. package/dist/utils/userOperation.d.ts.map +1 -0
  33. package/package.json +1 -1
  34. package/src/actions/createCoin.ts +314 -60
  35. package/src/actions/tradeCoin.ts +237 -72
  36. package/src/actions/updateCoinURI.ts +84 -6
  37. package/src/actions/updatePayoutRecipient.ts +92 -5
  38. package/src/api/api-raw.test.ts +61 -0
  39. package/src/api/api-raw.ts +9 -0
  40. package/src/api/index.ts +4 -0
  41. package/src/api/queries.ts +36 -0
  42. package/src/api/social.ts +23 -0
  43. package/src/client/sdk.gen.ts +48 -0
  44. package/src/client/types.gen.ts +421 -1
  45. package/src/index.ts +45 -3
  46. package/src/metadata/validateMetadataURIContent.ts +17 -2
  47. package/src/utils/calls.ts +129 -0
  48. package/src/utils/userOperation.test.ts +84 -0
  49. package/src/utils/userOperation.ts +124 -0
package/dist/index.js CHANGED
@@ -4,133 +4,12 @@ import {
4
4
  coinFactoryABI as zoraFactoryImplABI
5
5
  } from "@zoralabs/protocol-deployments";
6
6
  import {
7
- parseEventLogs,
8
- isAddressEqual
7
+ decodeFunctionData,
8
+ isAddressEqual,
9
+ parseEventLogs
9
10
  } from "viem";
10
11
  import { base as base3 } from "viem/chains";
11
12
 
12
- // src/utils/validateClientNetwork.ts
13
- import { base, baseSepolia } from "viem/chains";
14
- var validateClientNetwork = (publicClient) => {
15
- const clientChainId = publicClient?.chain?.id;
16
- if (clientChainId === base.id) {
17
- return;
18
- }
19
- if (clientChainId === baseSepolia.id) {
20
- return;
21
- }
22
- throw new Error(
23
- "Client network needs to be base or baseSepolia for current coin deployments."
24
- );
25
- };
26
-
27
- // src/metadata/cleanAndValidateMetadataURI.ts
28
- function cleanAndValidateMetadataURI(uri) {
29
- if (uri.startsWith("ipfs://")) {
30
- return uri.replace(
31
- "ipfs://",
32
- "https://magic.decentralized-content.com/ipfs/"
33
- );
34
- }
35
- if (uri.startsWith("ar://")) {
36
- return uri.replace("ar://", "http://arweave.net/");
37
- }
38
- if (uri.startsWith("data:")) {
39
- return uri;
40
- }
41
- if (uri.startsWith("http://") || uri.startsWith("https://")) {
42
- return uri;
43
- }
44
- throw new Error("Invalid metadata URI");
45
- }
46
-
47
- // src/metadata/validateMetadataJSON.ts
48
- function validateURIString(uri) {
49
- if (typeof uri !== "string") {
50
- throw new Error("URI must be a string");
51
- }
52
- if (uri.startsWith("ipfs://")) {
53
- return true;
54
- }
55
- if (uri.startsWith("ar://")) {
56
- return true;
57
- }
58
- if (uri.startsWith("https://")) {
59
- return true;
60
- }
61
- if (uri.startsWith("data:")) {
62
- return true;
63
- }
64
- return false;
65
- }
66
- function validateMetadataJSON(metadata) {
67
- if (typeof metadata !== "object" || !metadata) {
68
- throw new Error("Metadata must be an object and exist");
69
- }
70
- if (typeof metadata.name !== "string") {
71
- throw new Error("Metadata name is required and must be a string");
72
- }
73
- if (typeof metadata.description !== "string") {
74
- throw new Error("Metadata description is required and must be a string");
75
- }
76
- if (typeof metadata.image === "string") {
77
- if (!validateURIString(metadata.image)) {
78
- throw new Error("Metadata image is not a valid URI");
79
- }
80
- } else {
81
- throw new Error("Metadata image is required and must be a string");
82
- }
83
- if ("animation_url" in metadata) {
84
- if (typeof metadata.animation_url !== "string") {
85
- throw new Error("Metadata animation_url, if provided, must be a string");
86
- }
87
- if (!validateURIString(metadata.animation_url)) {
88
- throw new Error("Metadata animation_url is not a valid URI");
89
- }
90
- }
91
- const content = "content" in metadata && metadata.content;
92
- if (content) {
93
- if (typeof content.uri !== "string") {
94
- throw new Error("If provided, content.uri must be a string");
95
- }
96
- if (!validateURIString(content.uri)) {
97
- throw new Error("If provided, content.uri must be a valid URI string");
98
- }
99
- if (typeof content.mime !== "string") {
100
- throw new Error("If provided, content.mime must be a string");
101
- }
102
- }
103
- return true;
104
- }
105
-
106
- // src/metadata/validateMetadataURIContent.ts
107
- async function validateMetadataURIContent(metadataURI) {
108
- const cleanedURI = cleanAndValidateMetadataURI(metadataURI);
109
- const response = await fetch(cleanedURI);
110
- if (!response.ok) {
111
- throw new Error("Metadata fetch failed");
112
- }
113
- if (!["application/json", "text/plain"].includes(
114
- response.headers.get("content-type") ?? ""
115
- )) {
116
- throw new Error("Metadata is not a valid JSON or plain text response type");
117
- }
118
- const metadataJson = await response.json();
119
- return validateMetadataJSON(metadataJson);
120
- }
121
-
122
- // src/utils/getChainFromId.ts
123
- import { base as base2, baseSepolia as baseSepolia2 } from "viem/chains";
124
- function getChainFromId(chainId) {
125
- if (chainId === base2.id) {
126
- return base2;
127
- }
128
- if (chainId === baseSepolia2.id) {
129
- return baseSepolia2;
130
- }
131
- throw new Error(`Chain ID ${chainId} not supported`);
132
- }
133
-
134
13
  // src/client/client.gen.ts
135
14
  import {
136
15
  createClient,
@@ -179,6 +58,18 @@ var getCoinHolders = (options) => {
179
58
  ...options
180
59
  });
181
60
  };
61
+ var getCoinPriceHistory = (options) => {
62
+ return (options.client ?? client).get({
63
+ security: [
64
+ {
65
+ name: "api-key",
66
+ type: "apiKey"
67
+ }
68
+ ],
69
+ url: "/coinPriceHistory",
70
+ ...options
71
+ });
72
+ };
182
73
  var getCoinSwaps = (options) => {
183
74
  return (options.client ?? client).get({
184
75
  security: [
@@ -303,6 +194,18 @@ var getProfileBalances = (options) => {
303
194
  ...options
304
195
  });
305
196
  };
197
+ var getProfileBySocialHandle = (options) => {
198
+ return (options.client ?? client).get({
199
+ security: [
200
+ {
201
+ name: "api-key",
202
+ type: "apiKey"
203
+ }
204
+ ],
205
+ url: "/profileBySocialHandle",
206
+ ...options
207
+ });
208
+ };
306
209
  var getProfileCoins = (options) => {
307
210
  return (options.client ?? client).get({
308
211
  security: [
@@ -375,6 +278,18 @@ var getTrendsByName = (options) => {
375
278
  ...options
376
279
  });
377
280
  };
281
+ var getWalletTradeActivity = (options) => {
282
+ return (options.client ?? client).get({
283
+ security: [
284
+ {
285
+ name: "api-key",
286
+ type: "apiKey"
287
+ }
288
+ ],
289
+ url: "/walletTradeActivity",
290
+ ...options
291
+ });
292
+ };
378
293
  var postQuote = (options) => {
379
294
  return (options?.client ?? client).post({
380
295
  security: [
@@ -490,6 +405,13 @@ var getCoinHolders2 = async (query, options) => {
490
405
  ...options
491
406
  });
492
407
  };
408
+ var getCoinPriceHistory2 = async (query, options) => {
409
+ return await getCoinPriceHistory({
410
+ query,
411
+ ...getApiKeyMeta(),
412
+ ...options
413
+ });
414
+ };
493
415
  var getCoinSwaps2 = async (query, options) => {
494
416
  return await getCoinSwaps({
495
417
  query,
@@ -574,6 +496,13 @@ var getCreatorLivestreamComments2 = async (query, options) => {
574
496
  ...options
575
497
  });
576
498
  };
499
+ var getWalletTradeActivity2 = async (query, options) => {
500
+ return await getWalletTradeActivity({
501
+ query,
502
+ ...getApiKeyMeta(),
503
+ ...options
504
+ });
505
+ };
577
506
 
578
507
  // src/api/create.ts
579
508
  var postCreateContent2 = async (body, options) => {
@@ -584,6 +513,169 @@ var postCreateContent2 = async (body, options) => {
584
513
  });
585
514
  };
586
515
 
516
+ // src/api/social.ts
517
+ var getProfileBySocialHandle2 = async (query, options) => {
518
+ return await getProfileBySocialHandle({
519
+ ...options,
520
+ query,
521
+ ...getApiKeyMeta()
522
+ });
523
+ };
524
+
525
+ // src/metadata/cleanAndValidateMetadataURI.ts
526
+ function cleanAndValidateMetadataURI(uri) {
527
+ if (uri.startsWith("ipfs://")) {
528
+ return uri.replace(
529
+ "ipfs://",
530
+ "https://magic.decentralized-content.com/ipfs/"
531
+ );
532
+ }
533
+ if (uri.startsWith("ar://")) {
534
+ return uri.replace("ar://", "http://arweave.net/");
535
+ }
536
+ if (uri.startsWith("data:")) {
537
+ return uri;
538
+ }
539
+ if (uri.startsWith("http://") || uri.startsWith("https://")) {
540
+ return uri;
541
+ }
542
+ throw new Error("Invalid metadata URI");
543
+ }
544
+
545
+ // src/metadata/validateMetadataJSON.ts
546
+ function validateURIString(uri) {
547
+ if (typeof uri !== "string") {
548
+ throw new Error("URI must be a string");
549
+ }
550
+ if (uri.startsWith("ipfs://")) {
551
+ return true;
552
+ }
553
+ if (uri.startsWith("ar://")) {
554
+ return true;
555
+ }
556
+ if (uri.startsWith("https://")) {
557
+ return true;
558
+ }
559
+ if (uri.startsWith("data:")) {
560
+ return true;
561
+ }
562
+ return false;
563
+ }
564
+ function validateMetadataJSON(metadata) {
565
+ if (typeof metadata !== "object" || !metadata) {
566
+ throw new Error("Metadata must be an object and exist");
567
+ }
568
+ if (typeof metadata.name !== "string") {
569
+ throw new Error("Metadata name is required and must be a string");
570
+ }
571
+ if (typeof metadata.description !== "string") {
572
+ throw new Error("Metadata description is required and must be a string");
573
+ }
574
+ if (typeof metadata.image === "string") {
575
+ if (!validateURIString(metadata.image)) {
576
+ throw new Error("Metadata image is not a valid URI");
577
+ }
578
+ } else {
579
+ throw new Error("Metadata image is required and must be a string");
580
+ }
581
+ if ("animation_url" in metadata) {
582
+ if (typeof metadata.animation_url !== "string") {
583
+ throw new Error("Metadata animation_url, if provided, must be a string");
584
+ }
585
+ if (!validateURIString(metadata.animation_url)) {
586
+ throw new Error("Metadata animation_url is not a valid URI");
587
+ }
588
+ }
589
+ const content = "content" in metadata && metadata.content;
590
+ if (content) {
591
+ if (typeof content.uri !== "string") {
592
+ throw new Error("If provided, content.uri must be a string");
593
+ }
594
+ if (!validateURIString(content.uri)) {
595
+ throw new Error("If provided, content.uri must be a valid URI string");
596
+ }
597
+ if (typeof content.mime !== "string") {
598
+ throw new Error("If provided, content.mime must be a string");
599
+ }
600
+ }
601
+ return true;
602
+ }
603
+
604
+ // src/metadata/validateMetadataURIContent.ts
605
+ async function validateMetadataURIContent(metadataURI) {
606
+ let response;
607
+ const cleanedURI = cleanAndValidateMetadataURI(metadataURI);
608
+ try {
609
+ response = await fetch(cleanedURI);
610
+ } catch (error) {
611
+ const errorMessage = error instanceof Error ? error.message : String(error);
612
+ throw new Error(
613
+ `Metadata fetch failed for URL '${cleanedURI}': ${errorMessage}`
614
+ );
615
+ }
616
+ if (!response.ok) {
617
+ throw new Error(
618
+ `Metadata fetch failed for URL '${cleanedURI}': ${response.statusText ? `${response.statusText} (HTTP ${response.status})` : `HTTP ${response.status}`}`
619
+ );
620
+ }
621
+ if (!["application/json", "text/plain"].includes(
622
+ response.headers.get("content-type") ?? ""
623
+ )) {
624
+ throw new Error("Metadata is not a valid JSON or plain text response type");
625
+ }
626
+ const metadataJson = await response.json();
627
+ return validateMetadataJSON(metadataJson);
628
+ }
629
+
630
+ // src/utils/calls.ts
631
+ import {
632
+ concatHex,
633
+ encodeFunctionData
634
+ } from "viem";
635
+ var EMPTY_HEX = "0x";
636
+ var isContractCall = (call) => {
637
+ return call.address !== void 0 && call.abi !== void 0 && call.functionName !== void 0;
638
+ };
639
+ var isSendCall = (call) => {
640
+ return !isContractCall(call) && call.to !== void 0;
641
+ };
642
+ function toGenericCall(call) {
643
+ if (isSendCall(call)) {
644
+ return {
645
+ to: call.to,
646
+ value: call.value ?? 0n,
647
+ data: EMPTY_HEX
648
+ };
649
+ }
650
+ const { dataSuffix } = call;
651
+ const callData = encodeFunctionData(call);
652
+ const data = dataSuffix ? concatHex([callData, dataSuffix]) : callData;
653
+ return {
654
+ to: call.address,
655
+ value: call.value ?? 0n,
656
+ data
657
+ };
658
+ }
659
+ function toUserOperationCalls(calls) {
660
+ return calls.map((call) => ({
661
+ to: call.to,
662
+ data: call.data,
663
+ value: call.value
664
+ }));
665
+ }
666
+
667
+ // src/utils/getChainFromId.ts
668
+ import { base, baseSepolia } from "viem/chains";
669
+ function getChainFromId(chainId) {
670
+ if (chainId === base.id) {
671
+ return base;
672
+ }
673
+ if (chainId === baseSepolia.id) {
674
+ return baseSepolia;
675
+ }
676
+ throw new Error(`Chain ID ${chainId} not supported`);
677
+ }
678
+
587
679
  // src/utils/rethrowDecodedRevert.ts
588
680
  import {
589
681
  BaseError,
@@ -619,6 +711,89 @@ function rethrowDecodedRevert(err, abi) {
619
711
  throw err;
620
712
  }
621
713
 
714
+ // src/utils/userOperation.ts
715
+ import { formatEther } from "viem";
716
+ var prepareUserOperation = async ({
717
+ bundlerClient,
718
+ account,
719
+ calls
720
+ }) => {
721
+ const prepared = await bundlerClient.prepareUserOperation({
722
+ account,
723
+ calls
724
+ });
725
+ return prepared;
726
+ };
727
+ var submitUserOperation = async ({
728
+ bundlerClient,
729
+ account,
730
+ userOperation
731
+ }) => {
732
+ let hash;
733
+ const signature = await account.signUserOperation(userOperation);
734
+ try {
735
+ hash = await bundlerClient.sendUserOperation({
736
+ account,
737
+ ...userOperation,
738
+ signature
739
+ });
740
+ } catch (error) {
741
+ if (isGasError(error)) {
742
+ throw new CoinbaseGasError(error);
743
+ }
744
+ throw error;
745
+ }
746
+ return bundlerClient.waitForUserOperationReceipt({ hash });
747
+ };
748
+ var CoinbaseGasError = class extends Error {
749
+ constructor(error) {
750
+ let message;
751
+ let available;
752
+ let required;
753
+ const match = error.details.match(
754
+ /precheck failed: sender balance and deposit together is (\d+)? but must be at least (\d+)? to pay for this operation/
755
+ );
756
+ if (match) {
757
+ available = match[1] ? BigInt(match[1]) : void 0;
758
+ required = match[2] ? BigInt(match[2]) : void 0;
759
+ if (available !== void 0 && required !== void 0) {
760
+ message = `Insufficient balance. You need at least ${formatEther(required)} ETH to pay for this operation, but you only have ${formatEther(available)} ETH.`;
761
+ } else if (required !== void 0) {
762
+ message = `Insufficient balance. Make sure you have at least ${formatEther(required)} ETH in your wallet.`;
763
+ } else {
764
+ message = `Insufficient balance. Make sure you have enough ETH to pay for this operation.`;
765
+ }
766
+ } else {
767
+ message = error.details ?? error.message;
768
+ }
769
+ super(message);
770
+ this.cause = error.cause;
771
+ this.details = error.details;
772
+ this.available = available;
773
+ this.required = required;
774
+ }
775
+ };
776
+ function isGasError(error) {
777
+ return error.details?.startsWith(
778
+ "precheck failed: sender balance and deposit together is"
779
+ ) ?? false;
780
+ }
781
+
782
+ // src/utils/validateClientNetwork.ts
783
+ import { base as base2, baseSepolia as baseSepolia2 } from "viem/chains";
784
+ var validateClientNetwork = (publicClient) => {
785
+ const clientChainId = publicClient?.chain?.id;
786
+ if (clientChainId === base2.id) {
787
+ return;
788
+ }
789
+ if (clientChainId === baseSepolia2.id) {
790
+ return;
791
+ }
792
+ throw new Error(
793
+ "Client network needs to be base or baseSepolia for current coin deployments."
794
+ );
795
+ };
796
+
622
797
  // src/actions/createCoin.ts
623
798
  var STARTING_MARKET_CAPS = {
624
799
  LOW: "LOW",
@@ -644,7 +819,8 @@ async function createCoinCall({
644
819
  payoutRecipientOverride,
645
820
  additionalOwners,
646
821
  platformReferrer,
647
- skipMetadataValidation = false
822
+ skipMetadataValidation = false,
823
+ enableSmartWalletRouting
648
824
  }) {
649
825
  if (!skipMetadataValidation) {
650
826
  await validateMetadataURIContent(metadata.uri);
@@ -658,7 +834,8 @@ async function createCoinCall({
658
834
  symbol,
659
835
  platformReferrer,
660
836
  additionalOwners,
661
- payoutRecipientOverride
837
+ payoutRecipientOverride,
838
+ enableSmartWalletRouting
662
839
  });
663
840
  if (!createContentRequest.data?.calls) {
664
841
  throw new Error("Failed to create content calldata");
@@ -669,36 +846,19 @@ async function createCoinCall({
669
846
  data: data.data,
670
847
  value: BigInt(data.value)
671
848
  })),
672
- predictedCoinAddress: createContentRequest.data.predictedCoinAddress
849
+ predictedCoinAddress: createContentRequest.data.predictedCoinAddress,
850
+ usedSmartWalletRouting: createContentRequest.data.usedSmartWalletRouting
673
851
  };
674
852
  }
675
- function getCoinCreateFromLogs(receipt) {
676
- const eventLogs = parseEventLogs({
677
- abi: zoraFactoryImplABI,
678
- logs: receipt.logs
679
- });
680
- return eventLogs.find((log) => log.eventName === "CoinCreatedV4")?.args;
681
- }
682
- async function createCoin({
683
- call,
684
- walletClient,
685
- publicClient,
686
- options
687
- }) {
688
- validateClientNetwork(publicClient);
689
- const chainId = call.chainId ?? publicClient.chain.id;
690
- const callRequest = await createCoinCall({
691
- ...call,
692
- chainId
693
- });
694
- if (callRequest.calls.length !== 1) {
853
+ function validateCreateCoinCalls(calls, chainId) {
854
+ if (calls.length !== 1) {
695
855
  throw new Error("Only one call is supported for this SDK version");
696
856
  }
697
- const createContentCall = callRequest.calls[0];
857
+ const createContentCall = calls[0];
698
858
  if (!createContentCall) {
699
859
  throw new Error("Failed to load create content calldata from API");
700
860
  }
701
- const coinFactoryAddressForChain = coinFactoryAddress[call.chainId];
861
+ const coinFactoryAddressForChain = coinFactoryAddress[chainId];
702
862
  if (!isAddressEqual(createContentCall.to, coinFactoryAddressForChain)) {
703
863
  throw new Error("Creator coin is not supported for this SDK version");
704
864
  }
@@ -707,22 +867,87 @@ async function createCoin({
707
867
  "Creator coin and purchase is not supported for this SDK version."
708
868
  );
709
869
  }
710
- const selectedAccount = (typeof options?.account === "string" ? void 0 : options?.account) ?? walletClient.account;
711
- if (!selectedAccount) {
870
+ }
871
+ function validateCreateCoinSmartWalletCalls(calls, { usedSmartWalletRouting }) {
872
+ if (!usedSmartWalletRouting) {
873
+ throw new Error(
874
+ "Smart wallet routing was not applied. The creator must have a linked smart wallet; otherwise use createCoin for EOA creation."
875
+ );
876
+ }
877
+ if (calls.length !== 1) {
878
+ throw new Error("Only one call is supported for this SDK version");
879
+ }
880
+ const createContentCall = calls[0];
881
+ if (!createContentCall) {
882
+ throw new Error("Failed to load create content calldata from API");
883
+ }
884
+ if (createContentCall.value !== 0n) {
885
+ throw new Error(
886
+ "Creator coin and purchase is not supported for this SDK version."
887
+ );
888
+ }
889
+ }
890
+ function getCoinCreateFromLogs(receipt) {
891
+ const eventLogs = parseEventLogs({
892
+ abi: zoraFactoryImplABI,
893
+ logs: receipt.logs
894
+ });
895
+ return eventLogs.find((log) => log.eventName === "CoinCreatedV4")?.args;
896
+ }
897
+ var coinbaseSmartWalletExecuteABI = [
898
+ {
899
+ type: "function",
900
+ name: "execute",
901
+ stateMutability: "payable",
902
+ inputs: [
903
+ { name: "target", type: "address" },
904
+ { name: "value", type: "uint256" },
905
+ { name: "data", type: "bytes" }
906
+ ],
907
+ outputs: []
908
+ }
909
+ ];
910
+ function unwrapSmartWalletExecuteCall(call) {
911
+ let decoded;
912
+ try {
913
+ decoded = decodeFunctionData({
914
+ abi: coinbaseSmartWalletExecuteABI,
915
+ data: call.data
916
+ });
917
+ } catch {
918
+ throw new Error(
919
+ "Expected a smart wallet `execute` call from smart wallet routing, but the routed call could not be decoded."
920
+ );
921
+ }
922
+ const [target, value, data] = decoded.args;
923
+ return { to: target, value, data };
924
+ }
925
+ function selectExecutionAccount(walletClient, account) {
926
+ const selected = (typeof account === "string" ? void 0 : account) ?? walletClient.account;
927
+ if (!selected) {
712
928
  throw new Error("Account is required");
713
929
  }
930
+ return selected;
931
+ }
932
+ async function executeCreateContentCall({
933
+ createContentCall,
934
+ account,
935
+ walletClient,
936
+ publicClient,
937
+ skipValidateTransaction
938
+ }) {
714
939
  const viemCall = {
715
940
  ...createContentCall,
716
- account: selectedAccount
941
+ account
717
942
  };
718
- if (!options?.skipValidateTransaction) {
943
+ if (!skipValidateTransaction) {
719
944
  try {
720
945
  await publicClient.call(viemCall);
721
946
  } catch (err) {
722
947
  rethrowDecodedRevert(err, zoraFactoryImplABI);
723
948
  }
724
949
  }
725
- const gasEstimate = options?.skipValidateTransaction ? 10000000n : await publicClient.estimateGas(viemCall);
950
+ const gasEstimate = skipValidateTransaction ? 10000000n : await publicClient.estimateGas(viemCall);
726
951
  const gasPrice = await publicClient.getGasPrice();
727
952
  const hash = await (async () => {
728
953
  try {
@@ -748,6 +973,77 @@ async function createCoin({
748
973
  chain: getChainFromId(publicClient.chain.id)
749
974
  };
750
975
  }
976
+ async function createCoin({
977
+ call,
978
+ walletClient,
979
+ publicClient,
980
+ options
981
+ }) {
982
+ validateClientNetwork(publicClient);
983
+ const chainId = call.chainId ?? publicClient.chain.id;
984
+ const { calls } = await createCoinCall({
985
+ ...call,
986
+ chainId
987
+ });
988
+ validateCreateCoinCalls(calls, chainId);
989
+ const createContentCall = calls[0];
990
+ const account = selectExecutionAccount(walletClient, options?.account);
991
+ return executeCreateContentCall({
992
+ createContentCall,
993
+ account,
994
+ walletClient,
995
+ publicClient,
996
+ skipValidateTransaction: options?.skipValidateTransaction
997
+ });
998
+ }
999
+ async function createCoinSmartWallet({
1000
+ call,
1001
+ bundlerClient,
1002
+ publicClient
1003
+ }) {
1004
+ validateClientNetwork(publicClient);
1005
+ const account = bundlerClient.account;
1006
+ if (!account) {
1007
+ throw new Error("Account is required: the bundler client has no account");
1008
+ }
1009
+ const chainId = call.chainId ?? publicClient.chain.id;
1010
+ const { calls, usedSmartWalletRouting } = await createCoinCall({
1011
+ ...call,
1012
+ chainId,
1013
+ enableSmartWalletRouting: true
1014
+ });
1015
+ validateCreateCoinSmartWalletCalls(calls, { usedSmartWalletRouting });
1016
+ const innerCall = unwrapSmartWalletExecuteCall(calls[0]);
1017
+ const userOperation = await prepareUserOperation({
1018
+ bundlerClient,
1019
+ account,
1020
+ calls: toUserOperationCalls([innerCall])
1021
+ });
1022
+ const userOpReceipt = await submitUserOperation({
1023
+ bundlerClient,
1024
+ account,
1025
+ userOperation
1026
+ });
1027
+ if (!userOpReceipt.success) {
1028
+ throw new Error(
1029
+ `User operation reverted${userOpReceipt.reason ? `: ${userOpReceipt.reason}` : ""}`
1030
+ );
1031
+ }
1032
+ const eventLogs = parseEventLogs({
1033
+ abi: zoraFactoryImplABI,
1034
+ logs: userOpReceipt.logs
1035
+ });
1036
+ const deployment = eventLogs.find(
1037
+ (log) => log.eventName === "CoinCreatedV4"
1038
+ )?.args;
1039
+ return {
1040
+ hash: userOpReceipt.receipt.transactionHash,
1041
+ receipt: userOpReceipt.receipt,
1042
+ address: deployment?.coin,
1043
+ deployment,
1044
+ chain: getChainFromId(publicClient.chain.id)
1045
+ };
1046
+ }
751
1047
 
752
1048
  // src/actions/updateCoinURI.ts
753
1049
  import { coinABI } from "@zoralabs/protocol-deployments";
@@ -763,13 +1059,14 @@ function getAttribution() {
763
1059
  }
764
1060
 
765
1061
  // src/actions/updateCoinURI.ts
766
- function updateCoinURICall({
767
- newURI,
768
- coin
769
- }) {
1062
+ function validateUpdateCoinURI({ newURI }) {
770
1063
  if (!newURI.startsWith("ipfs://")) {
771
1064
  throw new Error("URI needs to be an ipfs:// prefix uri");
772
1065
  }
1066
+ }
1067
+ function updateCoinURICall(args) {
1068
+ validateUpdateCoinURI(args);
1069
+ const { coin, newURI } = args;
773
1070
  return {
774
1071
  abi: coinABI,
775
1072
  address: coin,
@@ -793,16 +1090,56 @@ async function updateCoinURI(args, walletClient, publicClient, account) {
793
1090
  );
794
1091
  return { hash, receipt, uriUpdated };
795
1092
  }
1093
+ async function updateCoinURISmartWallet(args, bundlerClient, publicClient, account) {
1094
+ const resolvedAccount = account ?? bundlerClient.account;
1095
+ if (!resolvedAccount) {
1096
+ throw new Error("Account is required");
1097
+ }
1098
+ validateClientNetwork(publicClient);
1099
+ const call = updateCoinURICall(args);
1100
+ const calls = toUserOperationCalls([toGenericCall(call)]);
1101
+ const userOp = await prepareUserOperation({
1102
+ bundlerClient,
1103
+ account: resolvedAccount,
1104
+ calls
1105
+ });
1106
+ const userOpReceipt = await submitUserOperation({
1107
+ bundlerClient,
1108
+ account: resolvedAccount,
1109
+ userOperation: userOp
1110
+ });
1111
+ if (!userOpReceipt.success) {
1112
+ throw new Error(
1113
+ `User operation reverted${userOpReceipt.reason ? `: ${userOpReceipt.reason}` : ""}`
1114
+ );
1115
+ }
1116
+ const eventLogs = parseEventLogs2({ abi: coinABI, logs: userOpReceipt.logs });
1117
+ const uriUpdated = eventLogs.find(
1118
+ (log) => log.eventName === "ContractURIUpdated"
1119
+ );
1120
+ return {
1121
+ hash: userOpReceipt.receipt.transactionHash,
1122
+ receipt: userOpReceipt.receipt,
1123
+ uriUpdated
1124
+ };
1125
+ }
796
1126
 
797
1127
  // src/actions/updatePayoutRecipient.ts
798
1128
  import { coinABI as coinABI2 } from "@zoralabs/protocol-deployments";
799
1129
  import {
1130
+ isAddress,
800
1131
  parseEventLogs as parseEventLogs3
801
1132
  } from "viem";
802
- function updatePayoutRecipientCall({
803
- newPayoutRecipient,
804
- coin
1133
+ function validateUpdatePayoutRecipient({
1134
+ newPayoutRecipient
805
1135
  }) {
1136
+ if (!isAddress(newPayoutRecipient)) {
1137
+ throw new Error("Payout recipient must be a valid address");
1138
+ }
1139
+ }
1140
+ function updatePayoutRecipientCall(args) {
1141
+ validateUpdatePayoutRecipient(args);
1142
+ const { coin, newPayoutRecipient } = args;
806
1143
  return {
807
1144
  abi: coinABI2,
808
1145
  address: coin,
@@ -826,10 +1163,44 @@ async function updatePayoutRecipient(args, walletClient, publicClient, account)
826
1163
  );
827
1164
  return { hash, receipt, payoutRecipientUpdated };
828
1165
  }
1166
+ async function updatePayoutRecipientSmartWallet(args, bundlerClient, publicClient, account) {
1167
+ const resolvedAccount = account ?? bundlerClient.account;
1168
+ if (!resolvedAccount) {
1169
+ throw new Error("Account is required");
1170
+ }
1171
+ validateClientNetwork(publicClient);
1172
+ const call = updatePayoutRecipientCall(args);
1173
+ const calls = toUserOperationCalls([toGenericCall(call)]);
1174
+ const userOp = await prepareUserOperation({
1175
+ bundlerClient,
1176
+ account: resolvedAccount,
1177
+ calls
1178
+ });
1179
+ const userOpReceipt = await submitUserOperation({
1180
+ bundlerClient,
1181
+ account: resolvedAccount,
1182
+ userOperation: userOp
1183
+ });
1184
+ if (!userOpReceipt.success) {
1185
+ throw new Error(
1186
+ `User operation reverted${userOpReceipt.reason ? `: ${userOpReceipt.reason}` : ""}`
1187
+ );
1188
+ }
1189
+ const eventLogs = parseEventLogs3({ abi: coinABI2, logs: userOpReceipt.logs });
1190
+ const payoutRecipientUpdated = eventLogs.find(
1191
+ (log) => log.eventName === "CoinPayoutRecipientUpdated"
1192
+ );
1193
+ return {
1194
+ hash: userOpReceipt.receipt.transactionHash,
1195
+ receipt: userOpReceipt.receipt,
1196
+ payoutRecipientUpdated
1197
+ };
1198
+ }
829
1199
 
830
1200
  // src/actions/tradeCoin.ts
831
1201
  import { permit2ABI, permit2Address } from "@zoralabs/protocol-deployments";
832
1202
  import {
1203
+ encodeFunctionData as encodeFunctionData2,
833
1204
  erc20Abi,
834
1205
  maxUint256
835
1206
  } from "viem";
@@ -857,6 +1228,73 @@ var PERMIT_SINGLE_TYPES = {
857
1228
  { name: "nonce", type: "uint48" }
858
1229
  ]
859
1230
  };
1231
+ async function resolveTradePermits({
1232
+ quote,
1233
+ owner,
1234
+ publicClient,
1235
+ signTypedData
1236
+ }) {
1237
+ const signatures = [];
1238
+ const approvalCalls = [];
1239
+ if (!quote.permits) {
1240
+ return { signatures, approvalCalls };
1241
+ }
1242
+ for (const permit of quote.permits) {
1243
+ const [, , nonce] = await publicClient.readContract({
1244
+ abi: permit2ABI,
1245
+ address: permit2Address[base4.id],
1246
+ functionName: "allowance",
1247
+ args: [
1248
+ owner,
1249
+ permit.permit.details.token,
1250
+ permit.permit.spender
1251
+ ]
1252
+ });
1253
+ const permitToken = permit.permit.details.token;
1254
+ const allowance = await publicClient.readContract({
1255
+ abi: erc20Abi,
1256
+ address: permitToken,
1257
+ functionName: "allowance",
1258
+ args: [owner, permit2Address[base4.id]]
1259
+ });
1260
+ if (allowance < BigInt(permit.permit.details.amount)) {
1261
+ approvalCalls.push({
1262
+ to: permitToken,
1263
+ data: encodeFunctionData2({
1264
+ abi: erc20Abi,
1265
+ functionName: "approve",
1266
+ args: [permit2Address[base4.id], maxUint256]
1267
+ }),
1268
+ value: 0n
1269
+ });
1270
+ }
1271
+ const message = {
1272
+ details: {
1273
+ token: permit.permit.details.token,
1274
+ amount: BigInt(permit.permit.details.amount),
1275
+ expiration: Number(permit.permit.details.expiration),
1276
+ nonce
1277
+ },
1278
+ spender: permit.permit.spender,
1279
+ sigDeadline: BigInt(permit.permit.sigDeadline)
1280
+ };
1281
+ const signature = await signTypedData({
1282
+ domain: {
1283
+ name: "Permit2",
1284
+ chainId: base4.id,
1285
+ verifyingContract: permit2Address[base4.id]
1286
+ },
1287
+ primaryType: "PermitSingle",
1288
+ types: PERMIT_SINGLE_TYPES,
1289
+ message
1290
+ });
1291
+ signatures.push({
1292
+ signature,
1293
+ permit: convertBigIntToString(message)
1294
+ });
1295
+ }
1296
+ return { signatures, approvalCalls };
1297
+ }
860
1298
  async function tradeCoin({
861
1299
  tradeParameters,
862
1300
  walletClient,
@@ -871,71 +1309,24 @@ async function tradeCoin({
871
1309
  if (!account) {
872
1310
  throw new Error("Account is required");
873
1311
  }
1312
+ const resolvedAccount = account;
1313
+ const owner = typeof resolvedAccount === "string" ? resolvedAccount : resolvedAccount.address;
874
1314
  if (!tradeParameters.recipient) {
875
- tradeParameters.recipient = typeof account === "string" ? account : account.address;
1315
+ tradeParameters.recipient = owner;
876
1316
  }
877
- const signatures = [];
878
- if (quote.permits) {
879
- for (const permit of quote.permits) {
880
- const [, , nonce] = await publicClient.readContract({
881
- abi: permit2ABI,
882
- address: permit2Address[base4.id],
883
- functionName: "allowance",
884
- args: [
885
- typeof account === "string" ? account : account.address,
886
- permit.permit.details.token,
887
- permit.permit.spender
888
- ]
889
- });
890
- const permitToken = permit.permit.details.token;
891
- const allowance = await publicClient.readContract({
892
- abi: erc20Abi,
893
- address: permitToken,
894
- functionName: "allowance",
895
- args: [
896
- typeof account === "string" ? account : account.address,
897
- permit2Address[base4.id]
898
- ]
899
- });
900
- if (allowance < BigInt(permit.permit.details.amount)) {
901
- const approvalTx = await walletClient.writeContract({
902
- abi: erc20Abi,
903
- address: permitToken,
904
- functionName: "approve",
905
- chain: base4,
906
- args: [permit2Address[base4.id], maxUint256],
907
- account
908
- });
909
- await publicClient.waitForTransactionReceipt({
910
- hash: approvalTx
911
- });
912
- }
913
- const message = {
914
- details: {
915
- token: permit.permit.details.token,
916
- amount: BigInt(permit.permit.details.amount),
917
- expiration: Number(permit.permit.details.expiration),
918
- nonce
919
- },
920
- spender: permit.permit.spender,
921
- sigDeadline: BigInt(permit.permit.sigDeadline)
922
- };
923
- const signature = await walletClient.signTypedData({
924
- domain: {
925
- name: "Permit2",
926
- chainId: base4.id,
927
- verifyingContract: permit2Address[base4.id]
928
- },
929
- primaryType: "PermitSingle",
930
- types: PERMIT_SINGLE_TYPES,
931
- message,
932
- account
933
- });
934
- signatures.push({
935
- signature,
936
- permit: convertBigIntToString(message)
937
- });
938
- }
1317
+ const { signatures, approvalCalls } = await resolveTradePermits({
1318
+ quote,
1319
+ owner,
1320
+ publicClient,
1321
+ signTypedData: (typedData) => walletClient.signTypedData({ ...typedData, account: resolvedAccount })
1322
+ });
1323
+ for (const approvalCall of approvalCalls) {
1324
+ const approvalTx = await walletClient.sendTransaction({
1325
+ ...approvalCall,
1326
+ account: resolvedAccount,
1327
+ chain: base4
1328
+ });
1329
+ await publicClient.waitForTransactionReceipt({ hash: approvalTx });
939
1330
  }
940
1331
  const newQuote = await createTradeCall({
941
1332
  ...tradeParameters,
@@ -946,7 +1337,7 @@ async function tradeCoin({
946
1337
  data: newQuote.call.data,
947
1338
  value: BigInt(newQuote.call.value),
948
1339
  chain: base4,
949
- account
1340
+ account: resolvedAccount
950
1341
  };
951
1342
  if (validateTransaction) {
952
1343
  await publicClient.call(call);
@@ -963,13 +1354,69 @@ async function tradeCoin({
963
1354
  });
964
1355
  return receipt;
965
1356
  }
966
- async function createTradeCall(tradeParameters) {
1357
+ async function tradeCoinSmartWallet({
1358
+ tradeParameters,
1359
+ bundlerClient,
1360
+ account,
1361
+ publicClient
1362
+ }) {
1363
+ const resolvedAccount = account ?? bundlerClient.account;
1364
+ if (!resolvedAccount) {
1365
+ throw new Error("Account is required");
1366
+ }
1367
+ const owner = resolvedAccount.address;
1368
+ const params = {
1369
+ ...tradeParameters,
1370
+ sender: owner,
1371
+ recipient: tradeParameters.recipient ?? owner
1372
+ };
1373
+ const quote = await createTradeCall(params);
1374
+ const { signatures, approvalCalls } = await resolveTradePermits({
1375
+ quote,
1376
+ owner,
1377
+ publicClient,
1378
+ signTypedData: (typedData) => resolvedAccount.signTypedData(typedData)
1379
+ });
1380
+ const newQuote = await createTradeCall({
1381
+ ...params,
1382
+ signatures
1383
+ });
1384
+ const tradeCall = {
1385
+ to: newQuote.call.target,
1386
+ data: newQuote.call.data,
1387
+ value: BigInt(newQuote.call.value)
1388
+ };
1389
+ const calls = toUserOperationCalls([...approvalCalls, tradeCall]);
1390
+ const userOp = await prepareUserOperation({
1391
+ bundlerClient,
1392
+ account: resolvedAccount,
1393
+ calls
1394
+ });
1395
+ const userOpReceipt = await submitUserOperation({
1396
+ bundlerClient,
1397
+ account: resolvedAccount,
1398
+ userOperation: userOp
1399
+ });
1400
+ if (!userOpReceipt.success) {
1401
+ throw new Error(
1402
+ `User operation reverted${userOpReceipt.reason ? `: ${userOpReceipt.reason}` : ""}`
1403
+ );
1404
+ }
1405
+ return userOpReceipt.receipt;
1406
+ }
1407
+ function validateTradeParameters(tradeParameters) {
967
1408
  if (tradeParameters.slippage && tradeParameters.slippage > 1) {
968
1409
  throw new Error("Slippage must be less than 1, max 0.99");
969
1410
  }
970
1411
  if (tradeParameters.amountIn === BigInt(0)) {
971
1412
  throw new Error("Amount in must be greater than 0");
972
1413
  }
1414
+ }
1415
+ async function createTradeCall(tradeParameters) {
1416
+ return createQuote(tradeParameters);
1417
+ }
1418
+ async function createQuote(tradeParameters) {
1419
+ validateTradeParameters(tradeParameters);
973
1420
  const quote = await postQuote({
974
1421
  body: {
975
1422
  tokenIn: tradeParameters.sell,
@@ -998,6 +1445,12 @@ async function createTradeCall(tradeParameters) {
998
1445
  import { createConfig as createConfig2 } from "@hey-api/client-fetch";
999
1446
  var apiGet = (path, data) => client.get({ url: path, query: data, ...getApiKeyMeta() });
1000
1447
  var apiPost = (path, data) => client.post({ url: path, body: data, ...getApiKeyMeta() });
1448
+ var apiUrl = (path) => {
1449
+ const baseUrl = client.getConfig().baseUrl ?? "";
1450
+ const normalizedBase = baseUrl.replace(/\/+$/, "");
1451
+ const normalizedPath = path.replace(/^\/+/, "");
1452
+ return `${normalizedBase}/${normalizedPath}`;
1453
+ };
1001
1454
  var setApiBaseUrl = (baseUrl) => {
1002
1455
  client.setConfig(createConfig2({ baseUrl }));
1003
1456
  };
@@ -1250,20 +1703,25 @@ function createZoraUploaderForCreator(creatorAddress) {
1250
1703
  }
1251
1704
  export {
1252
1705
  CoinMetadataBuilder,
1706
+ CoinbaseGasError,
1253
1707
  CreateConstants,
1254
1708
  ZoraUploader,
1255
1709
  apiGet,
1256
1710
  apiPost,
1711
+ apiUrl,
1257
1712
  cleanAndValidateMetadataURI,
1258
1713
  createCoin,
1259
1714
  createCoinCall,
1715
+ createCoinSmartWallet,
1260
1716
  createMetadataBuilder,
1717
+ createQuote,
1261
1718
  createTradeCall,
1262
1719
  createZoraUploaderForCreator,
1263
1720
  getCoin2 as getCoin,
1264
1721
  getCoinComments2 as getCoinComments,
1265
1722
  getCoinCreateFromLogs,
1266
1723
  getCoinHolders2 as getCoinHolders,
1724
+ getCoinPriceHistory2 as getCoinPriceHistory,
1267
1725
  getCoinSwaps2 as getCoinSwaps,
1268
1726
  getCoins2 as getCoins,
1269
1727
  getCoinsLastTraded,
@@ -1289,6 +1747,7 @@ export {
1289
1747
  getNewTrends,
1290
1748
  getProfile2 as getProfile,
1291
1749
  getProfileBalances2 as getProfileBalances,
1750
+ getProfileBySocialHandle2 as getProfileBySocialHandle,
1292
1751
  getProfileCoins2 as getProfileCoins,
1293
1752
  getProfileSocial2 as getProfileSocial,
1294
1753
  getTokenInfo2 as getTokenInfo,
@@ -1301,15 +1760,30 @@ export {
1301
1760
  getTrendingTrends,
1302
1761
  getTrends,
1303
1762
  getURLFromUploadResult,
1763
+ getWalletTradeActivity2 as getWalletTradeActivity,
1764
+ isContractCall,
1765
+ isSendCall,
1766
+ prepareUserOperation,
1304
1767
  setApiBaseUrl,
1305
1768
  setApiKey,
1769
+ submitUserOperation,
1770
+ toGenericCall,
1771
+ toUserOperationCalls,
1306
1772
  tradeCoin,
1773
+ tradeCoinSmartWallet,
1307
1774
  updateCoinURI,
1308
1775
  updateCoinURICall,
1776
+ updateCoinURISmartWallet,
1309
1777
  updatePayoutRecipient,
1310
1778
  updatePayoutRecipientCall,
1779
+ updatePayoutRecipientSmartWallet,
1780
+ validateCreateCoinCalls,
1781
+ validateCreateCoinSmartWalletCalls,
1311
1782
  validateImageMimeType,
1312
1783
  validateMetadataJSON,
1313
- validateMetadataURIContent
1784
+ validateMetadataURIContent,
1785
+ validateTradeParameters,
1786
+ validateUpdateCoinURI,
1787
+ validateUpdatePayoutRecipient
1314
1788
  };
1315
1789
  //# sourceMappingURL=index.js.map