@zoralabs/protocol-sdk 0.9.5 → 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 +17 -2
  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 +2 -2
  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,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
+ }
package/src/sdk.ts CHANGED
@@ -12,6 +12,11 @@ import {
12
12
  IContractGetter,
13
13
  SubgraphContractGetter,
14
14
  } from "./create/contract-getter";
15
+ import { RewardsClient } from "./rewards/rewards-client";
16
+ import {
17
+ IRewardsGetter,
18
+ SubgraphRewardsGetter,
19
+ } from "./rewards/subgraph-rewards-getter";
15
20
 
16
21
  export type CreatorClient = {
17
22
  createPremint: PremintClient["createPremint"];
@@ -19,6 +24,8 @@ export type CreatorClient = {
19
24
  deletePremint: PremintClient["deletePremint"];
20
25
  create1155: Create1155Client["createNew1155"];
21
26
  create1155OnExistingContract: Create1155Client["createNew1155OnExistingContract"];
27
+ withdrawRewards: RewardsClient["withdrawRewards"];
28
+ getRewardsBalances: RewardsClient["getRewardsBalances"];
22
29
  };
23
30
 
24
31
  export type CollectorClient = {
@@ -34,6 +41,7 @@ export type CreatorClientConfig = ClientConfig & {
34
41
  /** API for submitting and getting premints. Defaults to the Zora Premint API */
35
42
  premintApi?: IPremintAPI;
36
43
  contractGetter?: IContractGetter;
44
+ rewardsGetter?: IRewardsGetter;
37
45
  };
38
46
 
39
47
  /**
@@ -60,6 +68,14 @@ export function createCreatorClient(
60
68
  new SubgraphContractGetter(clientConfig.chainId),
61
69
  });
62
70
 
71
+ const rewardsClient = new RewardsClient({
72
+ chainId: clientConfig.chainId,
73
+ publicClient: clientConfig.publicClient,
74
+ rewardsGetter:
75
+ clientConfig.rewardsGetter ||
76
+ new SubgraphRewardsGetter(clientConfig.chainId),
77
+ });
78
+
63
79
  return {
64
80
  createPremint: (p) => premintClient.createPremint(p),
65
81
  updatePremint: (p) => premintClient.updatePremint(p),
@@ -67,6 +83,8 @@ export function createCreatorClient(
67
83
  create1155: (p) => create1155CreatorClient.createNew1155(p),
68
84
  create1155OnExistingContract: (p) =>
69
85
  create1155CreatorClient.createNew1155OnExistingContract(p),
86
+ getRewardsBalances: (p) => rewardsClient.getRewardsBalances(p),
87
+ withdrawRewards: (p) => rewardsClient.withdrawRewards(p),
70
88
  };
71
89
  }
72
90