@zoralabs/protocol-sdk 0.9.6 → 0.10.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 (45) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/CHANGELOG.md +6 -0
  3. package/dist/anvil.d.ts +11 -1
  4. package/dist/anvil.d.ts.map +1 -1
  5. package/dist/apis/multicall3.d.ts +9 -0
  6. package/dist/apis/multicall3.d.ts.map +1 -0
  7. package/dist/apis/subgraph-getter.d.ts +9 -0
  8. package/dist/apis/subgraph-getter.d.ts.map +1 -0
  9. package/dist/create/contract-getter.d.ts +3 -6
  10. package/dist/create/contract-getter.d.ts.map +1 -1
  11. package/dist/fixtures/mint-query-results.d.ts +10 -0
  12. package/dist/fixtures/mint-query-results.d.ts.map +1 -0
  13. package/dist/fixtures/rewards-query-results.d.ts +6 -0
  14. package/dist/fixtures/rewards-query-results.d.ts.map +1 -0
  15. package/dist/index.cjs +402 -161
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.js +408 -161
  18. package/dist/index.js.map +1 -1
  19. package/dist/mint/subgraph-mint-getter.d.ts +3 -5
  20. package/dist/mint/subgraph-mint-getter.d.ts.map +1 -1
  21. package/dist/rewards/rewards-client.d.ts +34 -0
  22. package/dist/rewards/rewards-client.d.ts.map +1 -0
  23. package/dist/rewards/rewards-queries.d.ts +34 -0
  24. package/dist/rewards/rewards-queries.d.ts.map +1 -0
  25. package/dist/rewards/subgraph-queries.d.ts +20 -0
  26. package/dist/rewards/subgraph-queries.d.ts.map +1 -0
  27. package/dist/rewards/subgraph-rewards-getter.d.ts +15 -0
  28. package/dist/rewards/subgraph-rewards-getter.d.ts.map +1 -0
  29. package/dist/sdk.d.ts +5 -0
  30. package/dist/sdk.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/apis/multicall3.ts +19 -0
  33. package/src/apis/subgraph-getter.ts +33 -0
  34. package/src/create/1155-create-helper.test.ts +18 -18
  35. package/src/create/contract-getter.ts +7 -29
  36. package/src/fixtures/mint-query-results.ts +55 -0
  37. package/src/fixtures/rewards-query-results.ts +25 -0
  38. package/src/mint/mint-client.test.ts +18 -46
  39. package/src/mint/subgraph-mint-getter.ts +7 -27
  40. package/src/rewards/rewards-client.test.ts +406 -0
  41. package/src/rewards/rewards-client.ts +67 -0
  42. package/src/rewards/rewards-queries.ts +253 -0
  43. package/src/rewards/subgraph-queries.ts +49 -0
  44. package/src/rewards/subgraph-rewards-getter.ts +33 -0
  45. package/src/sdk.ts +18 -0
