From 6b9721209b8754c42d21a5c12c79f3a20d284ca5 Mon Sep 17 00:00:00 2001 From: James Campbell Date: Tue, 24 Sep 2024 19:33:46 +1000 Subject: [PATCH] Calculate available rewards using contract instead of events Rewards are cumulative, so anything that has already been claimed needs to be subtracted to get the current available amount. This commit gets the claimed amount by calling `cumulativeClaimed` on the merkle contract. This is more reliable than scanning for claimed events. --- src/hooks/useFetchStakingRewards.ts | 87 +++++++++++-------- .../hooks/useClaimMerkleRewardsTransaction.ts | 2 +- src/web3/hooks/useMerkleDropContract.ts | 4 +- 3 files changed, 52 insertions(+), 41 deletions(-) diff --git a/src/hooks/useFetchStakingRewards.ts b/src/hooks/useFetchStakingRewards.ts index c7b38fbf2..45baed2b3 100644 --- a/src/hooks/useFetchStakingRewards.ts +++ b/src/hooks/useFetchStakingRewards.ts @@ -12,6 +12,8 @@ import { setInterimRewards } from "../store/rewards" import { selectStakingProviders } from "../store/staking" import { BigNumber } from "ethers" import { Zero } from "@ethersproject/constants" +import { useMulticall } from "../web3/hooks/useMulticall" +import { ContractCall } from "../threshold-ts/multicall" interface StakingRewards { [stakingProvider: string]: string @@ -25,6 +27,16 @@ export const useFetchStakingRewards = () => { ) const dispatch = useDispatch() + const cumulativeClaimedCalls: ContractCall[] = stakingProviders.map( + (stakingProvider) => ({ + address: merkleDropContract!.address, + interface: merkleDropContract!.interface!, + method: "cumulativeClaimed", + args: [stakingProvider], + }) + ) + const fetchCumulativeClaims = useMulticall(cumulativeClaimedCalls) + useEffect(() => { const fetch = async () => { if ( @@ -43,56 +55,48 @@ export const useFetchStakingRewards = () => { // See https://github.com/threshold-network/token-dashboard/issues/765 // - Note also that TACo rewards now accrue on each block. They can be // calculated via TACoApp.availableRewards(address _stakingProvider) - const claimedEvents = await getContractPastEvents(merkleDropContract, { - eventName: "Claimed", - fromBlock: DEPLOYMENT_BLOCK, - filterParams: [stakingProviders], - }) - const claimedAmountToStakingProvider = claimedEvents.reduce( - ( - reducer: { [stakingProvider: string]: string }, - event - ): { [stakingProvider: string]: string } => { - const stakingProvider = getAddress( - event.args?.stakingProvider as string - ) - const prevAmount = BigNumber.from(reducer[stakingProvider] || Zero) - reducer[stakingProvider] = prevAmount - .add(event.args?.amount as string) - .toString() - return reducer + const cumulativeClaimedResults = await fetchCumulativeClaims() + console.log(cumulativeClaimedResults) + + const claimedAmountToStakingProvider = stakingProviders.reduce( + (acc, stakingProvider, index) => { + acc[getAddress(stakingProvider)] = + cumulativeClaimedResults[index].toString() + return acc }, - {} + {} as { [stakingProvider: string]: string } ) + console.log(claimedAmountToStakingProvider) - const claimedRewardsInCurrentMerkleRoot = new Set( - claimedEvents - .filter((_) => _.args?.merkleRoot === rewardsData.merkleRoot) - .map((_) => getAddress(_.args?.stakingProvider as string)) - ) + // const claimedRewardsInCurrentMerkleRoot = new Set( + // claimedEvents + // .filter((_) => _.args?.merkleRoot === rewardsData.merkleRoot) + // .map((_) => getAddress(_.args?.stakingProvider as string)) + // ) const stakingRewards: StakingRewards = {} for (const stakingProvider of stakingProviders) { - if ( - !rewardsData.claims.hasOwnProperty(stakingProvider) || - claimedRewardsInCurrentMerkleRoot.has(stakingProvider) - ) { + if (!(stakingProvider in (rewardsData as RewardsJSONData).claims)) { // If the JSON file doesn't contain proofs for a given staking - // provider it means this staking provider has no Merkle rewards - - // we can skip this iteration. - // TODO: ^ But there's going to be TACo rewards - - // If the `Claimed` event exists with a current merkle - // root for a given staking provider it means that rewards have - // already been claimed - we can skip this iteration. - // TODO: ^ Same, there can be TACo rewards + // provider it means this staking provider has no Merkle rewards continue } - const { amount } = (rewardsData as RewardsJSONData).claims[ stakingProvider ] + const claimedAmount = + claimedAmountToStakingProvider[stakingProvider] || "0" + if (BigNumber.from(amount).eq(BigNumber.from(claimedAmount))) { + // if the claimed amount is equal to the amount of rewards available, then skip + continue + } + // TODO: ^ But there's going to be TACo rewards + + // If the `Claimed` event exists with a current merkle + // root for a given staking provider it means that rewards have + // already been claimed - we can skip this iteration. + // TODO: ^ Same, there can be TACo rewards const claimableAmount = BigNumber.from(amount).sub( claimedAmountToStakingProvider[stakingProvider] || Zero ) @@ -108,5 +112,12 @@ export const useFetchStakingRewards = () => { } fetch() - }, [stakingProviders, merkleDropContract, hasFetched, isFetching, dispatch]) + }, [ + stakingProviders, + merkleDropContract, + hasFetched, + isFetching, + dispatch, + fetchCumulativeClaims, + ]) } diff --git a/src/web3/hooks/useClaimMerkleRewardsTransaction.ts b/src/web3/hooks/useClaimMerkleRewardsTransaction.ts index bb62849b3..29578e2d0 100644 --- a/src/web3/hooks/useClaimMerkleRewardsTransaction.ts +++ b/src/web3/hooks/useClaimMerkleRewardsTransaction.ts @@ -36,7 +36,7 @@ export const useClaimMerkleRewardsTransaction = ( // TODO: // - This only signals no Merkle rewards, but there may be TACo rewards - // - We can still call the new Merkle contract with a claim with an empty + // - We can still call the new Merkle contract with a claim with an empty // merkle proof, signalling to not try to claim Merkle rewards. This // will still try to claim TACo rewards automatically. if (availableRewardsToClaim.length === 0) { diff --git a/src/web3/hooks/useMerkleDropContract.ts b/src/web3/hooks/useMerkleDropContract.ts index 7515878ce..d6b733f04 100644 --- a/src/web3/hooks/useMerkleDropContract.ts +++ b/src/web3/hooks/useMerkleDropContract.ts @@ -12,9 +12,9 @@ const CONTRACT_ADDRESSESS = { // https://etherscan.io/address/0xea7ca290c7811d1cc2e79f8d706bd05d8280bd37 [ChainID.Ethereum.valueOf().toString()]: "0xeA7CA290c7811d1cC2e79f8d706bD05d8280BD37", - // https://sepolia.etherscan.io/address/0x4621a14bbB5a53f79Ea532bdc032b8ACc383B153 + // https://sepolia.etherscan.io/address/0xBF807283ef74616065A5595ACa49b25A569A33c6 [ChainID.Sepolia.valueOf().toString()]: - "0x4621a14bbB5a53f79Ea532bdc032b8ACc383B153", + "0xBF807283ef74616065A5595ACa49b25A569A33c6", // TODO: Set local address- how to resolve it in local network? [ChainID.Localhost.valueOf().toString()]: AddressZero, } as Record