@zoralabs/protocol-sdk 0.9.6 → 0.11.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 (86) hide show
  1. package/.turbo/turbo-build.log +7 -7
  2. package/CHANGELOG.md +17 -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/1155-create-helper.test.d.ts +3 -0
  10. package/dist/create/1155-create-helper.test.d.ts.map +1 -0
  11. package/dist/create/contract-getter.d.ts +3 -6
  12. package/dist/create/contract-getter.d.ts.map +1 -1
  13. package/dist/fixtures/contract-setup.d.ts +16 -0
  14. package/dist/fixtures/contract-setup.d.ts.map +1 -0
  15. package/dist/fixtures/mint-query-results.d.ts +10 -0
  16. package/dist/fixtures/mint-query-results.d.ts.map +1 -0
  17. package/dist/fixtures/rewards-query-results.d.ts +6 -0
  18. package/dist/fixtures/rewards-query-results.d.ts.map +1 -0
  19. package/dist/index.cjs +989 -238
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.js +1013 -238
  22. package/dist/index.js.map +1 -1
  23. package/dist/mint/subgraph-mint-getter.d.ts +3 -5
  24. package/dist/mint/subgraph-mint-getter.d.ts.map +1 -1
  25. package/dist/rewards/rewards-client.d.ts +34 -0
  26. package/dist/rewards/rewards-client.d.ts.map +1 -0
  27. package/dist/rewards/rewards-queries.d.ts +34 -0
  28. package/dist/rewards/rewards-queries.d.ts.map +1 -0
  29. package/dist/rewards/subgraph-queries.d.ts +20 -0
  30. package/dist/rewards/subgraph-queries.d.ts.map +1 -0
  31. package/dist/rewards/subgraph-rewards-getter.d.ts +15 -0
  32. package/dist/rewards/subgraph-rewards-getter.d.ts.map +1 -0
  33. package/dist/sdk.d.ts +9 -0
  34. package/dist/sdk.d.ts.map +1 -1
  35. package/dist/secondary/conversions.d.ts +7 -0
  36. package/dist/secondary/conversions.d.ts.map +1 -0
  37. package/dist/secondary/secondary-client.d.ts +67 -0
  38. package/dist/secondary/secondary-client.d.ts.map +1 -0
  39. package/dist/secondary/slippage.d.ts +25 -0
  40. package/dist/secondary/slippage.d.ts.map +1 -0
  41. package/dist/secondary/types.d.ts +37 -0
  42. package/dist/secondary/types.d.ts.map +1 -0
  43. package/dist/secondary/uniswap/abis.d.ts +152 -0
  44. package/dist/secondary/uniswap/abis.d.ts.map +1 -0
  45. package/dist/secondary/uniswap/uniswapQuote.d.ts +13 -0
  46. package/dist/secondary/uniswap/uniswapQuote.d.ts.map +1 -0
  47. package/dist/secondary/uniswap/uniswapQuoteExact.d.ts +37 -0
  48. package/dist/secondary/uniswap/uniswapQuoteExact.d.ts.map +1 -0
  49. package/dist/secondary/uniswap/uniswapReserves.d.ts +3 -0
  50. package/dist/secondary/uniswap/uniswapReserves.d.ts.map +1 -0
  51. package/dist/secondary/utils.d.ts +10 -0
  52. package/dist/secondary/utils.d.ts.map +1 -0
  53. package/dist/sparks/sparks-contracts.d.ts.map +1 -1
  54. package/dist/types.d.ts +3 -2
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/utils.d.ts +2 -1
  57. package/dist/utils.d.ts.map +1 -1
  58. package/package.json +1 -1
  59. package/src/apis/multicall3.ts +19 -0
  60. package/src/apis/subgraph-getter.ts +33 -0
  61. package/src/create/1155-create-helper.test.ts +18 -18
  62. package/src/create/contract-getter.ts +7 -29
  63. package/src/fixtures/contract-setup.ts +57 -0
  64. package/src/fixtures/mint-query-results.ts +55 -0
  65. package/src/fixtures/rewards-query-results.ts +25 -0
  66. package/src/mint/mint-client.test.ts +18 -46
  67. package/src/mint/subgraph-mint-getter.ts +7 -27
  68. package/src/rewards/rewards-client.test.ts +310 -0
  69. package/src/rewards/rewards-client.ts +67 -0
  70. package/src/rewards/rewards-queries.ts +253 -0
  71. package/src/rewards/subgraph-queries.ts +49 -0
  72. package/src/rewards/subgraph-rewards-getter.ts +33 -0
  73. package/src/sdk.ts +29 -0
  74. package/src/secondary/conversions.ts +20 -0
  75. package/src/secondary/secondary-client.test.ts +276 -0
  76. package/src/secondary/secondary-client.ts +374 -0
  77. package/src/secondary/slippage.ts +42 -0
  78. package/src/secondary/types.ts +64 -0
  79. package/src/secondary/uniswap/abis.ts +20 -0
  80. package/src/secondary/uniswap/uniswapQuote.ts +134 -0
  81. package/src/secondary/uniswap/uniswapQuoteExact.ts +114 -0
  82. package/src/secondary/uniswap/uniswapReserves.ts +34 -0
  83. package/src/secondary/utils.ts +40 -0
  84. package/src/sparks/sparks-contracts.ts +1 -3
  85. package/src/types.ts +6 -7
  86. package/src/utils.ts +7 -1
