Skip to content

Commit

Permalink
Calculate available rewards using contract instead of events
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
theref committed Sep 24, 2024
1 parent a6f087e commit 6b97212
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 41 deletions.
87 changes: 49 additions & 38 deletions src/hooks/useFetchStakingRewards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 (
Expand All @@ -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
)
Expand All @@ -108,5 +112,12 @@ export const useFetchStakingRewards = () => {
}

fetch()
}, [stakingProviders, merkleDropContract, hasFetched, isFetching, dispatch])
}, [
stakingProviders,
merkleDropContract,
hasFetched,
isFetching,
dispatch,
fetchCumulativeClaims,
])
}
2 changes: 1 addition & 1 deletion src/web3/hooks/useClaimMerkleRewardsTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/web3/hooks/useMerkleDropContract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>
Expand Down

0 comments on commit 6b97212

Please sign in to comment.