@zoralabs/protocol-sdk 0.5.10 → 0.5.11

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 (39) hide show
  1. package/.turbo/turbo-build.log +3 -3
  2. package/CHANGELOG.md +9 -0
  3. package/README.md +10 -0
  4. package/dist/create/1155-create-helper.d.ts +2 -2
  5. package/dist/create/1155-create-helper.d.ts.map +1 -1
  6. package/dist/index.cjs +136 -212
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +135 -209
  11. package/dist/index.js.map +1 -1
  12. package/dist/mint/mint-client.d.ts +3 -4
  13. package/dist/mint/mint-client.d.ts.map +1 -1
  14. package/dist/mints/mints-contracts.d.ts +2868 -2027
  15. package/dist/mints/mints-contracts.d.ts.map +1 -1
  16. package/dist/mints/mints-eth-unwrapper-and-caller.d.ts +78 -0
  17. package/dist/mints/mints-eth-unwrapper-and-caller.d.ts.map +1 -0
  18. package/dist/mints/mints-relay-example.d.ts +60 -0
  19. package/dist/mints/mints-relay-example.d.ts.map +1 -0
  20. package/dist/premint/premint-client.d.ts +1 -1
  21. package/dist/premint/premint-client.d.ts.map +1 -1
  22. package/dist/preminter.d.ts +23 -11
  23. package/dist/preminter.d.ts.map +1 -1
  24. package/dist/test-utils.d.ts +11 -0
  25. package/dist/test-utils.d.ts.map +1 -0
  26. package/dist/utils.d.ts +3 -0
  27. package/dist/utils.d.ts.map +1 -0
  28. package/package.json +6 -4
  29. package/src/create/1155-create-helper.ts +14 -11
  30. package/src/index.ts +2 -0
  31. package/src/mint/mint-client.ts +16 -19
  32. package/src/mints/mints-contracts.test.ts +32 -225
  33. package/src/mints/mints-contracts.ts +200 -281
  34. package/src/mints/mints-eth-unwrapper-and-caller.test.ts +467 -0
  35. package/src/mints/mints-eth-unwrapper-and-caller.ts +83 -0
  36. package/src/mints/mints-relay-example.ts +313 -0
  37. package/src/premint/premint-client.ts +15 -11
  38. package/src/test-utils.ts +39 -0
  39. package/src/utils.ts +30 -0