@@ -0,0 +1,406 @@
1
+ import { describe, expect, vi } from "vitest";
2
+ import {
3
+ Address,
4
+ Chain,
5
+ encodeAbiParameters,
6
+ erc20Abi,
7
+ parseEther,
8
+ PublicClient,
9
+ WalletClient,
10
+ } from "viem";
11
+ import { zoraSepolia } from "viem/chains";
12
+ import {
13
+ forkUrls,
14
+ makeAnvilTest,
15
+ simulateAndWriteContractWithRetries,
16
+ } from "src/anvil";
17
+ import { createCollectorClient, createCreatorClient } from "src/sdk";
18
+ import {
19
+ demoContractMetadataURI,
20
+ demoTokenMetadataURI,
21
+ } from "src/create/1155-create-helper.test";
22
+ import { new1155ContractVersion } from "src/create/contract-setup";
23
+ import { ISubgraphQuerier } from "src/apis/subgraph-querier";
24
+ import { SubgraphMintGetter } from "src/mint/subgraph-mint-getter";
25
+ import { mockTimedSaleStrategyTokenQueryResult } from "src/fixtures/mint-query-results";
26
+ import {
27
+ secondarySwapABI,
28
+ secondarySwapAddress,
29
+ zoraCreator1155ImplABI,
30
+ zoraTimedSaleStrategyABI,
31
+ zoraTimedSaleStrategyAddress,
32
+ } from "@zoralabs/protocol-deployments";
33
+ import { makeContractParameters } from "src/utils";
34
+ import { SubgraphRewardsGetter } from "./subgraph-rewards-getter";
35
+ import { mockRewardsQueryResults } from "src/fixtures/rewards-query-results";
36
+
37
+ async function setupContractAndToken({
38
+ chain,
39
+ publicClient,
40
+ creatorAccount,
41
+ walletClient,
42
+ }: {
43
+ chain: Chain;
44
+ publicClient: PublicClient;
45
+ creatorAccount: Address;
46
+ walletClient: WalletClient;
47
+ }) {
48
+ const rewardsGetter = new SubgraphRewardsGetter(chain.id);
49
+ const creatorClient = createCreatorClient({
50
+ chainId: chain.id,
51
+ publicClient,
52
+ rewardsGetter,
53
+ });
54
+
55
+ const mintGetter = new SubgraphMintGetter(chain.id);
56
+ // create a new 1155 contract
57
+
58
+ const { contractAddress, parameters, newTokenId } =
59
+ await creatorClient.create1155({
60
+ contract: {
61
+ uri: demoContractMetadataURI,
62
+ name: `Test 1155-${Math.round(Math.random() * 100_000_000_000)}`,
63
+ },
64
+ token: {
65
+ tokenMetadataURI: demoTokenMetadataURI,
66
+ },
67
+ account: creatorAccount,
68
+ });
69
+
70
+ await simulateAndWriteContractWithRetries({
71
+ parameters,
72
+ walletClient,
73
+ publicClient,
74
+ });
75
+
76
+ return {
77
+ creatorClient,
78
+ contractAddress,
79
+ newTokenId,
80
+ mintGetter,
81
+ rewardsGetter,
82
+ };
83
+ }
84
+
85
+ describe("rewardsClient", () => {
86
+ makeAnvilTest({
87
+ forkBlockNumber: 13914833,
88
+ forkUrl: forkUrls.zoraSepolia,
89
+ anvilChainId: zoraSepolia.id,
90
+ })(
91
+ "it can view and withdraw rewards for mints",
92
+ async ({
93
+ viemClients: { publicClient, chain, walletClient, testClient },
94
+ }) => {
95
+ const creatorAccount = (await walletClient.getAddresses()!)[0]!;
96
+ const collectorAccount = (await walletClient.getAddresses()!)[1]!;
97
+
98
+ const { creatorClient, contractAddress, newTokenId, mintGetter } =
99
+ await setupContractAndToken({
100
+ chain,
101
+ publicClient,
102
+ creatorAccount,
103
+ walletClient,
104
+ });
105
+
106
+ await testClient.setBalance({
107
+ address: collectorAccount,
108
+ value: parseEther("10"),
109
+ });
110
+
111
+ const quantityToMint = 10n;
112
+
113
+ mintGetter.subgraphQuerier.query = vi
114
+ .fn<ISubgraphQuerier["query"]>()
115
+ .mockResolvedValue({
116
+ zoraCreateToken: mockTimedSaleStrategyTokenQueryResult({
117
+ chainId: chain.id,
118
+ tokenId: newTokenId,
119
+ contractAddress,
120
+ contractVersion:
121
+ new1155ContractVersion[
122
+ chain.id as keyof typeof new1155ContractVersion
123
+ ],
124
+ }),
125
+ });
126
+
127
+ const collectorClient = createCollectorClient({
128
+ chainId: chain.id,
129
+ publicClient,
130
+ mintGetter,
131
+ });
132
+
133
+ const { parameters: collectParameters } = await collectorClient.mint({
134
+ minterAccount: collectorAccount,
135
+ mintType: "1155",
136
+ quantityToMint,
137
+ tokenId: newTokenId,
138
+ tokenContract: contractAddress,
139
+ });
140
+
141
+ await simulateAndWriteContractWithRetries({
142
+ parameters: collectParameters,
143
+ walletClient,
144
+ publicClient,
145
+ });
146
+
147
+ const rewardsBalance = await creatorClient.getRewardsBalances({
148
+ account: creatorAccount,
149
+ });
150
+
151
+ // creator reward is
152
+ const expectedRewardsBalance = quantityToMint * parseEther("0.0000555");
153
+
154
+ expect(rewardsBalance.protocolRewards).toEqual(expectedRewardsBalance);
155
+
156
+ const beforeBalance = await publicClient.getBalance({
157
+ address: creatorAccount,
158
+ });
159
+
160
+ const { parameters: withdrawParams } =
161
+ await creatorClient.withdrawRewards({
162
+ account: collectorAccount,
163
+ withdrawFor: creatorAccount,
164
+ claimSecondaryRoyalties: false,
165
+ });
166
+
167
+ await simulateAndWriteContractWithRetries({
168
+ parameters: withdrawParams,
169
+ walletClient,
170
+ publicClient,
171
+ });
172
+
173
+ const afterBalance = await publicClient.getBalance({
174
+ address: creatorAccount,
175
+ });
176
+
177
+ expect(afterBalance - beforeBalance).toBe(expectedRewardsBalance);
178
+ },
179
+ 30_000,
180
+ );
181
+ makeAnvilTest({
182
+ forkBlockNumber: 13914833,
183
+ forkUrl: forkUrls.zoraSepolia,
184
+ anvilChainId: zoraSepolia.id,
185
+ })(
186
+ "it can view and withdraw rewards and secondary royalties for mints",
187
+ async ({
188
+ viemClients: { publicClient, chain, walletClient, testClient },
189
+ }) => {
190
+ const creatorAccount = (await walletClient.getAddresses()!)[0]!;
191
+ const collectorAccount = (await walletClient.getAddresses()!)[1]!;
192
+
193
+ const {
194
+ creatorClient,
195
+ contractAddress,
196
+ newTokenId,
197
+ mintGetter,
198
+ rewardsGetter,
199
+ } = await setupContractAndToken({
200
+ chain,
201
+ publicClient,
202
+ creatorAccount,
203
+ walletClient,
204
+ });
205
+
206
+ await testClient.setBalance({
207
+ address: collectorAccount,
208
+ value: parseEther("100"),
209
+ });
210
+
211
+ const quantityToMint = 10_000n;
212
+
213
+ mintGetter.subgraphQuerier.query = vi
214
+ .fn<ISubgraphQuerier["query"]>()
215
+ .mockResolvedValue({
216
+ zoraCreateToken: mockTimedSaleStrategyTokenQueryResult({
217
+ chainId: chain.id,
218
+ tokenId: newTokenId,
219
+ contractAddress,
220
+ contractVersion:
221
+ new1155ContractVersion[
222
+ chain.id as keyof typeof new1155ContractVersion
223
+ ],
224
+ }),
225
+ });
226
+
227
+ const collectorClient = createCollectorClient({
228
+ chainId: chain.id,
229
+ publicClient,
230
+ mintGetter,
231
+ });
232
+
233
+ const { parameters: collectParameters } = await collectorClient.mint({
234
+ minterAccount: collectorAccount,
235
+ mintType: "1155",
236
+ quantityToMint,
237
+ tokenId: newTokenId,
238
+ tokenContract: contractAddress,
239
+ });
240
+
241
+ await simulateAndWriteContractWithRetries({
242
+ parameters: collectParameters,
243
+ walletClient,
244
+ publicClient,
245
+ });
246
+
247
+ const saleEnd = (
248
+ await publicClient.readContract({
249
+ abi: zoraTimedSaleStrategyABI,
250
+ address:
251
+ zoraTimedSaleStrategyAddress[
252
+ chain.id as keyof typeof zoraTimedSaleStrategyAddress
253
+ ],
254
+ functionName: "saleV2",
255
+ args: [contractAddress, newTokenId],
256
+ })
257
+ ).saleEnd;
258
+
259
+ // advance to end of sale
260
+ await testClient.setNextBlockTimestamp({
261
+ timestamp: saleEnd,
262
+ });
263
+
264
+ await testClient.mine({
265
+ blocks: 1,
266
+ });
267
+
268
+ // advance to end of sale
269
+ // launch the market
270
+ await simulateAndWriteContractWithRetries({
271
+ parameters: makeContractParameters({
272
+ abi: zoraTimedSaleStrategyABI,
273
+ functionName: "launchMarket",
274
+ args: [contractAddress, newTokenId],
275
+ address:
276
+ zoraTimedSaleStrategyAddress[
277
+ chain.id as keyof typeof zoraTimedSaleStrategyAddress
278
+ ],
279
+ account: collectorAccount,
280
+ }),
281
+ publicClient,
282
+ walletClient,
283
+ });
284
+
285
+ const erc20z = (
286
+ await publicClient.readContract({
287
+ abi: zoraTimedSaleStrategyABI,
288
+ address:
289
+ zoraTimedSaleStrategyAddress[
290
+ chain.id as keyof typeof zoraTimedSaleStrategyAddress
291
+ ],
292
+ functionName: "sale",
293
+ args: [contractAddress, newTokenId],
294
+ })
295
+ ).erc20zAddress;
296
+
297
+ // after market is launched, by 100 from the pool. there should be some rewards
298
+ // balances from secondary royalties
299
+ await simulateAndWriteContractWithRetries({
300
+ parameters: makeContractParameters({
301
+ abi: secondarySwapABI,
302
+ address:
303
+ secondarySwapAddress[chain.id as keyof typeof secondarySwapAddress],
304
+ functionName: "buy1155",
305
+ args: [
306
+ erc20z,
307
+ 100n,
308
+ collectorAccount,
309
+ collectorAccount,
310
+ parseEther("1"),
311
+ 0n,
312
+ ],
313
+ account: collectorAccount,
314
+ value: parseEther("1"),
315
+ }),
316
+ walletClient,
317
+ publicClient,
318
+ });
319
+
320
+ const abiParameters = [
321
+ { name: "recipient", internalType: "address payable", type: "address" },
322
+ { name: "minEthToAcquire", internalType: "uint256", type: "uint256" },
323
+ { name: "sqrtPriceLimitX96", internalType: "uint160", type: "uint160" },
324
+ ] as const;
325
+ const sellData = encodeAbiParameters(abiParameters, [
326
+ collectorAccount,
327
+ 0n,
328
+ 0n,
329
+ ]);
330
+ await simulateAndWriteContractWithRetries({
331
+ parameters: makeContractParameters({
332
+ functionName: "safeTransferFrom",
333
+ address: contractAddress,
334
+ abi: zoraCreator1155ImplABI,
335
+ account: collectorAccount,
336
+ args: [
337
+ collectorAccount,
338
+ secondarySwapAddress[chain.id as keyof typeof secondarySwapAddress],
339
+ newTokenId,
340
+ 100n,
341
+ sellData,
342
+ ],
343
+ }),
344
+ walletClient,
345
+ publicClient,
346
+ });
347
+
348
+ // now we should be able to get rewards balances for these royalties
349
+
350
+ // we need to stub the subgraph return
351
+ rewardsGetter.subgraphQuerier.query = vi
352
+ .fn<ISubgraphQuerier["query"]>()
353
+ .mockResolvedValue(
354
+ mockRewardsQueryResults({
355
+ erc20z: [erc20z],
356
+ }),
357
+ );
358
+
359
+ const rewardsBalance = await creatorClient.getRewardsBalances({
360
+ account: creatorAccount,
361
+ });
362
+
363
+ expect(rewardsBalance.secondaryRoyalties.eth).toBeGreaterThan(0);
364
+ expect(rewardsBalance.secondaryRoyalties.erc20[erc20z]).toBeGreaterThan(
365
+ 0,
366
+ );
367
+
368
+ const beforeBalance = await publicClient.getBalance({
369
+ address: creatorAccount,
370
+ });
371
+
372
+ // it can withdraw all rewards
373
+ await simulateAndWriteContractWithRetries({
374
+ parameters: (
375
+ await creatorClient.withdrawRewards({
376
+ account: collectorAccount,
377
+ withdrawFor: creatorAccount,
378
+ claimSecondaryRoyalties: true,
379
+ })
380
+ ).parameters,
381
+ publicClient,
382
+ walletClient,
383
+ });
384
+
385
+ const afterBalance = await publicClient.getBalance({
386
+ address: creatorAccount,
387
+ });
388
+
389
+ // make sure that some additional royalties were withdrawn, this is how we can do greater than
390
+ // we cant get exact precision
391
+ expect(afterBalance - beforeBalance).toBeGreaterThan(
392
+ rewardsBalance.protocolRewards,
393
+ );
394
+
395
+ const erc20balance = await publicClient.readContract({
396
+ abi: erc20Abi,
397
+ address: erc20z,
398
+ functionName: "balanceOf",
399
+ args: [creatorAccount],
400
+ });
401
+
402
+ expect(erc20balance).toBeGreaterThan(0);
403
+ },
404
+ 30_000,
405
+ );
406
+ });
@@ -0,0 +1,67 @@
1
+ import { IPublicClient } from "src/types";
2
+ import { IRewardsGetter } from "./subgraph-rewards-getter";
3
+ import { Account, Address } from "viem";
4
+ import { getRewardsBalance, withdrawRewards } from "./rewards-queries";
5
+
6
+ export type WithdrawRewardsParams = {
7
+ // account is the address that is withdrawing the rewards
8
+ account: Address | Account;
9
+ // withdrawFor is the address that is receiving the rewards
10
+ withdrawFor: Address;
11
+ // claimSecondaryRoyalties is an optional flag to claim secondary royalties. Defaults to `true`.
12
+ claimSecondaryRoyalties?: boolean;
13
+ };
14
+
15
+ export type GetRewardsBalancesParams = {
16
+ // The address or account to get the rewards balance for
17
+ account: Address | Account;
18
+ };
19
+
20
+ export class RewardsClient {
21
+ // Private properties to store chain ID, public client, and rewards getter
22
+ private readonly chainId: number;
23
+ private readonly publicClient: IPublicClient;
24
+ private readonly rewardsGetter: IRewardsGetter;
25
+
26
+ constructor({
27
+ chainId,
28
+ publicClient,
29
+ rewardsGetter,
30
+ }: {
31
+ chainId: number;
32
+ publicClient: IPublicClient;
33
+ rewardsGetter: IRewardsGetter;
34
+ }) {
35
+ // Initialize the private properties
36
+ this.chainId = chainId;
37
+ this.publicClient = publicClient;
38
+ this.rewardsGetter = rewardsGetter;
39
+ }
40
+
41
+ /** Withdraws rewards for a given account */
42
+ async withdrawRewards({
43
+ account,
44
+ withdrawFor,
45
+ claimSecondaryRoyalties,
46
+ }: WithdrawRewardsParams) {
47
+ return {
48
+ parameters: await withdrawRewards({
49
+ chainId: this.chainId,
50
+ rewardsGetter: this.rewardsGetter,
51
+ withdrawFor,
52
+ claimSecondaryRoyalties,
53
+ account,
54
+ }),
55
+ };
56
+ }
57
+
58
+ /** Retrieves the rewards balances for a given account */
59
+ async getRewardsBalances(params: GetRewardsBalancesParams) {
60
+ return getRewardsBalance({
61
+ account: params.account,
62
+ chainId: this.chainId,
63
+ publicClient: this.publicClient,
64
+ rewardsGetter: this.rewardsGetter,
65
+ });
66
+ }
67
+ }