@zoralabs/protocol-sdk 0.7.3-SPARKS.0 → 0.7.3
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/.turbo/turbo-build.log +16 -0
- package/CHANGELOG.md +9 -4
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mints/mints-relay-example.d.ts +4 -4
- package/dist/mints/mints-relay-example.d.ts.map +1 -1
- package/package.json +10 -6
- package/src/create/1155-create-helper.test.ts +325 -0
- package/src/mint/mint-client.test.ts +263 -0
- package/src/mints/mints-contracts.test.ts +529 -0
- package/src/mints/mints-eth-unwrapper-and-caller.test.ts +467 -0
- package/src/mints/mints-eth-unwrapper-and-caller.ts +2 -2
- package/src/mints/mints-queries.test.ts +105 -0
- package/src/mints/mints-relay-example.ts +22 -13
- package/src/premint/premint-client.test.ts +290 -0
- package/src/premint/preminter.test.ts +866 -0
- package/test-integration/setup-test-contracts.ts +96 -0
- package/tsconfig.build.json +10 -0
- package/tsup.config.ts +12 -0
- package/yarn-error.log +0 -8602
|
@@ -0,0 +1,866 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Address,
|
|
3
|
+
hashTypedData,
|
|
4
|
+
keccak256,
|
|
5
|
+
stringToBytes,
|
|
6
|
+
zeroAddress,
|
|
7
|
+
} from "viem";
|
|
8
|
+
import { zoraSepolia } from "viem/chains";
|
|
9
|
+
import { describe, expect } from "vitest";
|
|
10
|
+
import { parseEther } from "viem";
|
|
11
|
+
import {
|
|
12
|
+
zoraCreator1155PremintExecutorImplABI as preminterAbi,
|
|
13
|
+
zoraCreator1155ImplABI,
|
|
14
|
+
zoraCreator1155FactoryImplAddress,
|
|
15
|
+
zoraCreator1155FactoryImplConfig,
|
|
16
|
+
PremintConfigV1,
|
|
17
|
+
TokenCreationConfigV1,
|
|
18
|
+
PremintConfigVersion,
|
|
19
|
+
TokenCreationConfigV2,
|
|
20
|
+
PremintConfigV2,
|
|
21
|
+
PremintMintArguments,
|
|
22
|
+
ContractCreationConfig,
|
|
23
|
+
premintTypedDataDefinition,
|
|
24
|
+
encodePremintConfig,
|
|
25
|
+
} from "@zoralabs/protocol-deployments";
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
isValidSignature,
|
|
29
|
+
getPremintExecutorAddress,
|
|
30
|
+
getPremintMintCosts,
|
|
31
|
+
} from "./preminter";
|
|
32
|
+
import { AnvilViemClientsTest, forkUrls, makeAnvilTest } from "src/anvil";
|
|
33
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
34
|
+
|
|
35
|
+
const erc1271Abi = [
|
|
36
|
+
{
|
|
37
|
+
type: "function",
|
|
38
|
+
name: "isValidSignature",
|
|
39
|
+
inputs: [
|
|
40
|
+
{ name: "_hash", type: "bytes32", internalType: "bytes32" },
|
|
41
|
+
{ name: "_signature", type: "bytes", internalType: "bytes" },
|
|
42
|
+
],
|
|
43
|
+
outputs: [{ name: "", type: "bytes4", internalType: "bytes4" }],
|
|
44
|
+
stateMutability: "view",
|
|
45
|
+
},
|
|
46
|
+
] as const;
|
|
47
|
+
|
|
48
|
+
// create token and contract creation config:
|
|
49
|
+
export const defaultContractConfig = ({
|
|
50
|
+
contractAdmin,
|
|
51
|
+
}: {
|
|
52
|
+
contractAdmin: Address;
|
|
53
|
+
}): ContractCreationConfig => ({
|
|
54
|
+
contractAdmin,
|
|
55
|
+
contractURI: "ipfs://asdfasdfasdf",
|
|
56
|
+
contractName: "My fun NFT",
|
|
57
|
+
additionalAdmins: [],
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const defaultTokenConfigV1 = (
|
|
61
|
+
fixedPriceMinterAddress: Address,
|
|
62
|
+
creatorAccount: Address,
|
|
63
|
+
): TokenCreationConfigV1 => ({
|
|
64
|
+
tokenURI: "ipfs://tokenIpfsId0",
|
|
65
|
+
maxSupply: 100n,
|
|
66
|
+
maxTokensPerAddress: 10n,
|
|
67
|
+
pricePerToken: 0n,
|
|
68
|
+
mintStart: 0n,
|
|
69
|
+
mintDuration: 100n,
|
|
70
|
+
royaltyMintSchedule: 30,
|
|
71
|
+
royaltyBPS: 200,
|
|
72
|
+
royaltyRecipient: creatorAccount,
|
|
73
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const defaultTokenConfigV2 = (
|
|
77
|
+
fixedPriceMinterAddress: Address,
|
|
78
|
+
creatorAccount: Address,
|
|
79
|
+
createReferral: Address,
|
|
80
|
+
pricePerToken = 0n,
|
|
81
|
+
): TokenCreationConfigV2 => ({
|
|
82
|
+
tokenURI: "ipfs://tokenIpfsId0",
|
|
83
|
+
maxSupply: 100n,
|
|
84
|
+
maxTokensPerAddress: 1000n,
|
|
85
|
+
pricePerToken,
|
|
86
|
+
mintStart: 0n,
|
|
87
|
+
mintDuration: 100n,
|
|
88
|
+
royaltyBPS: 200,
|
|
89
|
+
payoutRecipient: creatorAccount,
|
|
90
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
91
|
+
createReferral,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const defaultPremintConfigV1 = ({
|
|
95
|
+
fixedPriceMinter,
|
|
96
|
+
creatorAccount,
|
|
97
|
+
}: {
|
|
98
|
+
fixedPriceMinter: Address;
|
|
99
|
+
creatorAccount: Address;
|
|
100
|
+
}): PremintConfigV1 => ({
|
|
101
|
+
tokenConfig: defaultTokenConfigV1(fixedPriceMinter, creatorAccount),
|
|
102
|
+
deleted: false,
|
|
103
|
+
uid: 105,
|
|
104
|
+
version: 0,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export const defaultPremintConfigV2 = ({
|
|
108
|
+
fixedPriceMinter,
|
|
109
|
+
creatorAccount,
|
|
110
|
+
createReferral = zeroAddress,
|
|
111
|
+
pricePerToken = 0n,
|
|
112
|
+
}: {
|
|
113
|
+
fixedPriceMinter: Address;
|
|
114
|
+
creatorAccount: Address;
|
|
115
|
+
createReferral?: Address;
|
|
116
|
+
pricePerToken?: bigint;
|
|
117
|
+
}): PremintConfigV2 => ({
|
|
118
|
+
tokenConfig: defaultTokenConfigV2(
|
|
119
|
+
fixedPriceMinter,
|
|
120
|
+
creatorAccount,
|
|
121
|
+
createReferral,
|
|
122
|
+
pricePerToken,
|
|
123
|
+
),
|
|
124
|
+
deleted: false,
|
|
125
|
+
uid: 106,
|
|
126
|
+
version: 0,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const PREMINTER_ADDRESS = getPremintExecutorAddress();
|
|
130
|
+
|
|
131
|
+
async function setupContracts({
|
|
132
|
+
viemClients: { walletClient, testClient, publicClient, chain },
|
|
133
|
+
}: AnvilViemClientsTest) {
|
|
134
|
+
// JSON-RPC Account
|
|
135
|
+
const [
|
|
136
|
+
deployerAccount,
|
|
137
|
+
creatorAccount,
|
|
138
|
+
collectorAccount,
|
|
139
|
+
collaboratorAccount,
|
|
140
|
+
] = (await walletClient.getAddresses()) as [
|
|
141
|
+
Address,
|
|
142
|
+
Address,
|
|
143
|
+
Address,
|
|
144
|
+
Address,
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
// deploy signature minter contract
|
|
148
|
+
await testClient.setBalance({
|
|
149
|
+
address: deployerAccount,
|
|
150
|
+
value: parseEther("10"),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
const fixedPriceMinterAddress = await publicClient.readContract({
|
|
154
|
+
abi: zoraCreator1155FactoryImplConfig.abi,
|
|
155
|
+
address:
|
|
156
|
+
zoraCreator1155FactoryImplAddress[
|
|
157
|
+
chain.id as keyof typeof zoraCreator1155FactoryImplAddress
|
|
158
|
+
],
|
|
159
|
+
functionName: "fixedPriceMinter",
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
accounts: {
|
|
164
|
+
deployerAccount,
|
|
165
|
+
creatorAccount,
|
|
166
|
+
collectorAccount,
|
|
167
|
+
collaboratorAccount,
|
|
168
|
+
},
|
|
169
|
+
fixedPriceMinterAddress,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const zoraSepoliaAnvilTest = makeAnvilTest({
|
|
174
|
+
forkUrl: forkUrls.zoraSepolia,
|
|
175
|
+
forkBlockNumber: 9467979,
|
|
176
|
+
anvilChainId: zoraSepolia.id,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe("ZoraCreator1155Preminter", () => {
|
|
180
|
+
zoraSepoliaAnvilTest(
|
|
181
|
+
"can sign on the forked premint contract",
|
|
182
|
+
async ({ viemClients }) => {
|
|
183
|
+
const {
|
|
184
|
+
fixedPriceMinterAddress,
|
|
185
|
+
accounts: { creatorAccount },
|
|
186
|
+
} = await setupContracts({ viemClients });
|
|
187
|
+
const premintConfig = defaultPremintConfigV1({
|
|
188
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
189
|
+
creatorAccount,
|
|
190
|
+
});
|
|
191
|
+
const contractConfig = defaultContractConfig({
|
|
192
|
+
contractAdmin: creatorAccount,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const preminterAddress = getPremintExecutorAddress();
|
|
196
|
+
|
|
197
|
+
const contractAddress = await viemClients.publicClient.readContract({
|
|
198
|
+
abi: preminterAbi,
|
|
199
|
+
address: preminterAddress,
|
|
200
|
+
functionName: "getContractWithAdditionalAdminsAddress",
|
|
201
|
+
args: [contractConfig],
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
const signedMessage = await viemClients.walletClient.signTypedData({
|
|
205
|
+
...premintTypedDataDefinition({
|
|
206
|
+
verifyingContract: contractAddress,
|
|
207
|
+
chainId: viemClients.chain.id,
|
|
208
|
+
premintConfig,
|
|
209
|
+
premintConfigVersion: PremintConfigVersion.V1,
|
|
210
|
+
}),
|
|
211
|
+
account: creatorAccount,
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
console.log({
|
|
215
|
+
creatorAccount,
|
|
216
|
+
signedMessage,
|
|
217
|
+
contractConfig,
|
|
218
|
+
premintConfig,
|
|
219
|
+
contractAddress,
|
|
220
|
+
});
|
|
221
|
+
},
|
|
222
|
+
20 * 1000,
|
|
223
|
+
);
|
|
224
|
+
zoraSepoliaAnvilTest(
|
|
225
|
+
"can sign and recover a v1 premint config signature",
|
|
226
|
+
async ({ viemClients }) => {
|
|
227
|
+
const {
|
|
228
|
+
fixedPriceMinterAddress,
|
|
229
|
+
accounts: { creatorAccount },
|
|
230
|
+
} = await setupContracts({ viemClients });
|
|
231
|
+
|
|
232
|
+
const premintConfig = defaultPremintConfigV1({
|
|
233
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
234
|
+
creatorAccount,
|
|
235
|
+
});
|
|
236
|
+
const contractConfig = defaultContractConfig({
|
|
237
|
+
contractAdmin: creatorAccount,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
const tokenContract = await viemClients.publicClient.readContract({
|
|
241
|
+
abi: preminterAbi,
|
|
242
|
+
address: PREMINTER_ADDRESS,
|
|
243
|
+
functionName: "getContractWithAdditionalAdminsAddress",
|
|
244
|
+
args: [contractConfig],
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// sign message containing contract and token creation config and uid
|
|
248
|
+
const signedMessage = await viemClients.walletClient.signTypedData({
|
|
249
|
+
...premintTypedDataDefinition({
|
|
250
|
+
verifyingContract: tokenContract,
|
|
251
|
+
// we need to sign here for the anvil chain, cause thats where it is run on
|
|
252
|
+
chainId: viemClients.chain.id,
|
|
253
|
+
premintConfig,
|
|
254
|
+
premintConfigVersion: PremintConfigVersion.V1,
|
|
255
|
+
}),
|
|
256
|
+
account: creatorAccount,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
// recover and verify address is correct
|
|
260
|
+
const { recoveredAddress, isAuthorized } = await isValidSignature({
|
|
261
|
+
collection: contractConfig,
|
|
262
|
+
collectionAddress: tokenContract,
|
|
263
|
+
chainId: viemClients.publicClient.chain!.id,
|
|
264
|
+
premintConfig,
|
|
265
|
+
premintConfigVersion: PremintConfigVersion.V1,
|
|
266
|
+
publicClient: viemClients.publicClient,
|
|
267
|
+
signature: signedMessage,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
expect(recoveredAddress).to.equal(creatorAccount);
|
|
271
|
+
expect(isAuthorized).toBe(true);
|
|
272
|
+
|
|
273
|
+
expect(recoveredAddress).to.equal(creatorAccount);
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
20 * 1000,
|
|
277
|
+
);
|
|
278
|
+
zoraSepoliaAnvilTest(
|
|
279
|
+
"can sign and recover a v2 premint config signature",
|
|
280
|
+
async ({ viemClients }) => {
|
|
281
|
+
const {
|
|
282
|
+
fixedPriceMinterAddress,
|
|
283
|
+
accounts: { creatorAccount },
|
|
284
|
+
} = await setupContracts({ viemClients });
|
|
285
|
+
|
|
286
|
+
const premintConfig = defaultPremintConfigV2({
|
|
287
|
+
creatorAccount,
|
|
288
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
289
|
+
createReferral: creatorAccount,
|
|
290
|
+
});
|
|
291
|
+
const contractConfig = defaultContractConfig({
|
|
292
|
+
contractAdmin: creatorAccount,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const tokenContract = await viemClients.publicClient.readContract({
|
|
296
|
+
abi: preminterAbi,
|
|
297
|
+
address: PREMINTER_ADDRESS,
|
|
298
|
+
functionName: "getContractWithAdditionalAdminsAddress",
|
|
299
|
+
args: [contractConfig],
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// sign message containing contract and token creation config and uid
|
|
303
|
+
const signedMessage = await viemClients.walletClient.signTypedData({
|
|
304
|
+
...premintTypedDataDefinition({
|
|
305
|
+
verifyingContract: tokenContract,
|
|
306
|
+
// we need to sign here for the anvil chain, cause thats where it is run on
|
|
307
|
+
chainId: viemClients.chain.id,
|
|
308
|
+
premintConfig,
|
|
309
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
310
|
+
}),
|
|
311
|
+
account: creatorAccount,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
// recover and verify address is correct
|
|
315
|
+
const { recoveredAddress, isAuthorized } = await isValidSignature({
|
|
316
|
+
collection: contractConfig,
|
|
317
|
+
collectionAddress: tokenContract,
|
|
318
|
+
chainId: viemClients.publicClient.chain!.id,
|
|
319
|
+
premintConfig,
|
|
320
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
321
|
+
publicClient: viemClients.publicClient,
|
|
322
|
+
signature: signedMessage,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
expect(recoveredAddress).to.equal(creatorAccount);
|
|
326
|
+
expect(isAuthorized).toBe(true);
|
|
327
|
+
|
|
328
|
+
expect(recoveredAddress).to.equal(creatorAccount);
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
20 * 1000,
|
|
332
|
+
);
|
|
333
|
+
zoraSepoliaAnvilTest(
|
|
334
|
+
"can sign and mint multiple tokens",
|
|
335
|
+
async ({ viemClients }) => {
|
|
336
|
+
const {
|
|
337
|
+
fixedPriceMinterAddress,
|
|
338
|
+
accounts: { creatorAccount, collectorAccount },
|
|
339
|
+
} = await setupContracts({ viemClients });
|
|
340
|
+
// setup contract and token creation parameters
|
|
341
|
+
const premintConfig1 = defaultPremintConfigV1({
|
|
342
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
343
|
+
creatorAccount,
|
|
344
|
+
});
|
|
345
|
+
const contractConfig = defaultContractConfig({
|
|
346
|
+
contractAdmin: creatorAccount,
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
// lets make it a random number to not break the existing tests that expect fresh data
|
|
350
|
+
premintConfig1.uid = Math.round(Math.random() * 1000000);
|
|
351
|
+
|
|
352
|
+
let contractAddress = await viemClients.publicClient.readContract({
|
|
353
|
+
abi: preminterAbi,
|
|
354
|
+
address: PREMINTER_ADDRESS,
|
|
355
|
+
functionName: "getContractWithAdditionalAdminsAddress",
|
|
356
|
+
args: [contractConfig],
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// have creator sign the message to create the contract
|
|
360
|
+
// and the token
|
|
361
|
+
const signedMessage = await viemClients.walletClient.signTypedData({
|
|
362
|
+
...premintTypedDataDefinition({
|
|
363
|
+
verifyingContract: contractAddress,
|
|
364
|
+
// we need to sign here for the anvil chain, cause thats where it is run on
|
|
365
|
+
chainId: viemClients.chain.id,
|
|
366
|
+
premintConfig: premintConfig1,
|
|
367
|
+
premintConfigVersion: PremintConfigVersion.V1,
|
|
368
|
+
}),
|
|
369
|
+
account: creatorAccount,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
const quantityToMint = 2n;
|
|
373
|
+
|
|
374
|
+
const valueToSend = (
|
|
375
|
+
await getPremintMintCosts({
|
|
376
|
+
publicClient: viemClients.publicClient,
|
|
377
|
+
quantityToMint,
|
|
378
|
+
tokenContract: contractAddress,
|
|
379
|
+
tokenPrice: premintConfig1.tokenConfig.pricePerToken,
|
|
380
|
+
})
|
|
381
|
+
).totalCostEth;
|
|
382
|
+
|
|
383
|
+
await viemClients.testClient.setBalance({
|
|
384
|
+
address: collectorAccount,
|
|
385
|
+
value: parseEther("10"),
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
// get the premint status - it should not be minted
|
|
389
|
+
let [contractCreated, tokenId] =
|
|
390
|
+
await viemClients.publicClient.readContract({
|
|
391
|
+
abi: preminterAbi,
|
|
392
|
+
address: PREMINTER_ADDRESS,
|
|
393
|
+
functionName: "premintStatus",
|
|
394
|
+
args: [contractAddress, premintConfig1.uid],
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
expect(contractCreated).toBe(false);
|
|
398
|
+
expect(tokenId).toBe(0n);
|
|
399
|
+
|
|
400
|
+
const mintArguments: PremintMintArguments = {
|
|
401
|
+
mintComment: "",
|
|
402
|
+
mintRecipient: collectorAccount,
|
|
403
|
+
mintRewardsRecipients: [],
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const firstMinter = collectorAccount;
|
|
407
|
+
|
|
408
|
+
// now have the collector execute the first signed message;
|
|
409
|
+
// it should create the contract, the token,
|
|
410
|
+
// and min the quantity to mint tokens to the collector
|
|
411
|
+
// the signature along with contract + token creation
|
|
412
|
+
// parameters are required to call this function
|
|
413
|
+
const mintHash = await viemClients.walletClient.writeContract({
|
|
414
|
+
abi: preminterAbi,
|
|
415
|
+
functionName: "premint",
|
|
416
|
+
account: collectorAccount,
|
|
417
|
+
chain: viemClients.chain,
|
|
418
|
+
address: PREMINTER_ADDRESS,
|
|
419
|
+
args: [
|
|
420
|
+
contractConfig,
|
|
421
|
+
zeroAddress,
|
|
422
|
+
encodePremintConfig({
|
|
423
|
+
premintConfig: premintConfig1,
|
|
424
|
+
premintConfigVersion: PremintConfigVersion.V1,
|
|
425
|
+
}),
|
|
426
|
+
signedMessage,
|
|
427
|
+
quantityToMint,
|
|
428
|
+
mintArguments,
|
|
429
|
+
firstMinter,
|
|
430
|
+
zeroAddress,
|
|
431
|
+
],
|
|
432
|
+
value: valueToSend,
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
// ensure it succeeded
|
|
436
|
+
const receipt = await viemClients.publicClient.waitForTransactionReceipt({
|
|
437
|
+
hash: mintHash,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
expect(receipt.status).toBe("success");
|
|
441
|
+
|
|
442
|
+
// fetch the premint token id
|
|
443
|
+
[contractCreated, tokenId] = await viemClients.publicClient.readContract({
|
|
444
|
+
abi: preminterAbi,
|
|
445
|
+
address: PREMINTER_ADDRESS,
|
|
446
|
+
functionName: "premintStatus",
|
|
447
|
+
args: [contractAddress, premintConfig1.uid],
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
expect(contractCreated).toBe(true);
|
|
451
|
+
expect(tokenId).not.toBe(0n);
|
|
452
|
+
|
|
453
|
+
// now use what was created, to get the balance from the created contract
|
|
454
|
+
const tokenBalance = await viemClients.publicClient.readContract({
|
|
455
|
+
abi: zoraCreator1155ImplABI,
|
|
456
|
+
address: contractAddress,
|
|
457
|
+
functionName: "balanceOf",
|
|
458
|
+
args: [collectorAccount, tokenId],
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
// get token balance - should be amount that was created
|
|
462
|
+
expect(tokenBalance).toBe(quantityToMint);
|
|
463
|
+
|
|
464
|
+
const premintConfig2 = defaultPremintConfigV2({
|
|
465
|
+
creatorAccount,
|
|
466
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
467
|
+
createReferral: creatorAccount,
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// sign the message to create the second token
|
|
471
|
+
const signedMessage2 = await viemClients.walletClient.signTypedData({
|
|
472
|
+
...premintTypedDataDefinition({
|
|
473
|
+
verifyingContract: contractAddress,
|
|
474
|
+
chainId: viemClients.chain.id,
|
|
475
|
+
premintConfig: premintConfig2,
|
|
476
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
477
|
+
}),
|
|
478
|
+
account: creatorAccount,
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
const quantityToMint2 = 4n;
|
|
482
|
+
|
|
483
|
+
const valueToSend2 = (
|
|
484
|
+
await getPremintMintCosts({
|
|
485
|
+
publicClient: viemClients.publicClient,
|
|
486
|
+
quantityToMint: quantityToMint2,
|
|
487
|
+
tokenContract: contractAddress,
|
|
488
|
+
tokenPrice: premintConfig2.tokenConfig.pricePerToken,
|
|
489
|
+
})
|
|
490
|
+
).totalCostEth;
|
|
491
|
+
|
|
492
|
+
const simulationResult = await viemClients.publicClient.simulateContract({
|
|
493
|
+
abi: preminterAbi,
|
|
494
|
+
functionName: "premint",
|
|
495
|
+
account: collectorAccount,
|
|
496
|
+
chain: viemClients.chain,
|
|
497
|
+
address: PREMINTER_ADDRESS,
|
|
498
|
+
args: [
|
|
499
|
+
contractConfig,
|
|
500
|
+
zeroAddress,
|
|
501
|
+
encodePremintConfig({
|
|
502
|
+
premintConfig: premintConfig2,
|
|
503
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
504
|
+
}),
|
|
505
|
+
signedMessage2,
|
|
506
|
+
quantityToMint2,
|
|
507
|
+
mintArguments,
|
|
508
|
+
firstMinter,
|
|
509
|
+
zeroAddress,
|
|
510
|
+
],
|
|
511
|
+
value: valueToSend2,
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
// now have the collector execute the second signed message.
|
|
515
|
+
// it should create a new token against the existing contract
|
|
516
|
+
const mintHash2 = await viemClients.walletClient.writeContract(
|
|
517
|
+
simulationResult.request,
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
const premintV2Receipt =
|
|
521
|
+
await viemClients.publicClient.waitForTransactionReceipt({
|
|
522
|
+
hash: mintHash2,
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
expect(premintV2Receipt.status).toBe("success");
|
|
526
|
+
|
|
527
|
+
// now premint status for the second mint, it should be minted
|
|
528
|
+
[, tokenId] = await viemClients.publicClient.readContract({
|
|
529
|
+
abi: preminterAbi,
|
|
530
|
+
address: PREMINTER_ADDRESS,
|
|
531
|
+
functionName: "premintStatus",
|
|
532
|
+
args: [contractAddress, premintConfig2.uid],
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
expect(tokenId).not.toBe(0n);
|
|
536
|
+
|
|
537
|
+
// get balance of second token
|
|
538
|
+
const tokenBalance2 = await viemClients.publicClient.readContract({
|
|
539
|
+
abi: zoraCreator1155ImplABI,
|
|
540
|
+
address: contractAddress,
|
|
541
|
+
functionName: "balanceOf",
|
|
542
|
+
args: [collectorAccount, tokenId],
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
expect(tokenBalance2).toBe(quantityToMint2);
|
|
546
|
+
},
|
|
547
|
+
40 * 1000,
|
|
548
|
+
),
|
|
549
|
+
zoraSepoliaAnvilTest(
|
|
550
|
+
"can sign a premint from an smart contract account and mint tokens against that premint",
|
|
551
|
+
async ({ viemClients }) => {
|
|
552
|
+
const {
|
|
553
|
+
fixedPriceMinterAddress,
|
|
554
|
+
accounts: { collectorAccount },
|
|
555
|
+
} = await setupContracts({ viemClients });
|
|
556
|
+
|
|
557
|
+
// this test shows how to create a premint that is signed by an EOA, but the premint
|
|
558
|
+
// contract admin will be a smart wallet owned by the EOA.
|
|
559
|
+
// contract admin is set as the smart wallet. Smart wallet owner is the EOA.
|
|
560
|
+
// EOA signs the premint.
|
|
561
|
+
// When calling `premint` smart wallets address must be passed as an argument
|
|
562
|
+
|
|
563
|
+
// this was an AA contract that was deployed that has has the owner below as the
|
|
564
|
+
// valid signer. See https://sepolia.explorer.zora.energy/address/0x74F5fAf983d54FEd6D937654Aa4FD258534F2d4B?tab=contract
|
|
565
|
+
// it was deployed via the script `packages/1155-deployments/script/DeploySimpleAA.s.sol`
|
|
566
|
+
const smartWalletAddress = "0x74F5fAf983d54FEd6D937654Aa4FD258534F2d4B";
|
|
567
|
+
const ownerAddress = "0x7c8999dC9a822c1f0Df42023113EDB4FDd543266";
|
|
568
|
+
const ownerPrivateKey =
|
|
569
|
+
"0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0";
|
|
570
|
+
|
|
571
|
+
const ownerAccount = privateKeyToAccount(ownerPrivateKey);
|
|
572
|
+
|
|
573
|
+
const premintConfig = defaultPremintConfigV2({
|
|
574
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
575
|
+
// we set the creator to the AA contract
|
|
576
|
+
creatorAccount: smartWalletAddress,
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
expect(ownerAccount.address).toBe(ownerAddress);
|
|
580
|
+
|
|
581
|
+
const contractConfig = defaultContractConfig({
|
|
582
|
+
// for the contract config, we set the smart wallet as the admin
|
|
583
|
+
contractAdmin: smartWalletAddress,
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
let contractAddress = await viemClients.publicClient.readContract({
|
|
587
|
+
abi: preminterAbi,
|
|
588
|
+
address: PREMINTER_ADDRESS,
|
|
589
|
+
functionName: "getContractWithAdditionalAdminsAddress",
|
|
590
|
+
args: [contractConfig],
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
const typedData = premintTypedDataDefinition({
|
|
594
|
+
verifyingContract: contractAddress,
|
|
595
|
+
// we need to sign here for the anvil chain, cause thats where it is run on
|
|
596
|
+
chainId: viemClients.chain.id,
|
|
597
|
+
premintConfig: premintConfig,
|
|
598
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
// have creator sign the message to create the contract
|
|
602
|
+
// and the token
|
|
603
|
+
const signedMessage = await viemClients.walletClient.signTypedData({
|
|
604
|
+
...typedData,
|
|
605
|
+
account: ownerAccount,
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
// sanity check - validate the signature on the smart wallet contract
|
|
609
|
+
const result = await viemClients.publicClient.readContract({
|
|
610
|
+
abi: erc1271Abi,
|
|
611
|
+
address: smartWalletAddress,
|
|
612
|
+
functionName: "isValidSignature",
|
|
613
|
+
args: [hashTypedData(typedData), signedMessage],
|
|
614
|
+
});
|
|
615
|
+
|
|
616
|
+
// if is a valid signature, signature should return `bytes4(keccak256("isValidSignature(bytes32,bytes)"))`
|
|
617
|
+
const expectedMagicValue = keccak256(
|
|
618
|
+
stringToBytes("isValidSignature(bytes32,bytes)"),
|
|
619
|
+
).slice(0, 10);
|
|
620
|
+
|
|
621
|
+
expect(result).toBe(expectedMagicValue);
|
|
622
|
+
|
|
623
|
+
const quantityToMint = 2n;
|
|
624
|
+
|
|
625
|
+
const valueToSend = (
|
|
626
|
+
await getPremintMintCosts({
|
|
627
|
+
publicClient: viemClients.publicClient,
|
|
628
|
+
quantityToMint,
|
|
629
|
+
tokenContract: contractAddress,
|
|
630
|
+
tokenPrice: premintConfig.tokenConfig.pricePerToken,
|
|
631
|
+
})
|
|
632
|
+
).totalCostEth;
|
|
633
|
+
|
|
634
|
+
await viemClients.testClient.setBalance({
|
|
635
|
+
address: collectorAccount,
|
|
636
|
+
value: parseEther("10"),
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
const mintArguments: PremintMintArguments = {
|
|
640
|
+
mintComment: "",
|
|
641
|
+
mintRecipient: collectorAccount,
|
|
642
|
+
mintRewardsRecipients: [],
|
|
643
|
+
};
|
|
644
|
+
|
|
645
|
+
const firstMinter = collectorAccount;
|
|
646
|
+
|
|
647
|
+
// now have the collector execute the first signed message;
|
|
648
|
+
// it should create the contract, the token,
|
|
649
|
+
// and min the quantity to mint tokens to the collector
|
|
650
|
+
// the signature along with contract + token creation
|
|
651
|
+
// parameters are required to call this function
|
|
652
|
+
await viemClients.publicClient.simulateContract({
|
|
653
|
+
abi: preminterAbi,
|
|
654
|
+
functionName: "premint",
|
|
655
|
+
account: collectorAccount,
|
|
656
|
+
chain: viemClients.chain,
|
|
657
|
+
address: PREMINTER_ADDRESS,
|
|
658
|
+
args: [
|
|
659
|
+
contractConfig,
|
|
660
|
+
zeroAddress,
|
|
661
|
+
encodePremintConfig({
|
|
662
|
+
premintConfig: premintConfig,
|
|
663
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
664
|
+
}),
|
|
665
|
+
signedMessage,
|
|
666
|
+
quantityToMint,
|
|
667
|
+
mintArguments,
|
|
668
|
+
firstMinter,
|
|
669
|
+
// we must specify the smart wallet address in the call, so that the 1155 contract
|
|
670
|
+
// knows to have the smart wallet validate the signature
|
|
671
|
+
smartWalletAddress,
|
|
672
|
+
],
|
|
673
|
+
value: valueToSend,
|
|
674
|
+
});
|
|
675
|
+
},
|
|
676
|
+
40 * 1000,
|
|
677
|
+
),
|
|
678
|
+
zoraSepoliaAnvilTest(
|
|
679
|
+
"can have collaborators create premints that can be executed on existing contracts",
|
|
680
|
+
async ({ viemClients }) => {
|
|
681
|
+
const {
|
|
682
|
+
fixedPriceMinterAddress,
|
|
683
|
+
accounts: { creatorAccount, collectorAccount, collaboratorAccount },
|
|
684
|
+
} = await setupContracts({ viemClients });
|
|
685
|
+
|
|
686
|
+
await viemClients.testClient.setBalance({
|
|
687
|
+
address: collectorAccount,
|
|
688
|
+
value: parseEther("10"),
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// setup contract and token creation parameters
|
|
692
|
+
const premintConfig = defaultPremintConfigV2({
|
|
693
|
+
fixedPriceMinter: fixedPriceMinterAddress,
|
|
694
|
+
creatorAccount,
|
|
695
|
+
});
|
|
696
|
+
// lets make it a random number to not break the existing tests that expect fresh data
|
|
697
|
+
premintConfig.uid = Math.round(Math.random() * 1000000);
|
|
698
|
+
|
|
699
|
+
// create a premint config that a collaboratorw ill sign
|
|
700
|
+
const collaboratorPremintConfig = {
|
|
701
|
+
...premintConfig,
|
|
702
|
+
uid: Math.round(Math.random() * 1000000),
|
|
703
|
+
};
|
|
704
|
+
|
|
705
|
+
const contractConfig = defaultContractConfig({
|
|
706
|
+
contractAdmin: creatorAccount,
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
// modify contract config to have collaborators
|
|
710
|
+
contractConfig.additionalAdmins = [collaboratorAccount];
|
|
711
|
+
|
|
712
|
+
const contractAddress = await viemClients.publicClient.readContract({
|
|
713
|
+
abi: preminterAbi,
|
|
714
|
+
address: PREMINTER_ADDRESS,
|
|
715
|
+
functionName: "getContractWithAdditionalAdminsAddress",
|
|
716
|
+
args: [contractConfig],
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
// have creator sign the message to create the contract
|
|
720
|
+
// and the token
|
|
721
|
+
const creatorSignedMessage =
|
|
722
|
+
await viemClients.walletClient.signTypedData({
|
|
723
|
+
...premintTypedDataDefinition({
|
|
724
|
+
verifyingContract: contractAddress,
|
|
725
|
+
// we need to sign here for the anvil chain, cause thats where it is run on
|
|
726
|
+
chainId: viemClients.chain.id,
|
|
727
|
+
premintConfig: premintConfig,
|
|
728
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
729
|
+
}),
|
|
730
|
+
account: creatorAccount,
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
const collaboratorSignedMessage =
|
|
734
|
+
await viemClients.walletClient.signTypedData({
|
|
735
|
+
...premintTypedDataDefinition({
|
|
736
|
+
verifyingContract: contractAddress,
|
|
737
|
+
// we need to sign here for the anvil chain, cause thats where it is run on
|
|
738
|
+
chainId: viemClients.chain.id,
|
|
739
|
+
premintConfig: collaboratorPremintConfig,
|
|
740
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
741
|
+
}),
|
|
742
|
+
account: collaboratorAccount,
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
const quantityToMint = 2n;
|
|
746
|
+
|
|
747
|
+
const valueToSend = (
|
|
748
|
+
await getPremintMintCosts({
|
|
749
|
+
publicClient: viemClients.publicClient,
|
|
750
|
+
quantityToMint,
|
|
751
|
+
tokenContract: contractAddress,
|
|
752
|
+
tokenPrice: premintConfig.tokenConfig.pricePerToken,
|
|
753
|
+
})
|
|
754
|
+
).totalCostEth;
|
|
755
|
+
|
|
756
|
+
const mintArguments: PremintMintArguments = {
|
|
757
|
+
mintComment: "",
|
|
758
|
+
mintRecipient: collectorAccount,
|
|
759
|
+
mintRewardsRecipients: [],
|
|
760
|
+
};
|
|
761
|
+
|
|
762
|
+
const firstMinter = collectorAccount;
|
|
763
|
+
|
|
764
|
+
await viemClients.publicClient.simulateContract({
|
|
765
|
+
abi: preminterAbi,
|
|
766
|
+
functionName: "premint",
|
|
767
|
+
account: collectorAccount,
|
|
768
|
+
chain: viemClients.chain,
|
|
769
|
+
address: PREMINTER_ADDRESS,
|
|
770
|
+
args: [
|
|
771
|
+
contractConfig,
|
|
772
|
+
zeroAddress,
|
|
773
|
+
encodePremintConfig({
|
|
774
|
+
premintConfig: collaboratorPremintConfig,
|
|
775
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
776
|
+
}),
|
|
777
|
+
collaboratorSignedMessage,
|
|
778
|
+
quantityToMint,
|
|
779
|
+
mintArguments,
|
|
780
|
+
firstMinter,
|
|
781
|
+
zeroAddress,
|
|
782
|
+
],
|
|
783
|
+
value: valueToSend,
|
|
784
|
+
});
|
|
785
|
+
|
|
786
|
+
// now have the collector execute collaborators signed message;
|
|
787
|
+
// it should create the contract, the token, and add the collaborator
|
|
788
|
+
// as an admin to the contract along with the original creator
|
|
789
|
+
let tx = await viemClients.walletClient.writeContract({
|
|
790
|
+
abi: preminterAbi,
|
|
791
|
+
functionName: "premint",
|
|
792
|
+
account: collectorAccount,
|
|
793
|
+
chain: viemClients.chain,
|
|
794
|
+
address: PREMINTER_ADDRESS,
|
|
795
|
+
args: [
|
|
796
|
+
contractConfig,
|
|
797
|
+
zeroAddress,
|
|
798
|
+
encodePremintConfig({
|
|
799
|
+
premintConfig: collaboratorPremintConfig,
|
|
800
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
801
|
+
}),
|
|
802
|
+
collaboratorSignedMessage,
|
|
803
|
+
quantityToMint,
|
|
804
|
+
mintArguments,
|
|
805
|
+
firstMinter,
|
|
806
|
+
zeroAddress,
|
|
807
|
+
],
|
|
808
|
+
value: valueToSend,
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
// ensure it succeeded
|
|
812
|
+
expect(
|
|
813
|
+
(
|
|
814
|
+
await viemClients.publicClient.waitForTransactionReceipt({
|
|
815
|
+
hash: tx,
|
|
816
|
+
})
|
|
817
|
+
).status,
|
|
818
|
+
).toBe("success");
|
|
819
|
+
|
|
820
|
+
tx = await viemClients.walletClient.writeContract({
|
|
821
|
+
abi: preminterAbi,
|
|
822
|
+
functionName: "premint",
|
|
823
|
+
account: collectorAccount,
|
|
824
|
+
chain: viemClients.chain,
|
|
825
|
+
address: PREMINTER_ADDRESS,
|
|
826
|
+
args: [
|
|
827
|
+
contractConfig,
|
|
828
|
+
zeroAddress,
|
|
829
|
+
encodePremintConfig({
|
|
830
|
+
premintConfig: premintConfig,
|
|
831
|
+
premintConfigVersion: PremintConfigVersion.V2,
|
|
832
|
+
}),
|
|
833
|
+
creatorSignedMessage,
|
|
834
|
+
quantityToMint,
|
|
835
|
+
mintArguments,
|
|
836
|
+
firstMinter,
|
|
837
|
+
zeroAddress,
|
|
838
|
+
],
|
|
839
|
+
value: valueToSend,
|
|
840
|
+
});
|
|
841
|
+
|
|
842
|
+
expect(
|
|
843
|
+
(
|
|
844
|
+
await viemClients.publicClient.waitForTransactionReceipt({
|
|
845
|
+
hash: tx,
|
|
846
|
+
})
|
|
847
|
+
).status,
|
|
848
|
+
).toBe("success");
|
|
849
|
+
|
|
850
|
+
// get balance of second token
|
|
851
|
+
const tokenBalances = await viemClients.publicClient.readContract({
|
|
852
|
+
abi: zoraCreator1155ImplABI,
|
|
853
|
+
address: contractAddress,
|
|
854
|
+
functionName: "balanceOfBatch",
|
|
855
|
+
args: [
|
|
856
|
+
[collectorAccount, collectorAccount],
|
|
857
|
+
[1n, 2n],
|
|
858
|
+
],
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
expect(tokenBalances).toEqual([quantityToMint, quantityToMint]);
|
|
862
|
+
},
|
|
863
|
+
// 10 second timeout
|
|
864
|
+
40 * 1000,
|
|
865
|
+
);
|
|
866
|
+
});
|