@@ -0,0 +1,313 @@
1
+ import {
2
+ Address,
3
+ Hex,
4
+ PublicClient,
5
+ SignTypedDataParameters,
6
+ TypedData,
7
+ WalletClient,
8
+ recoverTypedDataAddress,
9
+ } from "viem";
10
+ import axios from "axios";
11
+ import { paths, TransactionStepItem } from "@reservoir0x/relay-sdk";
12
+ import { PermitSafeTransferBatch } from "./mints-contracts";
13
+ import {
14
+ mintsEthUnwrapperAndCallerABI,
15
+ mintsEthUnwrapperAndCallerAddress,
16
+ } from "@zoralabs/protocol-deployments";
17
+ import { makeCallWithEthSafeTransferData } from "./mints-eth-unwrapper-and-caller";
18
+
19
+ type RelayCallBody =
20
+ paths["/execute/call"]["post"]["requestBody"]["content"]["application/json"];
21
+ type RelayCallResponse =
22
+ paths["/execute/call"]["post"]["responses"]["200"]["content"]["application/json"];
23
+
24
+ const postToRelay = async ({
25
+ data,
26
+ }: {
27
+ data: RelayCallBody;
28
+ }): Promise<RelayCallResponse> => {
29
+ const request = {
30
+ url: "https://api.relay.link/execute/call",
31
+ method: "post",
32
+ data,
33
+ };
34
+
35
+ return (await axios.post(request.url, request.data))
36
+ .data as RelayCallResponse;
37
+ };
38
+
39
+ export const getRelayCall = async ({
40
+ tx,
41
+ depositingAccount,
42
+ originChainId,
43
+ toChainId,
44
+ }: {
45
+ tx: {
46
+ to: Address;
47
+ value: bigint;
48
+ data: Hex;
49
+ };
50
+ depositingAccount: Address;
51
+ originChainId: number;
52
+ toChainId: number;
53
+ }) => {
54
+ const data: RelayCallBody = {
55
+ user: depositingAccount,
56
+ txs: [
57
+ {
58
+ to: tx.to,
59
+ value: tx.value.toString(),
60
+ data: tx.data,
61
+ },
62
+ ],
63
+ originChainId: originChainId,
64
+ destinationChainId: toChainId,
65
+ };
66
+
67
+ const response = await postToRelay({
68
+ data,
69
+ });
70
+
71
+ if (response.steps!.length !== 1) {
72
+ throw new Error("should only be a single step.");
73
+ }
74
+
75
+ const step = response.steps![0]!;
76
+
77
+ if (step.items!.length !== 1) {
78
+ throw new Error("should only be a single item.");
79
+ }
80
+
81
+ const stepItem = step.items![0]! as TransactionStepItem;
82
+
83
+ const relayCall = stepItem.data!;
84
+
85
+ // compute relay fee by subtracting cross-chain call value,
86
+ // from value to send to relay
87
+ const relayFee = BigInt(relayCall.value) - tx.value;
88
+
89
+ return {
90
+ relayCall,
91
+ relayFee,
92
+ };
93
+ };
94
+
95
+ // call relay to get the relay call data
96
+ // then sign a message with an executing account
97
+ // signaling intent to pay the fee later, with a 30 second deadline
98
+ export const makeAndSignSponsoredRelayCall = async ({
99
+ originChainId,
100
+ toChainId,
101
+ tx,
102
+ executingAccount,
103
+ walletClient,
104
+ }: {
105
+ // the chain to call from (current chain)
106
+ originChainId: keyof typeof mintsEthUnwrapperAndCallerAddress;
107
+ // the chain that the call will be executed on
108
+ toChainId: number;
109
+ // the tx to call on the other chain:
110
+ tx: {
111
+ to: Address;
112
+ value: bigint;
113
+ data: Hex;
114
+ };
115
+ executingAccount: Address;
116
+ walletClient: WalletClient;
117
+ }) => {
118
+ // call relay to get the cross-chain calldata
119
+ const { relayCall: relayCall, relayFee: relayFee } = await getRelayCall({
120
+ originChainId,
121
+ toChainId,
122
+ tx,
123
+ depositingAccount: mintsEthUnwrapperAndCallerAddress[originChainId],
124
+ });
125
+
126
+ // build call to forward the relay call to the other chain that will be
127
+ // set as the `safeTransferData` in the permit
128
+ const safeTransferData = makeCallWithEthSafeTransferData({
129
+ address: relayCall.to,
130
+ call: relayCall.data,
131
+ value: BigInt(relayCall.value),
132
+ });
133
+
134
+ const deadline = BigInt(new Date().getTime() + 30 * 1000);
135
+
136
+ // build and sign a message indicating intent to execute this call
137
+ // and pay the relay fee
138
+ const typedData = permitWithAdditionalValueTypedDataDefinition({
139
+ additionalValueToSend: relayFee,
140
+ chainId: originChainId,
141
+ // make a deadline 30 seconds from now:
142
+ deadline,
143
+ safeTransferData,
144
+ });
145
+
146
+ // have the account that is to execute the transaction later
147
+ // sign the typed data
148
+ const signature = await walletClient.signTypedData({
149
+ ...typedData,
150
+ account: executingAccount,
151
+ });
152
+
153
+ return {
154
+ safeTransferData,
155
+ additionalValueToSend: relayFee,
156
+ signature,
157
+ deadline,
158
+ };
159
+ };
160
+
161
+ // recovers the signer of the sponsored relay call
162
+ // and throws an error if the signer is not the executing account
163
+ export const validateSponsoredRelayCall = async ({
164
+ safeTransferData,
165
+ additionalValueToSend,
166
+ deadline,
167
+ chainId,
168
+ // account that should ahve signed the message
169
+ executingAccount,
170
+ signature,
171
+ }: {
172
+ safeTransferData: Hex;
173
+ additionalValueToSend: bigint;
174
+ deadline: bigint;
175
+ executingAccount: Address;
176
+ chainId: keyof typeof mintsEthUnwrapperAndCallerAddress;
177
+ signature: Hex;
178
+ }) => {
179
+ const typedData = permitWithAdditionalValueTypedDataDefinition({
180
+ additionalValueToSend,
181
+ chainId,
182
+ deadline,
183
+ safeTransferData,
184
+ });
185
+
186
+ const recovered = await recoverTypedDataAddress({
187
+ ...typedData,
188
+ signature,
189
+ });
190
+
191
+ if (recovered !== executingAccount) {
192
+ throw new Error("Invalid signature");
193
+ }
194
+ };
195
+
196
+ // validate that the executingAccount signed a message
197
+ // indicating intent to pay the relay fee, and execute the call
198
+ export const validateAndExecuteSponsoredRelayCall = async ({
199
+ permit,
200
+ permitSignature,
201
+ additionalValueToSend,
202
+ deadline,
203
+ chainId,
204
+ // account that should ahve signed the message
205
+ executingAccount,
206
+ sponsoredCallSignature,
207
+ walletClient,
208
+ publicClient,
209
+ }: {
210
+ permit: PermitSafeTransferBatch;
211
+ permitSignature: Hex;
212
+ // additional value to send to the unwrapper
213
+ additionalValueToSend: bigint;
214
+ // deadline to execute the call
215
+ deadline: bigint;
216
+ // account that is to execute the call, and must have signed the sponsored call
217
+ executingAccount: Address;
218
+ chainId: keyof typeof mintsEthUnwrapperAndCallerAddress;
219
+ // signature of the sponsored call
220
+ sponsoredCallSignature: Hex;
221
+ publicClient: PublicClient;
222
+ walletClient: WalletClient;
223
+ }) => {
224
+ if (deadline < BigInt(new Date().getTime())) {
225
+ throw new Error("Deadline has passed");
226
+ }
227
+
228
+ await validateSponsoredRelayCall({
229
+ safeTransferData: permit.safeTransferData,
230
+ additionalValueToSend,
231
+ deadline,
232
+ chainId,
233
+ signature: sponsoredCallSignature,
234
+ executingAccount,
235
+ });
236
+
237
+ // now we call a payable function on the mints eth unwrapper and caller contract,
238
+ // with the permit and corresponding signature to transfer the mints to the unwrapper,
239
+ // and call relay with the unwrapped value + relay fee.
240
+ // the payable value on this call is the relay fee, which is added to the unwrapped
241
+ // value of the mints and sent to the other chain.
242
+ // any remaining value from the unwrapped MINTs is refunded to the original caller.
243
+ const simulated = await publicClient.simulateContract({
244
+ abi: mintsEthUnwrapperAndCallerABI,
245
+ address: mintsEthUnwrapperAndCallerAddress[chainId],
246
+ functionName: "permitWithAdditionalValue",
247
+ args: [permit, permitSignature],
248
+ account: executingAccount,
249
+ // we must call this functio
250
+ value: additionalValueToSend,
251
+ });
252
+
253
+ // wait for the transaction to succeed.
254
+ const hash = await walletClient.writeContract(simulated.request);
255
+
256
+ const receipt = await publicClient.waitForTransactionReceipt({
257
+ hash,
258
+ });
259
+
260
+ if (receipt.status !== "success") {
261
+ throw new Error("Transaction failed");
262
+ }
263
+ };
264
+
265
+ function makeTypeData<
266
+ const TTypedData extends TypedData | { [key: string]: unknown },
267
+ TPrimaryType extends string,
268
+ >(args: Omit<SignTypedDataParameters<TTypedData, TPrimaryType>, "account">) {
269
+ return args;
270
+ }
271
+
272
+ function permitWithAdditionalValueTypedDataDefinition({
273
+ safeTransferData,
274
+ additionalValueToSend,
275
+ deadline,
276
+ chainId,
277
+ }: {
278
+ safeTransferData: Hex;
279
+ additionalValueToSend: bigint;
280
+ deadline: bigint;
281
+ chainId: keyof typeof mintsEthUnwrapperAndCallerAddress;
282
+ }) {
283
+ return makeTypeData({
284
+ primaryType: "PermitWithAdditionalValue",
285
+ types: {
286
+ PermitWithAdditionalValue: [
287
+ {
288
+ name: "safeTransferData",
289
+ type: "bytes",
290
+ },
291
+ {
292
+ name: "additionalValueToSend",
293
+ type: "uint256",
294
+ },
295
+ {
296
+ name: "deadline",
297
+ type: "uint256",
298
+ },
299
+ ],
300
+ },
301
+ message: {
302
+ safeTransferData,
303
+ additionalValueToSend: additionalValueToSend,
304
+ deadline: deadline,
305
+ },
306
+ domain: {
307
+ chainId,
308
+ name: "Relay",
309
+ version: "1",
310
+ verifyingContract: mintsEthUnwrapperAndCallerAddress[chainId],
311
+ },
312
+ });
313
+ }
@@ -41,6 +41,7 @@ import { OPEN_EDITION_MINT_SIZE } from "../constants";
41
41
  import { IHttpClient } from "src/apis/http-api-base";
