@zoralabs/coins-sdk 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/actions/createCoin.d.ts +92 -7
- package/dist/actions/createCoin.d.ts.map +1 -1
- package/dist/actions/tradeCoin.d.ts +27 -2
- package/dist/actions/tradeCoin.d.ts.map +1 -1
- package/dist/actions/updateCoinURI.d.ts +37 -1
- package/dist/actions/updateCoinURI.d.ts.map +1 -1
- package/dist/actions/updatePayoutRecipient.d.ts +43 -1
- package/dist/actions/updatePayoutRecipient.d.ts.map +1 -1
- package/dist/api/api-raw.d.ts +1 -0
- package/dist/api/api-raw.d.ts.map +1 -1
- package/dist/index.cjs +655 -243
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +9 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +639 -227
- package/dist/index.js.map +1 -1
- package/dist/metadata/validateMetadataURIContent.d.ts.map +1 -1
- package/dist/utils/calls.d.ts +61 -0
- package/dist/utils/calls.d.ts.map +1 -0
- package/dist/utils/userOperation.d.ts +44 -0
- package/dist/utils/userOperation.d.ts.map +1 -0
- package/package.json +3 -3
- package/src/actions/createCoin.ts +314 -60
- package/src/actions/tradeCoin.ts +237 -72
- package/src/actions/updateCoinURI.ts +84 -6
- package/src/actions/updatePayoutRecipient.ts +92 -5
- package/src/api/api-raw.test.ts +61 -0
- package/src/api/api-raw.ts +9 -0
- package/src/index.ts +41 -3
- package/src/metadata/validateMetadataURIContent.ts +17 -2
- package/src/utils/calls.ts +129 -0
- package/src/utils/userOperation.test.ts +84 -0
- 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
|
-
|
|
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,
|
|
@@ -643,6 +522,160 @@ var getProfileBySocialHandle2 = async (query, options) => {
|
|
|
643
522
|
});
|
|
644
523
|
};
|
|
645
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
|
+
|
|
646
679
|
// src/utils/rethrowDecodedRevert.ts
|
|
647
680
|
import {
|
|
648
681
|
BaseError,
|
|
@@ -678,6 +711,89 @@ function rethrowDecodedRevert(err, abi) {
|
|
|
678
711
|
throw err;
|
|
679
712
|
}
|
|
680
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
|
+
|
|
681
797
|
// src/actions/createCoin.ts
|
|
682
798
|
var STARTING_MARKET_CAPS = {
|
|
683
799
|
LOW: "LOW",
|
|
@@ -703,7 +819,8 @@ async function createCoinCall({
|
|
|
703
819
|
payoutRecipientOverride,
|
|
704
820
|
additionalOwners,
|
|
705
821
|
platformReferrer,
|
|
706
|
-
skipMetadataValidation = false
|
|
822
|
+
skipMetadataValidation = false,
|
|
823
|
+
enableSmartWalletRouting
|
|
707
824
|
}) {
|
|
708
825
|
if (!skipMetadataValidation) {
|
|
709
826
|
await validateMetadataURIContent(metadata.uri);
|
|
@@ -717,7 +834,8 @@ async function createCoinCall({
|
|
|
717
834
|
symbol,
|
|
718
835
|
platformReferrer,
|
|
719
836
|
additionalOwners,
|
|
720
|
-
payoutRecipientOverride
|
|
837
|
+
payoutRecipientOverride,
|
|
838
|
+
enableSmartWalletRouting
|
|
721
839
|
});
|
|
722
840
|
if (!createContentRequest.data?.calls) {
|
|
723
841
|
throw new Error("Failed to create content calldata");
|
|
@@ -728,36 +846,19 @@ async function createCoinCall({
|
|
|
728
846
|
data: data.data,
|
|
729
847
|
value: BigInt(data.value)
|
|
730
848
|
})),
|
|
731
|
-
predictedCoinAddress: createContentRequest.data.predictedCoinAddress
|
|
849
|
+
predictedCoinAddress: createContentRequest.data.predictedCoinAddress,
|
|
850
|
+
usedSmartWalletRouting: createContentRequest.data.usedSmartWalletRouting
|
|
732
851
|
};
|
|
733
852
|
}
|
|
734
|
-
function
|
|
735
|
-
|
|
736
|
-
abi: zoraFactoryImplABI,
|
|
737
|
-
logs: receipt.logs
|
|
738
|
-
});
|
|
739
|
-
return eventLogs.find((log) => log.eventName === "CoinCreatedV4")?.args;
|
|
740
|
-
}
|
|
741
|
-
async function createCoin({
|
|
742
|
-
call,
|
|
743
|
-
walletClient,
|
|
744
|
-
publicClient,
|
|
745
|
-
options
|
|
746
|
-
}) {
|
|
747
|
-
validateClientNetwork(publicClient);
|
|
748
|
-
const chainId = call.chainId ?? publicClient.chain.id;
|
|
749
|
-
const callRequest = await createCoinCall({
|
|
750
|
-
...call,
|
|
751
|
-
chainId
|
|
752
|
-
});
|
|
753
|
-
if (callRequest.calls.length !== 1) {
|
|
853
|
+
function validateCreateCoinCalls(calls, chainId) {
|
|
854
|
+
if (calls.length !== 1) {
|
|
754
855
|
throw new Error("Only one call is supported for this SDK version");
|
|
755
856
|
}
|
|
756
|
-
const createContentCall =
|
|
857
|
+
const createContentCall = calls[0];
|
|
757
858
|
if (!createContentCall) {
|
|
758
859
|
throw new Error("Failed to load create content calldata from API");
|
|
759
860
|
}
|
|
760
|
-
const coinFactoryAddressForChain = coinFactoryAddress[
|
|
861
|
+
const coinFactoryAddressForChain = coinFactoryAddress[chainId];
|
|
761
862
|
if (!isAddressEqual(createContentCall.to, coinFactoryAddressForChain)) {
|
|
762
863
|
throw new Error("Creator coin is not supported for this SDK version");
|
|
763
864
|
}
|
|
@@ -766,22 +867,87 @@ async function createCoin({
|
|
|
766
867
|
"Creator coin and purchase is not supported for this SDK version."
|
|
767
868
|
);
|
|
768
869
|
}
|
|
769
|
-
|
|
770
|
-
|
|
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) {
|
|
771
928
|
throw new Error("Account is required");
|
|
772
929
|
}
|
|
930
|
+
return selected;
|
|
931
|
+
}
|
|
932
|
+
async function executeCreateContentCall({
|
|
933
|
+
createContentCall,
|
|
934
|
+
account,
|
|
935
|
+
walletClient,
|
|
936
|
+
publicClient,
|
|
937
|
+
skipValidateTransaction
|
|
938
|
+
}) {
|
|
773
939
|
const viemCall = {
|
|
774
940
|
...createContentCall,
|
|
775
|
-
account
|
|
941
|
+
account
|
|
776
942
|
};
|
|
777
|
-
if (!
|
|
943
|
+
if (!skipValidateTransaction) {
|
|
778
944
|
try {
|
|
779
945
|
await publicClient.call(viemCall);
|
|
780
946
|
} catch (err) {
|
|
781
947
|
rethrowDecodedRevert(err, zoraFactoryImplABI);
|
|
782
948
|
}
|
|
783
949
|
}
|
|
784
|
-
const gasEstimate =
|
|
950
|
+
const gasEstimate = skipValidateTransaction ? 10000000n : await publicClient.estimateGas(viemCall);
|
|
785
951
|
const gasPrice = await publicClient.getGasPrice();
|
|
786
952
|
const hash = await (async () => {
|
|
787
953
|
try {
|
|
@@ -807,6 +973,77 @@ async function createCoin({
|
|
|
807
973
|
chain: getChainFromId(publicClient.chain.id)
|
|
808
974
|
};
|
|
809
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
|
+
}
|
|
810
1047
|
|
|
811
1048
|
// src/actions/updateCoinURI.ts
|
|
812
1049
|
import { coinABI } from "@zoralabs/protocol-deployments";
|
|
@@ -822,13 +1059,14 @@ function getAttribution() {
|
|
|
822
1059
|
}
|
|
823
1060
|
|
|
824
1061
|
// src/actions/updateCoinURI.ts
|
|
825
|
-
function
|
|
826
|
-
newURI,
|
|
827
|
-
coin
|
|
828
|
-
}) {
|
|
1062
|
+
function validateUpdateCoinURI({ newURI }) {
|
|
829
1063
|
if (!newURI.startsWith("ipfs://")) {
|
|
830
1064
|
throw new Error("URI needs to be an ipfs:// prefix uri");
|
|
831
1065
|
}
|
|
1066
|
+
}
|
|
1067
|
+
function updateCoinURICall(args) {
|
|
1068
|
+
validateUpdateCoinURI(args);
|
|
1069
|
+
const { coin, newURI } = args;
|
|
832
1070
|
return {
|
|
833
1071
|
abi: coinABI,
|
|
834
1072
|
address: coin,
|
|
@@ -852,16 +1090,56 @@ async function updateCoinURI(args, walletClient, publicClient, account) {
|
|
|
852
1090
|
);
|
|
853
1091
|
return { hash, receipt, uriUpdated };
|
|
854
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
|
+
}
|
|
855
1126
|
|
|
856
1127
|
// src/actions/updatePayoutRecipient.ts
|
|
857
1128
|
import { coinABI as coinABI2 } from "@zoralabs/protocol-deployments";
|
|
858
1129
|
import {
|
|
1130
|
+
isAddress,
|
|
859
1131
|
parseEventLogs as parseEventLogs3
|
|
860
1132
|
} from "viem";
|
|
861
|
-
function
|
|
862
|
-
newPayoutRecipient
|
|
863
|
-
coin
|
|
1133
|
+
function validateUpdatePayoutRecipient({
|
|
1134
|
+
newPayoutRecipient
|
|
864
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;
|
|
865
1143
|
return {
|
|
866
1144
|
abi: coinABI2,
|
|
867
1145
|
address: coin,
|
|
@@ -885,10 +1163,44 @@ async function updatePayoutRecipient(args, walletClient, publicClient, account)
|
|
|
885
1163
|
);
|
|
886
1164
|
return { hash, receipt, payoutRecipientUpdated };
|
|
887
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
|
+
}
|
|
888
1199
|
|
|
889
1200
|
// src/actions/tradeCoin.ts
|
|
890
1201
|
import { permit2ABI, permit2Address } from "@zoralabs/protocol-deployments";
|
|
891
1202
|
import {
|
|
1203
|
+
encodeFunctionData as encodeFunctionData2,
|
|
892
1204
|
erc20Abi,
|
|
893
1205
|
maxUint256
|
|
894
1206
|
} from "viem";
|
|
@@ -916,6 +1228,73 @@ var PERMIT_SINGLE_TYPES = {
|
|
|
916
1228
|
{ name: "nonce", type: "uint48" }
|
|
917
1229
|
]
|
|
918
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
|
+
}
|
|
919
1298
|
async function tradeCoin({
|
|
920
1299
|
tradeParameters,
|
|
921
1300
|
walletClient,
|
|
@@ -930,71 +1309,24 @@ async function tradeCoin({
|
|
|
930
1309
|
if (!account) {
|
|
931
1310
|
throw new Error("Account is required");
|
|
932
1311
|
}
|
|
1312
|
+
const resolvedAccount = account;
|
|
1313
|
+
const owner = typeof resolvedAccount === "string" ? resolvedAccount : resolvedAccount.address;
|
|
933
1314
|
if (!tradeParameters.recipient) {
|
|
934
|
-
tradeParameters.recipient =
|
|
1315
|
+
tradeParameters.recipient = owner;
|
|
935
1316
|
}
|
|
936
|
-
const signatures =
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
const permitToken = permit.permit.details.token;
|
|
950
|
-
const allowance = await publicClient.readContract({
|
|
951
|
-
abi: erc20Abi,
|
|
952
|
-
address: permitToken,
|
|
953
|
-
functionName: "allowance",
|
|
954
|
-
args: [
|
|
955
|
-
typeof account === "string" ? account : account.address,
|
|
956
|
-
permit2Address[base4.id]
|
|
957
|
-
]
|
|
958
|
-
});
|
|
959
|
-
if (allowance < BigInt(permit.permit.details.amount)) {
|
|
960
|
-
const approvalTx = await walletClient.writeContract({
|
|
961
|
-
abi: erc20Abi,
|
|
962
|
-
address: permitToken,
|
|
963
|
-
functionName: "approve",
|
|
964
|
-
chain: base4,
|
|
965
|
-
args: [permit2Address[base4.id], maxUint256],
|
|
966
|
-
account
|
|
967
|
-
});
|
|
968
|
-
await publicClient.waitForTransactionReceipt({
|
|
969
|
-
hash: approvalTx
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
const message = {
|
|
973
|
-
details: {
|
|
974
|
-
token: permit.permit.details.token,
|
|
975
|
-
amount: BigInt(permit.permit.details.amount),
|
|
976
|
-
expiration: Number(permit.permit.details.expiration),
|
|
977
|
-
nonce
|
|
978
|
-
},
|
|
979
|
-
spender: permit.permit.spender,
|
|
980
|
-
sigDeadline: BigInt(permit.permit.sigDeadline)
|
|
981
|
-
};
|
|
982
|
-
const signature = await walletClient.signTypedData({
|
|
983
|
-
domain: {
|
|
984
|
-
name: "Permit2",
|
|
985
|
-
chainId: base4.id,
|
|
986
|
-
verifyingContract: permit2Address[base4.id]
|
|
987
|
-
},
|
|
988
|
-
primaryType: "PermitSingle",
|
|
989
|
-
types: PERMIT_SINGLE_TYPES,
|
|
990
|
-
message,
|
|
991
|
-
account
|
|
992
|
-
});
|
|
993
|
-
signatures.push({
|
|
994
|
-
signature,
|
|
995
|
-
permit: convertBigIntToString(message)
|
|
996
|
-
});
|
|
997
|
-
}
|
|
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 });
|
|
998
1330
|
}
|
|
999
1331
|
const newQuote = await createTradeCall({
|
|
1000
1332
|
...tradeParameters,
|
|
@@ -1005,7 +1337,7 @@ async function tradeCoin({
|
|
|
1005
1337
|
data: newQuote.call.data,
|
|
1006
1338
|
value: BigInt(newQuote.call.value),
|
|
1007
1339
|
chain: base4,
|
|
1008
|
-
account
|
|
1340
|
+
account: resolvedAccount
|
|
1009
1341
|
};
|
|
1010
1342
|
if (validateTransaction) {
|
|
1011
1343
|
await publicClient.call(call);
|
|
@@ -1022,13 +1354,69 @@ async function tradeCoin({
|
|
|
1022
1354
|
});
|
|
1023
1355
|
return receipt;
|
|
1024
1356
|
}
|
|
1025
|
-
async function
|
|
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) {
|
|
1026
1408
|
if (tradeParameters.slippage && tradeParameters.slippage > 1) {
|
|
1027
1409
|
throw new Error("Slippage must be less than 1, max 0.99");
|
|
1028
1410
|
}
|
|
1029
1411
|
if (tradeParameters.amountIn === BigInt(0)) {
|
|
1030
1412
|
throw new Error("Amount in must be greater than 0");
|
|
1031
1413
|
}
|
|
1414
|
+
}
|
|
1415
|
+
async function createTradeCall(tradeParameters) {
|
|
1416
|
+
return createQuote(tradeParameters);
|
|
1417
|
+
}
|
|
1418
|
+
async function createQuote(tradeParameters) {
|
|
1419
|
+
validateTradeParameters(tradeParameters);
|
|
1032
1420
|
const quote = await postQuote({
|
|
1033
1421
|
body: {
|
|
1034
1422
|
tokenIn: tradeParameters.sell,
|
|
@@ -1057,6 +1445,12 @@ async function createTradeCall(tradeParameters) {
|
|
|
1057
1445
|
import { createConfig as createConfig2 } from "@hey-api/client-fetch";
|
|
1058
1446
|
var apiGet = (path, data) => client.get({ url: path, query: data, ...getApiKeyMeta() });
|
|
1059
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
|
+
};
|
|
1060
1454
|
var setApiBaseUrl = (baseUrl) => {
|
|
1061
1455
|
client.setConfig(createConfig2({ baseUrl }));
|
|
1062
1456
|
};
|
|
@@ -1309,14 +1703,18 @@ function createZoraUploaderForCreator(creatorAddress) {
|
|
|
1309
1703
|
}
|
|
1310
1704
|
export {
|
|
1311
1705
|
CoinMetadataBuilder,
|
|
1706
|
+
CoinbaseGasError,
|
|
1312
1707
|
CreateConstants,
|
|
1313
1708
|
ZoraUploader,
|
|
1314
1709
|
apiGet,
|
|
1315
1710
|
apiPost,
|
|
1711
|
+
apiUrl,
|
|
1316
1712
|
cleanAndValidateMetadataURI,
|
|
1317
1713
|
createCoin,
|
|
1318
1714
|
createCoinCall,
|
|
1715
|
+
createCoinSmartWallet,
|
|
1319
1716
|
createMetadataBuilder,
|
|
1717
|
+
createQuote,
|
|
1320
1718
|
createTradeCall,
|
|
1321
1719
|
createZoraUploaderForCreator,
|
|
1322
1720
|
getCoin2 as getCoin,
|
|
@@ -1363,15 +1761,29 @@ export {
|
|
|
1363
1761
|
getTrends,
|
|
1364
1762
|
getURLFromUploadResult,
|
|
1365
1763
|
getWalletTradeActivity2 as getWalletTradeActivity,
|
|
1764
|
+
isContractCall,
|
|
1765
|
+
isSendCall,
|
|
1766
|
+
prepareUserOperation,
|
|
1366
1767
|
setApiBaseUrl,
|
|
1367
1768
|
setApiKey,
|
|
1769
|
+
submitUserOperation,
|
|
1770
|
+
toGenericCall,
|
|
1771
|
+
toUserOperationCalls,
|
|
1368
1772
|
tradeCoin,
|
|
1773
|
+
tradeCoinSmartWallet,
|
|
1369
1774
|
updateCoinURI,
|
|
1370
1775
|
updateCoinURICall,
|
|
1776
|
+
updateCoinURISmartWallet,
|
|
1371
1777
|
updatePayoutRecipient,
|
|
1372
1778
|
updatePayoutRecipientCall,
|
|
1779
|
+
updatePayoutRecipientSmartWallet,
|
|
1780
|
+
validateCreateCoinCalls,
|
|
1781
|
+
validateCreateCoinSmartWalletCalls,
|
|
1373
1782
|
validateImageMimeType,
|
|
1374
1783
|
validateMetadataJSON,
|
|
1375
|
-
validateMetadataURIContent
|
|
1784
|
+
validateMetadataURIContent,
|
|
1785
|
+
validateTradeParameters,
|
|
1786
|
+
validateUpdateCoinURI,
|
|
1787
|
+
validateUpdatePayoutRecipient
|
|
1376
1788
|
};
|
|
1377
1789
|
//# sourceMappingURL=index.js.map
|