@@ -0,0 +1,310 @@
1
+ import { describe, expect, vi } from "vitest";
2
+ import { encodeAbiParameters, erc20Abi, parseEther } from "viem";
3
+ import { zoraSepolia } from "viem/chains";
4
+ import {
5
+ forkUrls,
6
+ makeAnvilTest,
7
+ simulateAndWriteContractWithRetries,
8
+ } from "src/anvil";
9
+ import { createCollectorClient } from "src/sdk";
10
+ import { new1155ContractVersion } from "src/create/contract-setup";
11
+ import { ISubgraphQuerier } from "src/apis/subgraph-querier";
12
+ import { mockTimedSaleStrategyTokenQueryResult } from "src/fixtures/mint-query-results";
13
+ import {
14
+ secondarySwapABI,
15
+ secondarySwapAddress,
16
+ zoraCreator1155ImplABI,
17
+ } from "@zoralabs/protocol-deployments";
18
+ import { makeContractParameters } from "src/utils";
19
+ import { mockRewardsQueryResults } from "src/fixtures/rewards-query-results";
20
+ import { setupContractAndToken } from "src/fixtures/contract-setup";
21
+ import { advanceToSaleAndAndLaunchMarket } from "src/secondary/secondary-client.test";
22
+
23
+ describe("rewardsClient", () => {
24
+ makeAnvilTest({
25
+ forkBlockNumber: 13914833,
26
+ forkUrl: forkUrls.zoraSepolia,
27
+ anvilChainId: zoraSepolia.id,
28
+ })(
29
+ "it can view and withdraw rewards for mints",
30
+ async ({
31
+ viemClients: { publicClient, chain, walletClient, testClient },
32
+ }) => {
33
+ const creatorAccount = (await walletClient.getAddresses()!)[0]!;
34
+ const collectorAccount = (await walletClient.getAddresses()!)[1]!;
35
+
36
+ const { creatorClient, contractAddress, newTokenId, mintGetter } =
37
+ await setupContractAndToken({
38
+ chain,
39
+ publicClient,
40
+ creatorAccount,
41
+ walletClient,
42
+ });
43
+
44
+ await testClient.setBalance({
45
+ address: collectorAccount,
46
+ value: parseEther("10"),
47
+ });
48
+
49
+ const quantityToMint = 10n;
50
+
51
+ mintGetter.subgraphQuerier.query = vi
52
+ .fn<ISubgraphQuerier["query"]>()
53
+ .mockResolvedValue({
54
+ zoraCreateToken: mockTimedSaleStrategyTokenQueryResult({
55
+ chainId: chain.id,
56
+ tokenId: newTokenId,
57
+ contractAddress,
58
+ contractVersion:
59
+ new1155ContractVersion[
60
+ chain.id as keyof typeof new1155ContractVersion
61
+ ],
62
+ }),
63
+ });
64
+
65
+ const collectorClient = createCollectorClient({
66
+ chainId: chain.id,
67
+ publicClient,
68
+ mintGetter,
69
+ });
70
+
71
+ const { parameters: collectParameters } = await collectorClient.mint({
72
+ minterAccount: collectorAccount,
73
+ mintType: "1155",
74
+ quantityToMint,
75
+ tokenId: newTokenId,
76
+ tokenContract: contractAddress,
77
+ });
78
+
79
+ await simulateAndWriteContractWithRetries({
80
+ parameters: collectParameters,
81
+ walletClient,
82
+ publicClient,
83
+ });
84
+
85
+ const rewardsBalance = await creatorClient.getRewardsBalances({
86
+ account: creatorAccount,
87
+ });
88
+
89
+ // creator reward is
90
+ const expectedRewardsBalance = quantityToMint * parseEther("0.0000555");
91
+
92
+ expect(rewardsBalance.protocolRewards).toEqual(expectedRewardsBalance);
93
+
94
+ const beforeBalance = await publicClient.getBalance({
95
+ address: creatorAccount,
96
+ });
97
+
98
+ const { parameters: withdrawParams } =
99
+ await creatorClient.withdrawRewards({
100
+ account: collectorAccount,
101
+ withdrawFor: creatorAccount,
102
+ claimSecondaryRoyalties: false,
103
+ });
104
+
105
+ await simulateAndWriteContractWithRetries({
106
+ parameters: withdrawParams,
107
+ walletClient,
108
+ publicClient,
109
+ });
110
+
111
+ const afterBalance = await publicClient.getBalance({
112
+ address: creatorAccount,
113
+ });
114
+
115
+ expect(afterBalance - beforeBalance).toBe(expectedRewardsBalance);
116
+ },
117
+ 30_000,
118
+ );
119
+ makeAnvilTest({
120
+ forkBlockNumber: 13914833,
121
+ forkUrl: forkUrls.zoraSepolia,
122
+ anvilChainId: zoraSepolia.id,
123
+ })(
124
+ "it can view and withdraw rewards and secondary royalties for mints",
125
+ async ({
126
+ viemClients: { publicClient, chain, walletClient, testClient },
127
+ }) => {
128
+ const creatorAccount = (await walletClient.getAddresses()!)[0]!;
129
+ const collectorAccount = (await walletClient.getAddresses()!)[1]!;
130
+
131
+ const {
132
+ creatorClient,
133
+ contractAddress,
134
+ newTokenId,
135
+ mintGetter,
136
+ rewardsGetter,
137
+ } = await setupContractAndToken({
138
+ chain,
139
+ publicClient,
140
+ creatorAccount,
141
+ walletClient,
142
+ });
143
+
144
+ await testClient.setBalance({
145
+ address: collectorAccount,
146
+ value: parseEther("100"),
147
+ });
148
+
149
+ const quantityToMint = 10_000n;
150
+
151
+ mintGetter.subgraphQuerier.query = vi
152
+ .fn<ISubgraphQuerier["query"]>()
153
+ .mockResolvedValue({
154
+ zoraCreateToken: mockTimedSaleStrategyTokenQueryResult({
155
+ chainId: chain.id,
156
+ tokenId: newTokenId,
157
+ contractAddress,
158
+ contractVersion:
159
+ new1155ContractVersion[
160
+ chain.id as keyof typeof new1155ContractVersion
161
+ ],
162
+ }),
163
+ });
164
+
165
+ const collectorClient = createCollectorClient({
166
+ chainId: chain.id,
167
+ publicClient,
168
+ mintGetter,
169
+ });
170
+
171
+ const { parameters: collectParameters } = await collectorClient.mint({
172
+ minterAccount: collectorAccount,
173
+ mintType: "1155",
174
+ quantityToMint,
175
+ tokenId: newTokenId,
176
+ tokenContract: contractAddress,
177
+ });
178
+
179
+ await simulateAndWriteContractWithRetries({
180
+ parameters: collectParameters,
181
+ walletClient,
182
+ publicClient,
183
+ });
184
+
185
+ await advanceToSaleAndAndLaunchMarket({
186
+ chainId: chain.id,
187
+ account: collectorAccount,
188
+ publicClient,
189
+ walletClient,
190
+ collectorClient,
191
+ testClient,
192
+ contractAddress,
193
+ tokenId: newTokenId,
194
+ });
195
+
196
+ const erc20z = (await collectorClient.getSecondaryInfo({
197
+ contract: contractAddress,
198
+ tokenId: newTokenId,
199
+ }))!.erc20z!;
200
+
201
+ // after market is launched, by 100 from the pool. there should be some rewards
202
+ // balances from secondary royalties
203
+ await simulateAndWriteContractWithRetries({
204
+ parameters: makeContractParameters({
205
+ abi: secondarySwapABI,
206
+ address:
207
+ secondarySwapAddress[chain.id as keyof typeof secondarySwapAddress],
208
+ functionName: "buy1155",
209
+ args: [
210
+ erc20z,
211
+ 100n,
212
+ collectorAccount,
213
+ collectorAccount,
214
+ parseEther("1"),
215
+ 0n,
216
+ ],
217
+ account: collectorAccount,
218
+ value: parseEther("1"),
219
+ }),
220
+ walletClient,
221
+ publicClient,
222
+ });
223
+
224
+ const abiParameters = [
225
+ { name: "recipient", internalType: "address payable", type: "address" },
226
+ { name: "minEthToAcquire", internalType: "uint256", type: "uint256" },
227
+ { name: "sqrtPriceLimitX96", internalType: "uint160", type: "uint160" },
228
+ ] as const;
229
+ const sellData = encodeAbiParameters(abiParameters, [
230
+ collectorAccount,
231
+ 0n,
232
+ 0n,
233
+ ]);
234
+ await simulateAndWriteContractWithRetries({
235
+ parameters: makeContractParameters({
236
+ functionName: "safeTransferFrom",
237
+ address: contractAddress,
238
+ abi: zoraCreator1155ImplABI,
239
+ account: collectorAccount,
240
+ args: [
241
+ collectorAccount,
242
+ secondarySwapAddress[chain.id as keyof typeof secondarySwapAddress],
243
+ newTokenId,
244
+ 100n,
245
+ sellData,
246
+ ],
247
+ }),
248
+ walletClient,
249
+ publicClient,
250
+ });
251
+
252
+ // now we should be able to get rewards balances for these royalties
253
+
254
+ // we need to stub the subgraph return
255
+ rewardsGetter.subgraphQuerier.query = vi
256
+ .fn<ISubgraphQuerier["query"]>()
257
+ .mockResolvedValue(
258
+ mockRewardsQueryResults({
259
+ erc20z: [erc20z],
260
+ }),
261
+ );
262
+
263
+ const rewardsBalance = await creatorClient.getRewardsBalances({
264
+ account: creatorAccount,
265
+ });
266
+
267
+ expect(rewardsBalance.secondaryRoyalties.eth).toBeGreaterThan(0);
268
+ expect(rewardsBalance.secondaryRoyalties.erc20[erc20z]).toBeGreaterThan(
269
+ 0,
270
+ );
271
+
272
+ const beforeBalance = await publicClient.getBalance({
273
+ address: creatorAccount,
274
+ });
275
+
276
+ // it can withdraw all rewards
277
+ await simulateAndWriteContractWithRetries({
278
+ parameters: (
279
+ await creatorClient.withdrawRewards({
280
+ account: collectorAccount,
281
+ withdrawFor: creatorAccount,
282
+ claimSecondaryRoyalties: true,
283
+ })
284
+ ).parameters,
285
+ publicClient,
286
+ walletClient,
287
+ });
288
+
289
+ const afterBalance = await publicClient.getBalance({
290
+ address: creatorAccount,
291
+ });
292
+
293
+ // make sure that some additional royalties were withdrawn, this is how we can do greater than
294
+ // we cant get exact precision
295
+ expect(afterBalance - beforeBalance).toBeGreaterThan(
296
+ rewardsBalance.protocolRewards,
297
+ );
298
+
299
+ const erc20balance = await publicClient.readContract({
300
+ abi: erc20Abi,
301
+ address: erc20z,
302
+ functionName: "balanceOf",
303
+ args: [creatorAccount],
304
+ });
305
+
306
+ expect(erc20balance).toBeGreaterThan(0);
307
+ },
308
+ 30_000,
309
+ );
310
+ });
@@ -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
+ }
@@ -0,0 +1,253 @@
1
+ import {
2
+ protocolRewardsABI,
3
+ protocolRewardsAddress,
4
+ erc20ZRoyaltiesABI,
5
+ erc20ZRoyaltiesAddress,
6
+ wethAddress,
7
+ } from "@zoralabs/protocol-deployments";
8
+ import { makeContractParameters, PublicClient } from "src/utils";
9
+ import { PublicClient as PublicClientWithMulticall } from "viem";
10
+ import { Account, Address, encodeFunctionData, parseAbi } from "viem";
11
+ import { IRewardsGetter } from "./subgraph-rewards-getter";
12
+ import {
13
+ multicall3Abi,
14
+ multicall3Address,
15
+ Multicall3Call3,
16
+ } from "src/apis/multicall3";
17
+
18
+ // Aggregates unclaimed fees and separates ETH from other ERC20 tokens
19
+ function aggregateUnclaimedFees(
20
+ unclaimedFees: readonly {
21
+ token0: `0x${string}`;
22
+ token1: `0x${string}`;
23
+ token0Amount: bigint;
24
+ token1Amount: bigint;
25
+ }[],
26
+ wethAddress: Address,
27
+ ) {
28
+ let ethBalance = 0n;
29
+ // Aggregate unclaimed fees by token address
30
+ const unclaimedFeesAggregate = unclaimedFees.reduce(
31
+ (acc, fee) => {
32
+ const addFee = (token: `0x${string}`, amount: bigint) => {
33
+ if (token === wethAddress) {
34
+ ethBalance += amount;
35
+ } else if (acc[token]) {
36
+ acc[token] += amount;
37
+ } else {
38
+ acc[token] = amount;
39
+ }
40
+ };
41
+ // Apply 75% fee to each token amount
42
+ addFee(fee.token0, (fee.token0Amount * 75n) / 100n);
43
+ addFee(fee.token1, (fee.token1Amount * 75n) / 100n);
44
+ return acc;
45
+ },
46
+ {} as Record<string, bigint>,
47
+ );
48
+
49
+ return {
50
+ eth: (ethBalance * 75n) / 100n, // Apply 75% fee to ETH balance
51
+ erc20: unclaimedFeesAggregate,
52
+ };
53
+ }
54
+
55
+ // Define the return type for getRewardsBalance
56
+ type RewardsBalance = {
57
+ // The total balance, in eth of protocol rewards
58
+ protocolRewards: bigint;
59
+ // The secondary royalties balance.
60
+ secondaryRoyalties: {
61
+ // The balance, in eth, of secondary royalties
62
+ eth: bigint;
63
+ // The balance, aggregated by erc20 address, of secondary royalties
64
+ erc20: Record<Address, bigint>;
65
+ };
66
+ };
67
+
68
+ export const getRewardsBalance = async ({
69
+ account, // The account to check rewards for (Address or Account object)
70
+ publicClient, // The public client for making blockchain queries
71
+ chainId, // The ID of the blockchain network
72
+ rewardsGetter, // Interface for getting ERC20Z tokens for a creator
73
+ }: {
74
+ account: Account | Address;
75
+ publicClient: PublicClient;
76
+ chainId: number;
77
+ rewardsGetter: IRewardsGetter;
78
+ }): Promise<RewardsBalance> => {
79
+ const address = typeof account === "string" ? account : account.address;
80
+ const erc20Zs = await rewardsGetter.getErc20ZzForCreator({ address });
81
+
82
+ // Perform multicall to get protocol rewards balance and unclaimed fees
83
+ const result = await (publicClient as PublicClientWithMulticall).multicall({
84
+ contracts: [
85
+ {
86
+ address:
87
+ protocolRewardsAddress[
88
+ chainId as keyof typeof protocolRewardsAddress
89
+ ],
90
+ abi: protocolRewardsABI,
91
+ functionName: "balanceOf",
92
+ args: [address],
93
+ },
94
+ {
95
+ address:
96
+ erc20ZRoyaltiesAddress[
97
+ chainId as keyof typeof erc20ZRoyaltiesAddress
98
+ ],
99
+ abi: erc20ZRoyaltiesABI,
100
+ functionName: "getUnclaimedFeesBatch",
101
+ args: [erc20Zs],
102
+ },
103
+ ],
104
+ multicallAddress: multicall3Address,
105
+ allowFailure: false,
106
+ });
107
+
108
+ const protocolRewardsBalance = result[0];
109
+
110
+ const wethAddressForChain = wethAddress[chainId as keyof typeof wethAddress];
111
+
112
+ // Aggregate unclaimed fees
113
+ const unclaimedFeesAggregate = aggregateUnclaimedFees(
114
+ result[1],
115
+ wethAddressForChain,
116
+ );
117
+
118
+ return {
119
+ protocolRewards: protocolRewardsBalance,
120
+ secondaryRoyalties: unclaimedFeesAggregate,
121
+ };
122
+ };
123
+
124
+ export const withdrawProtocolRewards = ({
125
+ withdrawFor,
126
+ chainId,
127
+ }: {
128
+ // Account to execute the transaction
129
+ withdrawFor: Address;
130
+ chainId: number;
131
+ }) => {
132
+ return makeContractParameters({
133
+ abi: protocolRewardsABI,
134
+ functionName: "withdrawFor",
135
+ address:
136
+ protocolRewardsAddress[chainId as keyof typeof protocolRewardsAddress],
137
+ args: [withdrawFor, 0n],
138
+ });
139
+ };
140
+
141
+ const makeClaimSecondaryRoyaltiesCalls = async ({
142
+ claimFor,
143
+ chainId,
144
+ rewardsGetter,
145
+ }: {
146
+ claimFor: Address;
147
+ chainId: number;
148
+ rewardsGetter: IRewardsGetter;
149
+ }) => {
150
+ const erc20z = await rewardsGetter.getErc20ZzForCreator({
151
+ address: claimFor,
152
+ });
153
+
154
+ const royaltiesAddress =
155
+ erc20ZRoyaltiesAddress[chainId as keyof typeof erc20ZRoyaltiesAddress];
156
+
157
+ if (erc20z.length === 0) {
158
+ return [];
159
+ }
160
+
161
+ return erc20z.map((erc20z) => ({
162
+ target: royaltiesAddress,
163
+ callData: encodeFunctionData({
164
+ abi: erc20ZRoyaltiesABI,
165
+ functionName: "claimFor",
166
+ args: [erc20z],
167
+ }),
168
+ allowFailure: false,
169
+ }));
170
+ };
171
+
172
+ export async function withdrawSecondaryRoyalties({
173
+ claimFor,
174
+ chainId,
175
+ rewardsGetter,
176
+ }: {
177
+ claimFor: Address;
178
+ chainId: number;
179
+ rewardsGetter: IRewardsGetter;
180
+ }) {
181
+ const calls = await makeClaimSecondaryRoyaltiesCalls({
182
+ claimFor,
183
+ chainId,
184
+ rewardsGetter,
185
+ });
186
+
187
+ return makeContractParameters({
188
+ abi: multicall3Abi,
189
+ functionName: "aggregate3",
190
+ address: multicall3Address,
191
+ args: [calls],
192
+ });
193
+ }
194
+
195
+ // Extract protocol rewards withdrawal call creation
196
+ const createProtocolRewardsCall = (
197
+ chainId: number,
198
+ withdrawFor: Address,
199
+ ): Multicall3Call3 => ({
200
+ target:
201
+ protocolRewardsAddress[chainId as keyof typeof protocolRewardsAddress],
202
+ callData: encodeFunctionData({
203
+ abi: protocolRewardsABI,
204
+ functionName: "withdrawFor",
205
+ args: [withdrawFor, 0n],
206
+ }),
207
+ allowFailure: false,
208
+ });
209
+
210
+ // Extract multicall parameters creation
211
+ const createMulticallParameters = (
212
+ calls: Multicall3Call3[],
213
+ account: Address | Account,
214
+ ) =>
215
+ makeContractParameters({
216
+ abi: parseAbi(multicall3Abi),
217
+ functionName: "aggregate3",
218
+ address: multicall3Address,
219
+ args: [calls],
220
+ account,
221
+ });
222
+
223
+ // Main withdrawRewards function
224
+ export const withdrawRewards = async ({
225
+ account,
226
+ withdrawFor,
227
+ claimSecondaryRoyalties = true,
228
+ chainId,
229
+ rewardsGetter,
230
+ }: {
231
+ account: Address | Account;
232
+ withdrawFor: Address;
233
+ claimSecondaryRoyalties?: boolean;
234
+ chainId: number;
235
+ rewardsGetter: IRewardsGetter;
236
+ }) => {
237
+ if (!claimSecondaryRoyalties) {
238
+ return {
239
+ ...withdrawProtocolRewards({ chainId, withdrawFor }),
240
+ account,
241
+ };
242
+ }
243
+
244
+ const protocolRewardsCall = createProtocolRewardsCall(chainId, withdrawFor);
245
+ const secondaryRoyaltiesCalls = await makeClaimSecondaryRoyaltiesCalls({
246
+ chainId,
247
+ claimFor: withdrawFor,
248
+ rewardsGetter,
249
+ });
250
+
251
+ const allCalls = [protocolRewardsCall, ...secondaryRoyaltiesCalls];
252
+ return createMulticallParameters(allCalls, account);
253
+ };
@@ -0,0 +1,49 @@
1
+ import { ISubgraphQuery } from "src/apis/subgraph-querier";
2
+ import { Address } from "viem";
3
+
4
+ export type RewardsToken = {
5
+ salesStrategies: [
6
+ {
7
+ zoraTimedMinter: {
8
+ erc20Z: {
9
+ id: Address;
10
+ };
11
+ };
12
+ },
13
+ ];
14
+ };
15
+
16
+ export type CreatorERC20zQueryResult = {
17
+ zoraCreateTokens: RewardsToken[];
18
+ };
19
+
20
+ export function buildCreatorERC20zs({
21
+ address,
22
+ }: {
23
+ address: Address;
24
+ }): ISubgraphQuery<CreatorERC20zQueryResult["zoraCreateTokens"]> {
25
+ return {
26
+ query: `
27
+ query ($address: Bytes!) {
28
+ zoraCreateTokens(
29
+ where: { royalties_: { user: $address }, salesStrategies_: { type: "ZORA_TIMED" } }
30
+ ) {
31
+ royalties {
32
+ user
33
+ }
34
+ salesStrategies {
35
+ zoraTimedMinter {
36
+ erc20Z {
37
+ id
38
+ }
39
+ }
40
+ }
41
+ }
42
+ }
43
+ `,
44
+ variables: { address },
45
+ parseResponseData: (responseData: any | undefined) => {
46
+ return responseData?.zoraCreateTokens;
47
+ },
48
+ };
49
+ }
@@ -0,0 +1,33 @@
1
+ import { SubgraphGetter } from "src/apis/subgraph-getter";
2
+ import { ISubgraphQuerier } from "src/apis/subgraph-querier";
3
+ import { Address } from "viem";
4
+ import { buildCreatorERC20zs } from "./subgraph-queries";
5
+
6
+ export interface IRewardsGetter {
7
+ getErc20ZzForCreator: (params: { address: Address }) => Promise<Address[]>;
8
+ }
9
+
10
+ export class SubgraphRewardsGetter
11
+ extends SubgraphGetter
12
+ implements IRewardsGetter
13
+ {
14
+ constructor(chainId: number, subgraphQuerier?: ISubgraphQuerier) {
15
+ super(chainId, subgraphQuerier);
16
+ }
17
+
18
+ async getErc20ZzForCreator({
19
+ address,
20
+ }: {
21
+ address: Address;
22
+ }): Promise<Address[]> {
23
+ const queryResults = await this.querySubgraphWithRetries(
24
+ buildCreatorERC20zs({ address }),
25
+ );
26
+
27
+ return (
28
+ queryResults?.map(
29
+ (result) => result.salesStrategies[0].zoraTimedMinter.erc20Z.id,
30
+ ) || []
31
+ );
32
+ }
33
+ }