42
42
  import { getApiNetworkConfigForChain } from "src/mint/mint-api-client";
43
43
  import { MintCosts } from "src/mint/mint-client";
44
+ import { makeSimulateContractParamaters } from "src/utils";
44
45
 
45
46
  type PremintedV2LogType = DecodeEventLogReturnType<
46
47
  typeof zoraCreator1155PremintExecutorImplABI,
@@ -510,7 +511,16 @@ class PremintClient {
510
511
  platformReferral?: Address;
511
512
  mintRecipient?: Address;
512
513
  };
513
- }): Promise<SimulateContractParameters> {
514
+ }): Promise<
515
+ SimulateContractParameters<
516
+ typeof zoraCreator1155PremintExecutorImplABI,
517
+ "premintV1" | "premintV2",
518
+ any,
519
+ any,
520
+ any,
521
+ Account | Address
522
+ >
523
+ > {
514
524
  if (mintArguments && mintArguments?.quantityToMint < 1) {
515
525
  throw new Error("Quantity to mint cannot be below 1");
516
526
  }
@@ -550,7 +560,7 @@ class PremintClient {
550
560
  };
551
561
 
552
562
  if (premintConfigVersion === PremintConfigVersion.V1) {
553
- return {
563
+ return makeSimulateContractParamaters({
554
564
  account: minterAccount,
555
565
  abi: zoraCreator1155PremintExecutorImplABI,
556
566
  functionName: "premintV1",
@@ -563,12 +573,9 @@ class PremintClient {
563
573
  numberToMint,
564
574
  mintArgumentsContract,
565
575
  ],
566
- } satisfies SimulateContractParameters<
567
- typeof zoraCreator1155PremintExecutorImplABI,
568
- "premintV1"
569
- >;
576
+ });
570
577
  } else if (premintConfigVersion === PremintConfigVersion.V2) {
571
- return {
578
+ return makeSimulateContractParamaters({
572
579
  account: minterAccount,
573
580
  abi: zoraCreator1155PremintExecutorImplABI,
574
581
  functionName: "premintV2",
@@ -581,10 +588,7 @@ class PremintClient {
581
588
  numberToMint,
582
589
  mintArgumentsContract,
583
590
  ],
584
- } satisfies SimulateContractParameters<
585
- typeof zoraCreator1155PremintExecutorImplABI,
586
- "premintV2"
587
- >;
591
+ });
588
592
  }
589
593
 
590
594
  throw new Error(`Invalid premint config version ${premintConfigVersion}`);
@@ -0,0 +1,39 @@
1
+ import {
2
+ zoraCreator1155FactoryImplABI,
3
+ zoraCreator1155FactoryImplAddress,
4
+ } from "@zoralabs/protocol-deployments";
5
+ import {
6
+ Address,
7
+ Hex,
8
+ PublicClient,
9
+ encodeAbiParameters,
10
+ parseAbiParameters,
11
+ } from "viem";
12
+ import { expect } from "vitest";
13
+
14
+ export const waitForSuccess = async (hash: Hex, publicClient: PublicClient) => {
15
+ const receipt = await publicClient.waitForTransactionReceipt({
16
+ hash,
17
+ });
18
+
19
+ expect(receipt.status).toBe("success");
20
+ };
21
+
22
+ export const getFixedPricedMinter = async ({
23
+ publicClient,
24
+ chainId,
25
+ }: {
26
+ publicClient: PublicClient;
27
+ chainId: keyof typeof zoraCreator1155FactoryImplAddress;
28
+ }) =>
29
+ await publicClient.readContract({
30
+ abi: zoraCreator1155FactoryImplABI,
31
+ address: zoraCreator1155FactoryImplAddress[chainId],
32
+ functionName: "fixedPriceMinter",
33
+ });
34
+
35
+ export const fixedPriceMinterMinterArguments = ({
36
+ mintRecipient,
37
+ }: {
38
+ mintRecipient: Address;
39
+ }) => encodeAbiParameters(parseAbiParameters("address"), [mintRecipient]);
package/src/utils.ts ADDED
@@ -0,0 +1,30 @@
1
+ import {
2
+ Abi,
3
+ Account,
4
+ Address,
5
+ Chain,
6
+ ContractFunctionArgs,
7
+ ContractFunctionName,
8
+ SimulateContractParameters,
9
+ } from "viem";
10
+
11
+ export const makeSimulateContractParamaters = <
12
+ const abi extends Abi | readonly unknown[],
13
+ functionName extends ContractFunctionName<abi, "nonpayable" | "payable">,
14
+ args extends ContractFunctionArgs<
15
+ abi,
16
+ "nonpayable" | "payable",
17
+ functionName
18
+ >,
19
+ chainOverride extends Chain | undefined,
20
+ accountOverride extends Account | Address | undefined = undefined,
21
+ >(
22
+ args: SimulateContractParameters<
23
+ abi,
24
+ functionName,
25
+ args,
26
+ Chain,
27
+ chainOverride,
28
+ accountOverride
29
+ >,
30
+ ) => args;