From 330f1131b00d2cedda0ab1d479dc40c0f0acab45 Mon Sep 17 00:00:00 2001 From: Dan Oved Date: Thu, 14 Nov 2024 09:46:39 +0700 Subject: [PATCH] fix rewards queries (#913) fix rewards queries for erc20 addresses to filter by those that have the secondary market activated --- .changeset/famous-humans-cry.md | 5 ++ packages/protocol-sdk/src/anvil.ts | 1 + .../src/fixtures/rewards-query-results.ts | 11 +++- .../src/rewards/rewards-client.test.ts | 50 +++++++++++++++---- .../src/rewards/rewards-queries.ts | 20 ++++++-- .../src/rewards/subgraph-queries.ts | 4 +- .../src/rewards/subgraph-rewards-getter.ts | 34 +++++++++---- 7 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 .changeset/famous-humans-cry.md diff --git a/.changeset/famous-humans-cry.md b/.changeset/famous-humans-cry.md new file mode 100644 index 000000000..02ffd0670 --- /dev/null +++ b/.changeset/famous-humans-cry.md @@ -0,0 +1,5 @@ +--- +"@zoralabs/protocol-sdk": patch +--- + +Fix royalties queries to filter by erc20z that have secondary activated diff --git a/packages/protocol-sdk/src/anvil.ts b/packages/protocol-sdk/src/anvil.ts index dc1126a5f..0c114ad28 100644 --- a/packages/protocol-sdk/src/anvil.ts +++ b/packages/protocol-sdk/src/anvil.ts @@ -113,6 +113,7 @@ export const makeAnvilTest = ({ export const forkUrls = { zoraMainnet: `https://rpc.zora.energy/${process.env.VITE_CONDUIT_KEY}`, zoraSepolia: `https://sepolia.rpc.zora.energy/${process.env.VITE_CONDUIT_KEY}`, + baseMainnet: `https://base-mainnet.g.alchemy.com/v2/${process.env.VITE_ALCHEMY_KEY}`, }; export const anvilTest = makeAnvilTest({ diff --git a/packages/protocol-sdk/src/fixtures/rewards-query-results.ts b/packages/protocol-sdk/src/fixtures/rewards-query-results.ts index cdf9d0bfc..82a6a241d 100644 --- a/packages/protocol-sdk/src/fixtures/rewards-query-results.ts +++ b/packages/protocol-sdk/src/fixtures/rewards-query-results.ts @@ -4,10 +4,17 @@ import { CreatorERC20zQueryResult, } from "../rewards/subgraph-queries"; -const mockResult = (erz20z: Address): RewardsToken => ({ +const mockResult = ({ + erz20z, + secondaryActivated, +}: { + erz20z: Address; + secondaryActivated: boolean; +}): RewardsToken => ({ salesStrategies: [ { zoraTimedMinter: { + secondaryActivated, erc20Z: { id: erz20z, }, @@ -19,7 +26,7 @@ const mockResult = (erz20z: Address): RewardsToken => ({ export const mockRewardsQueryResults = ({ erc20z, }: { - erc20z: Address[]; + erc20z: { secondaryActivated: boolean; erz20z: Address }[]; }): CreatorERC20zQueryResult => ({ zoraCreateTokens: erc20z.map(mockResult), }); diff --git a/packages/protocol-sdk/src/rewards/rewards-client.test.ts b/packages/protocol-sdk/src/rewards/rewards-client.test.ts index 2103e5a21..27216c219 100644 --- a/packages/protocol-sdk/src/rewards/rewards-client.test.ts +++ b/packages/protocol-sdk/src/rewards/rewards-client.test.ts @@ -1,12 +1,12 @@ import { describe, expect, vi } from "vitest"; import { encodeAbiParameters, erc20Abi, parseEther } from "viem"; -import { zoraSepolia } from "viem/chains"; +import { zoraSepolia, base } from "viem/chains"; import { forkUrls, makeAnvilTest, simulateAndWriteContractWithRetries, } from "src/anvil"; -import { createCollectorClient } from "src/sdk"; +import { createCollectorClient, createCreatorClient } from "src/sdk"; import { new1155ContractVersion } from "src/create/contract-setup"; import { ISubgraphQuerier } from "src/apis/subgraph-querier"; import { mockTimedSaleStrategyTokenQueryResult } from "src/fixtures/mint-query-results"; @@ -16,11 +16,32 @@ import { zoraCreator1155ImplABI, } from "@zoralabs/protocol-deployments"; import { makeContractParameters } from "src/utils"; -import { mockRewardsQueryResults } from "src/fixtures/rewards-query-results"; import { setupContractAndToken } from "src/fixtures/contract-setup"; import { advanceToSaleAndAndLaunchMarket } from "src/fixtures/secondary"; +import { CreatorERC20zQueryResult } from "./subgraph-queries"; describe("rewardsClient", () => { + makeAnvilTest({ + forkBlockNumber: 22375202, + forkUrl: forkUrls.baseMainnet, + anvilChainId: base.id, + })( + "it can query rewards balances where there are multiple minters", + async ({ viemClients: { publicClient, chain } }) => { + const creatorClient = createCreatorClient({ + chainId: chain.id, + publicClient, + }); + const rewardsBalance = await creatorClient.getRewardsBalances({ + account: "0x129F04B140Acc1AA350be2F9f048C178103c62f3", + }); + + const erc20zKeys = Object.keys(rewardsBalance.secondaryRoyalties.erc20); + + expect(erc20zKeys.length).toBeGreaterThan(0); + }, + 20_000, + ); makeAnvilTest({ forkBlockNumber: 14653556, forkUrl: forkUrls.zoraSepolia, @@ -251,20 +272,31 @@ describe("rewardsClient", () => { // now we should be able to get rewards balances for these royalties + const mockResult: CreatorERC20zQueryResult = { + zoraCreateTokens: [ + { + salesStrategies: [ + { + zoraTimedMinter: { + secondaryActivated: true, + erc20Z: { id: erc20z }, + }, + }, + ], + }, + ], + }; + // we need to stub the subgraph return rewardsGetter.subgraphQuerier.query = vi .fn() - .mockResolvedValue( - mockRewardsQueryResults({ - erc20z: [erc20z], - }), - ); + .mockResolvedValue(mockResult); const rewardsBalance = await creatorClient.getRewardsBalances({ account: creatorAccount, }); - expect(rewardsBalance.secondaryRoyalties.eth).toBeGreaterThan(0); + expect(rewardsBalance.protocolRewards).toBeGreaterThan(0n); expect(rewardsBalance.secondaryRoyalties.erc20[erc20z]).toBeGreaterThan( 0, ); diff --git a/packages/protocol-sdk/src/rewards/rewards-queries.ts b/packages/protocol-sdk/src/rewards/rewards-queries.ts index a59a3f2b7..c99ecb5ca 100644 --- a/packages/protocol-sdk/src/rewards/rewards-queries.ts +++ b/packages/protocol-sdk/src/rewards/rewards-queries.ts @@ -77,7 +77,13 @@ export const getRewardsBalance = async ({ rewardsGetter: IRewardsGetter; }): Promise => { const address = typeof account === "string" ? account : account.address; - const erc20Zs = await rewardsGetter.getErc20ZzForCreator({ address }); + const erc20ZsAndSecondaryActivated = await rewardsGetter.getErc20ZzForCreator( + { address }, + ); + + const validErc20Zs = erc20ZsAndSecondaryActivated + .filter(({ secondaryActivated }) => secondaryActivated) + .map(({ erc20z }) => erc20z); // Perform multicall to get protocol rewards balance and unclaimed fees const result = await (publicClient as PublicClientWithMulticall).multicall({ @@ -98,7 +104,7 @@ export const getRewardsBalance = async ({ ], abi: erc20ZRoyaltiesABI, functionName: "getUnclaimedFeesBatch", - args: [erc20Zs], + args: [validErc20Zs], }, ], multicallAddress: multicall3Address, @@ -147,9 +153,13 @@ const makeClaimSecondaryRoyaltiesCalls = async ({ chainId: number; rewardsGetter: IRewardsGetter; }) => { - const erc20z = await rewardsGetter.getErc20ZzForCreator({ - address: claimFor, - }); + const erc20ZsAndSecondaryActivated = await rewardsGetter.getErc20ZzForCreator( + { address: claimFor }, + ); + + const erc20z = erc20ZsAndSecondaryActivated + .filter(({ secondaryActivated }) => secondaryActivated) + .map(({ erc20z }) => erc20z); const royaltiesAddress = erc20ZRoyaltiesAddress[chainId as keyof typeof erc20ZRoyaltiesAddress]; diff --git a/packages/protocol-sdk/src/rewards/subgraph-queries.ts b/packages/protocol-sdk/src/rewards/subgraph-queries.ts index 63204647a..d3f289520 100644 --- a/packages/protocol-sdk/src/rewards/subgraph-queries.ts +++ b/packages/protocol-sdk/src/rewards/subgraph-queries.ts @@ -4,7 +4,8 @@ import { Address } from "viem"; export type RewardsToken = { salesStrategies: [ { - zoraTimedMinter: { + zoraTimedMinter?: { + secondaryActivated: boolean; erc20Z: { id: Address; }; @@ -33,6 +34,7 @@ export function buildCreatorERC20zs({ } salesStrategies { zoraTimedMinter { + secondaryActivated erc20Z { id } diff --git a/packages/protocol-sdk/src/rewards/subgraph-rewards-getter.ts b/packages/protocol-sdk/src/rewards/subgraph-rewards-getter.ts index 24c2f3e31..b92942bac 100644 --- a/packages/protocol-sdk/src/rewards/subgraph-rewards-getter.ts +++ b/packages/protocol-sdk/src/rewards/subgraph-rewards-getter.ts @@ -4,7 +4,9 @@ import { Address } from "viem"; import { buildCreatorERC20zs } from "./subgraph-queries"; export interface IRewardsGetter { - getErc20ZzForCreator: (params: { address: Address }) => Promise; + getErc20ZzForCreator: (params: { + address: Address; + }) => Promise<{ secondaryActivated: boolean; erc20z: Address }[]>; } export class SubgraphRewardsGetter @@ -15,19 +17,31 @@ export class SubgraphRewardsGetter super(chainId, subgraphQuerier); } - async getErc20ZzForCreator({ - address, - }: { - address: Address; - }): Promise { + async getErc20ZzForCreator({ address }: { address: Address }) { const queryResults = await this.querySubgraphWithRetries( buildCreatorERC20zs({ address }), ); - return ( - queryResults?.map( - (result) => result.salesStrategies[0].zoraTimedMinter.erc20Z.id, - ) || [] + const results = ( + queryResults?.map((result) => { + const timedMinter = result.salesStrategies[0].zoraTimedMinter; + + if (!timedMinter) { + return null; + } + + return { + secondaryActivated: timedMinter.secondaryActivated, + erc20z: timedMinter.erc20Z.id, + }; + }) || [] + ).filter( + ( + idAndActivated, + ): idAndActivated is { secondaryActivated: boolean; erc20z: Address } => + !!idAndActivated, ); + + return results; } }