From eca35f76c560b0de25616f22ac06ccae68f8c531 Mon Sep 17 00:00:00 2001 From: Yaki Date: Tue, 9 Jan 2024 08:42:40 -0800 Subject: [PATCH 1/4] fix restakeClaim logic --- src/utils/restakeClaim.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/utils/restakeClaim.ts b/src/utils/restakeClaim.ts index b99511a..670e0a8 100644 --- a/src/utils/restakeClaim.ts +++ b/src/utils/restakeClaim.ts @@ -60,9 +60,9 @@ export const restakeClaim = async ({ if (enableAutoRestake) { uniqueCores.forEach(core => { if (!core?.earliestEra) return; - const restakeAmount = handleRestakingLogic(); - if (restakeAmount && !restakeAmount.isZero()) { - const restakeAmountInteger = restakeAmount.integerValue().toString(); + const restakeUnclaimedAmount = handleRestakingLogic(); + if (restakeUnclaimedAmount && !restakeUnclaimedAmount.isZero()) { + const restakeAmountInteger = restakeUnclaimedAmount.integerValue().toString(); batch.push(api.tx.ocifStaking.stake(core.coreId, restakeAmountInteger)); } }); @@ -92,11 +92,11 @@ export const restakeClaim = async ({ if (enableAutoRestake) { uniqueCores.forEach(core => { if (!core?.earliestEra) return; - const restakeAmount = handleRestakingLogic(); - if (restakeAmount && !restakeAmount.isZero()) { - const batchTxFeesBigNumber = new BigNumber(batchTxFees.toString()); - const feesPerCore = batchTxFeesBigNumber.dividedBy(uniqueCores.length).times(1.5); - let adjustedRestakeAmount = restakeAmount.minus(feesPerCore); + const restakeUnclaimedAmount = handleRestakingLogic(); + if (restakeUnclaimedAmount && !restakeUnclaimedAmount.isZero()) { + const batchTxFeesBigNumber = new BigNumber(batchTxFees.toString()).times(1.5); + const feesPerCore = batchTxFeesBigNumber.dividedBy(uniqueCores.length); + let adjustedRestakeAmount = restakeUnclaimedAmount.minus(feesPerCore); if (adjustedRestakeAmount.isNegative()) { adjustedRestakeAmount = new BigNumber(0); } From 4af1a31119d2efbd52937c0af5663ae1efeb3ae9 Mon Sep 17 00:00:00 2001 From: Yaki Date: Tue, 9 Jan 2024 09:17:26 -0800 Subject: [PATCH 2/4] Fix restaking logic and handle partial fee in restakeClaim --- src/routes/staking.tsx | 20 +++++++++++++------- src/utils/restakeClaim.ts | 20 +++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/routes/staking.tsx b/src/routes/staking.tsx index dda3194..eebc0ca 100644 --- a/src/routes/staking.tsx +++ b/src/routes/staking.tsx @@ -19,6 +19,7 @@ import { StakedDaoType } from "./overview"; import OnOffSwitch from "../components/Switch"; import { autoRestake } from "../utils/autoRestake"; import { restakeClaim } from "../utils/restakeClaim"; +import { Balance } from "@polkadot/types/interfaces"; export type UnsubFunction = () => Promise; @@ -418,17 +419,17 @@ const Staking = () => { autoRestake(bool); }; - const handleRestakingLogic = () => { + const handleRestakingLogic = (partialFee?: Balance | undefined) => { // grab the total unclaimed rewards and account for the existential deposit - const unclaimedMinusED = new BigNumber(totalUnclaimed); + let unclaimedRewards = new BigNumber(totalUnclaimed); - // Check if unclaimedMinusED is a valid number - if (isNaN(unclaimedMinusED.toNumber())) { + // Check if unclaimedRewards is a valid number + if (isNaN(unclaimedRewards.toNumber())) { console.error("Invalid unclaimedMinusED"); return; } - if (unclaimedMinusED.toNumber() <= 0) { + if (unclaimedRewards.toNumber() <= 0) { console.error("unclaimedMinusED must be greater than 0"); return; } @@ -439,8 +440,13 @@ const Staking = () => { return; } - // divide unclaimedMinusED by the number of stakedDaos the user is part of - const unclaimedPerCore = unclaimedMinusED.div(stakedDaos.length); + // Subtract partialFee * 1.5 from unclaimedRewards if partialFee exists + if (partialFee) { + unclaimedRewards = unclaimedRewards.minus(new BigNumber(partialFee.toString()).times(1.5)); + } + + // divide unclaimedRewards by the number of stakedDaos the user has staked TNKR in + const unclaimedPerCore = unclaimedRewards.div(stakedDaos.length); return unclaimedPerCore; }; diff --git a/src/utils/restakeClaim.ts b/src/utils/restakeClaim.ts index 670e0a8..3e453d2 100644 --- a/src/utils/restakeClaim.ts +++ b/src/utils/restakeClaim.ts @@ -16,7 +16,7 @@ export interface RestakeClaimProps { setWaiting: (isWaiting: boolean) => void; disableClaiming: boolean; enableAutoRestake: boolean; - handleRestakingLogic: () => void | BigNumber; + handleRestakingLogic: (partialFee?: Balance | undefined) => void | BigNumber; } export const restakeClaim = async ({ @@ -75,12 +75,12 @@ export const restakeClaim = async ({ throw new Error("No transactions to send"); }; - // Get the fee that each batch transaction will cost + // Get the fee that the entire batch transaction will cost const info = await api.tx.utility.batchAll(batch as Vec).paymentInfo(selectedAccount.address, { signer: injector.signer }); const batchTxFees: Balance = info.partialFee; - const rebuildBatch: unknown[] = []; // Rebuild the batch exactly like we did before, + const rebuildBatch: unknown[] = []; uniqueCores.forEach(core => { if (!core?.earliestEra) return; for (let i = core.earliestEra; i < currentStakingEra; i++) { @@ -88,20 +88,14 @@ export const restakeClaim = async ({ } }); - // But now, using adjustedRestakeAmount in the stake call(s) + // But this time, use the adjusted restakeUnclaimedAmount (minus fees) if (enableAutoRestake) { uniqueCores.forEach(core => { if (!core?.earliestEra) return; - const restakeUnclaimedAmount = handleRestakingLogic(); + const restakeUnclaimedAmount = handleRestakingLogic(batchTxFees); if (restakeUnclaimedAmount && !restakeUnclaimedAmount.isZero()) { - const batchTxFeesBigNumber = new BigNumber(batchTxFees.toString()).times(1.5); - const feesPerCore = batchTxFeesBigNumber.dividedBy(uniqueCores.length); - let adjustedRestakeAmount = restakeUnclaimedAmount.minus(feesPerCore); - if (adjustedRestakeAmount.isNegative()) { - adjustedRestakeAmount = new BigNumber(0); - } - const adjustedRestakeAmountInteger = adjustedRestakeAmount.integerValue().toString(); - rebuildBatch.push(api.tx.ocifStaking.stake(core.coreId, adjustedRestakeAmountInteger)); + const restakeAmountInteger = restakeUnclaimedAmount.integerValue().toString(); + rebuildBatch.push(api.tx.ocifStaking.stake(core.coreId, restakeAmountInteger)); } }); } From 358f0510e3e70910c8d1e72c8d22fab8559e5f11 Mon Sep 17 00:00:00 2001 From: Yaki Date: Tue, 9 Jan 2024 10:23:21 -0800 Subject: [PATCH 3/4] fix bug in setupSubscription logic --- src/routes/overview.tsx | 70 ++++++---------------- src/routes/staking.tsx | 127 +++++++++++----------------------------- 2 files changed, 52 insertions(+), 145 deletions(-) diff --git a/src/routes/overview.tsx b/src/routes/overview.tsx index d1d3b60..021ab62 100644 --- a/src/routes/overview.tsx +++ b/src/routes/overview.tsx @@ -8,7 +8,6 @@ import useApi from "../hooks/useApi"; import useAccount from "../stores/account"; import { useQuery } from "urql"; import { AnyJson, Codec } from "@polkadot/types/types"; -import { UnsubscribePromise } from "@polkadot/api/types"; import { StakesInfo, VestingData, VestingSchedule } from "./claim"; import MetricDashboard from "../components/MetricDashboard"; import { loadProjectCores } from '../utils/stakingServices'; @@ -56,35 +55,10 @@ const Overview = () => { pause: !selectedAccount, }); - const setupSubscriptions = useCallback(({ - selectedAccount, - }: { - selectedAccount: InjectedAccountWithMeta; - }) => { - // Current block subscription - const blocks = api.rpc.chain.subscribeNewHeads(() => { }); - - // Next era starting block subscription - const nextEraStartingBlock = api.query.ocifStaking.nextEraStartingBlock(() => { }); - - let generalEraInfo; - - if (currentStakingEra > 0) { - generalEraInfo = api.query.ocifStaking.generalEraInfo(currentStakingEra); - } - - // Staking current era subscription - const currentEra = api.query.ocifStaking.currentEra((era: Codec) => { - setCurrentStakingEra(era.toPrimitive() as number); - }); - - const account = api.query.system.account(selectedAccount.address); - - const unsubs = [blocks, nextEraStartingBlock, currentEra, account]; - - if (generalEraInfo) { - unsubs.push(generalEraInfo); - } + const setupSubscriptions = useCallback(async () => { + if (!selectedAccount) { + throw new Error("selectedAccount is null"); + }; const userStakedInfoMap: Map< number, UserStakedInfoType @@ -92,7 +66,7 @@ const Overview = () => { if (coreEraStakeInfo && coreEraStakeInfo.length > 0) { for (const stakingCore of stakingCores) { - api.query.ocifStaking.generalStakerInfo( + await api.query.ocifStaking.generalStakerInfo( stakingCore.key, selectedAccount.address, (generalStakerInfo: Codec) => { @@ -147,15 +121,12 @@ const Overview = () => { const newTotalStaked = Array.from( userStakedInfoMap.values() ).reduce((acc, cur) => acc.plus(cur.staked), new BigNumber(0)); - setTotalUserStaked(newTotalStaked); } ); } } - - return Promise.resolve(unsubs as UnsubscribePromise[]); - }, [api, currentStakingEra, coreEraStakeInfo, stakingCores, unclaimedEras]); + }, [api, currentStakingEra, stakingCores, unclaimedEras, selectedAccount, coreEraStakeInfo]); const loadAggregateStaked = useCallback(async () => { const totalIssuance = (await api.query.balances.totalIssuance()).toPrimitive() as string; @@ -181,6 +152,11 @@ const Overview = () => { setLockedBalance(new BigNumber(balance.data.frozen)); }, [api]); + const loadCurrentEra = useCallback(async () => { + const currentStakingEra = (await api.query.ocifStaking.currentEra()).toPrimitive() as number; + setCurrentStakingEra(currentStakingEra); + }, [api]); + const loadVestingBalance = useCallback(async (selectedAccount: InjectedAccountWithMeta | null) => { if (!selectedAccount) return; try { @@ -198,6 +174,7 @@ const Overview = () => { } }, [api]); + const initializeData = useCallback(async (selectedAccount: InjectedAccountWithMeta | null) => { try { toast.loading("Loading staking cores..."); @@ -205,6 +182,7 @@ const Overview = () => { if (selectedAccount) { await Promise.all([ loadAccountInfo(selectedAccount), + loadCurrentEra(), loadCores(), loadAggregateStaked(), loadVestingBalance(selectedAccount) @@ -219,7 +197,7 @@ const Overview = () => { setLoading(false); setDataLoaded(true); } - }, [loadAccountInfo, loadCores, loadAggregateStaked, loadVestingBalance]); + }, [loadAccountInfo, loadCores, loadAggregateStaked, loadVestingBalance, loadCurrentEra]); useEffect(() => { initializeData(selectedAccount); @@ -258,27 +236,15 @@ const Overview = () => { }, [selectedAccount, stakingCores, rewardsCoreClaimedQuery.data, rewardsCoreClaimedQuery.fetching]); useEffect(() => { - let unsubs: UnsubscribePromise[] = []; const setup = async () => { - if (selectedAccount) { - unsubs = await setupSubscriptions({ selectedAccount }); + if (selectedAccount && typeof setupSubscriptions === 'function') { + console.log('calling setupSubscriptions'); + await setupSubscriptions(); } }; setup(); - - return () => { - unsubs.forEach((unsub: UnsubscribePromise) => { - if (unsub) { - unsub.then(unsubFunc => { - if (typeof unsubFunc === 'function') { - unsubFunc(); - } - }); - } - }); - }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedAccount, api]); + }, [selectedAccount, stakingCores, coreEraStakeInfo]); return (
diff --git a/src/routes/staking.tsx b/src/routes/staking.tsx index eebc0ca..e89c0f2 100644 --- a/src/routes/staking.tsx +++ b/src/routes/staking.tsx @@ -6,7 +6,6 @@ import LoadingSpinner from "../components/LoadingSpinner"; import useApi from "../hooks/useApi"; import useAccount from "../stores/account"; import { Codec } from "@polkadot/types/types"; -import { UnsubscribePromise } from "@polkadot/api/types"; import { StakesInfo } from "./claim"; import MetricDashboard from "../components/MetricDashboard"; import { loadProjectCores, loadStakedDaos } from '../utils/stakingServices'; @@ -205,48 +204,10 @@ const Staking = () => { pause: !selectedAccount, }); - const setupSubscriptions = useCallback(({ - selectedAccount, - }: { - selectedAccount: InjectedAccountWithMeta; - }) => { - // Current block subscription - const blocks = api.rpc.chain.subscribeNewHeads((header) => { - setCurrentBlock(header.number.toNumber()); - }); - - // Next era starting block subscription - const nextEraStartingBlock = api.query.ocifStaking.nextEraStartingBlock( - (blockNumber: Codec) => { - setNextEraBlock(blockNumber.toPrimitive() as number); - } - ); - - let generalEraInfo; - - if (currentStakingEra > 0) { - generalEraInfo = api.query.ocifStaking.generalEraInfo( - currentStakingEra, - (c: Codec) => { - const stakingInfo = c.toPrimitive() as StakedType; - - setTotalStaked(new BigNumber(stakingInfo.staked)); - } - ); - } - - // Staking current era subscription - const currentEra = api.query.ocifStaking.currentEra((era: Codec) => { - setCurrentStakingEra(era.toPrimitive() as number); - }); - - const account = api.query.system.account(selectedAccount.address); - - const unsubs = [blocks, nextEraStartingBlock, currentEra, account]; - - if (generalEraInfo) { - unsubs.push(generalEraInfo); - } + const setupSubscriptions = useCallback(async () => { + if (!selectedAccount) { + throw new Error("selectedAccount is null"); + }; const userStakedInfoMap: Map< number, UserStakedInfoType @@ -254,7 +215,7 @@ const Staking = () => { if (coreEraStakeInfo && coreEraStakeInfo.length > 0) { for (const stakingCore of stakingCores) { - api.query.ocifStaking.generalStakerInfo( + await api.query.ocifStaking.generalStakerInfo( stakingCore.key, selectedAccount.address, (generalStakerInfo: Codec) => { @@ -272,10 +233,8 @@ const Staking = () => { const unclaimedCore = unclaimed.cores.find(value => value.coreId === stakingCore.key); if (unclaimedCore) { - // Update the earliestEra of the existing core unclaimedCore.earliestEra = parseInt(unclaimedEarliest); } else { - // Add a new core unclaimed.cores.push({ coreId: stakingCore.key, earliestEra: parseInt(unclaimedEarliest), @@ -311,39 +270,17 @@ const Staking = () => { const newTotalStaked = Array.from( userStakedInfoMap.values() ).reduce((acc, cur) => acc.plus(cur.staked), new BigNumber(0)); - setTotalUserStaked(newTotalStaked); - // setUserStakedInfo(Array.from(userStakedInfoMap.values())); } ); } } - - return Promise.resolve(unsubs as UnsubscribePromise[]); - }, [api, currentStakingEra, coreEraStakeInfo, stakingCores, unclaimedEras]); - - const loadTotalUserStaked = useCallback(() => { - if (!selectedAccount) return; - - const coreInfoResults: { [key: number]: Partial | undefined; } = {}; - // const totalUserStakedResults: TotalUserStakedData = {}; - - for (const core of stakingCores) { - const coreInfo = getCoreInfo(coreEraStakeInfo, core); - // const totalUserStaked = getTotalUserStaked(userStakedInfo, core); - - coreInfoResults[core.key] = coreInfo; - // totalUserStakedResults[core.key] = totalUserStaked; - } - - // setTotalUserStakedData(totalUserStakedResults); - }, [selectedAccount, stakingCores, coreEraStakeInfo]); + }, [api, currentStakingEra, stakingCores, unclaimedEras, selectedAccount, coreEraStakeInfo]); const loadCurrentEraAndStake = useCallback(async () => { const currentStakingEra = (await api.query.ocifStaking.currentEra()).toPrimitive() as number; const generalEraInfo = (await api.query.ocifStaking.generalEraInfo(currentStakingEra)).toPrimitive() as StakedType; const totalStaked = new BigNumber(generalEraInfo.staked); - setCurrentStakingEra(currentStakingEra); setTotalStaked(totalStaked); }, [api]); @@ -356,7 +293,28 @@ const Staking = () => { const loadStakingConstants = useCallback(async () => { const blocksPerEra = api.consts.ocifStaking.blocksPerEra.toPrimitive() as number; setBlocksPerEra(blocksPerEra); - }, [api]); + + await api.rpc.chain.subscribeNewHeads((header) => { + setCurrentBlock(header.number.toNumber()); + }); + + await api.query.ocifStaking.nextEraStartingBlock( + (blockNumber: Codec) => { + setNextEraBlock(blockNumber.toPrimitive() as number); + } + ); + + if (currentStakingEra > 0) { + await api.query.ocifStaking.generalEraInfo( + currentStakingEra, + (c: Codec) => { + const stakingInfo = c.toPrimitive() as StakedType; + + setTotalStaked(new BigNumber(stakingInfo.staked)); + } + ); + } + }, [api, currentStakingEra]); const loadAggregateStaked = useCallback(async () => { const totalIssuance = (await api.query.balances.totalIssuance()).toPrimitive() as string; @@ -385,8 +343,8 @@ const Staking = () => { if (selectedAccount) { await Promise.all([ loadCores(), - loadStakingConstants(), loadCurrentEraAndStake(), + loadStakingConstants(), loadTotalSupply(), loadAggregateStaked() ]); @@ -526,10 +484,6 @@ const Staking = () => { setTotalUnclaimed(claimAllSuccess ? new BigNumber(0) : totalUnclaimed); }, [selectedAccount, rewardsUserClaimedQuery.fetching, rewardsUserClaimedQuery.data, claimAllSuccess]); - useEffect(() => { - loadTotalUserStaked(); - }, [loadTotalUserStaked]); - useEffect(() => { if (rewardsCoreClaimedQuery.fetching || !rewardsCoreClaimedQuery.data?.cores?.length || !selectedAccount) return; @@ -540,31 +494,18 @@ const Staking = () => { ); setCoreEraStakeInfo(uniqueCoreEraStakeInfo); - }, [selectedAccount, rewardsCoreClaimedQuery.fetching, rewardsCoreClaimedQuery.data]); + }, [selectedAccount, stakingCores, rewardsCoreClaimedQuery.fetching, rewardsCoreClaimedQuery.data]); useEffect(() => { - let unsubs: UnsubscribePromise[] = []; const setup = async () => { - if (selectedAccount) { - unsubs = await setupSubscriptions({ selectedAccount }); + if (selectedAccount && typeof setupSubscriptions === 'function') { + console.log('calling setupSubscriptions'); + await setupSubscriptions(); } }; setup(); - - return () => { - unsubs.forEach((unsub: UnsubscribePromise) => { - if (unsub) { - unsub.then(unsubFunc => { - if (typeof unsubFunc === 'function') { - console.log("Unsubscribing from subscription"); - unsubFunc(); - } - }); - } - }); - }; // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedAccount, api]); + }, [selectedAccount, stakingCores, coreEraStakeInfo]); return (
From ac48bedc27125fa6e1dda4a935b33431de987dbb Mon Sep 17 00:00:00 2001 From: Yaki Date: Tue, 9 Jan 2024 10:28:00 -0800 Subject: [PATCH 4/4] chore: fix small loading issue on overview --- src/routes/overview.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/routes/overview.tsx b/src/routes/overview.tsx index 021ab62..105384c 100644 --- a/src/routes/overview.tsx +++ b/src/routes/overview.tsx @@ -254,9 +254,7 @@ const Overview = () => { {isLoading || !isDataLoaded ? : null}
- {selectedAccount && - currentStakingEra && - unclaimedEras ? ( + {selectedAccount ? (