diff --git a/package.json b/package.json
index 4df55bd2..120bda33 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"node": ">=16.0.0"
},
"scripts": {
- "dev": "NODE_ENV=production next dev",
+ "dev": "next dev",
"build": "prisma generate && next build",
"start": "next start",
"lint": "next lint",
diff --git a/src/components/app/staking/AppStaking.tsx b/src/components/app/staking/AppStaking.tsx
index bfbd8f17..293bb426 100644
--- a/src/components/app/staking/AppStaking.tsx
+++ b/src/components/app/staking/AppStaking.tsx
@@ -6,11 +6,12 @@ import { AppStakingPools } from "./AppStakingPools";
import { useContractAddress } from "@/hooks/useContractAddress";
import { useAccount, usePublicClient } from "wagmi";
import { zeroAddress } from "viem";
-import { useReadLdyBalanceOf, useReadLdyDecimals } from "@/generated";
+import {
+ useReadLdyBalanceOf,
+ useReadLdyDecimals,
+ useReadLdyStakingRewardPerTokenStored,
+} from "@/generated";
import { useQueryClient } from "@tanstack/react-query";
-import { useGetStakingAprById } from "@/services/graph";
-import { STAKING_APR_INFO_ID } from "@/constants/staking";
-import { STAKING_APR_INFO_QUERY } from "@/services/graph/queries";
export const AppStaking: FC = () => {
const queryClient = useQueryClient();
@@ -25,25 +26,18 @@ export const AppStaking: FC = () => {
const { data: ldyDecimals } = useReadLdyDecimals();
- const {
- data: stakingAprInfo,
- refetch: refetchStakingAPR,
- isFetching: isFetchingAPR,
- } = useGetStakingAprById(STAKING_APR_INFO_ID);
+ const { data: rewardPerToken, queryKey: rewardPerTokenQuery } =
+ useReadLdyStakingRewardPerTokenStored();
- // Refetch LdyBalance & APR from contract on network/wallet change
- const queryKeys = [ldyBalanceQuery, [STAKING_APR_INFO_QUERY]];
+ // Refetch LdyBalance & RewardPerToken from contract on network/wallet change
+ const queryKeys = [ldyBalanceQuery, rewardPerTokenQuery];
useEffect(() => {
queryKeys.forEach((k) => queryClient.invalidateQueries({ queryKey: k }));
}, [account.address, publicClient]);
- // Refetch stakingAPR on ldyBalance change.
+ // Refetch rewardPerToken on ldyBalance change.
useEffect(() => {
- // Refetch after 3 seconds due to subgraph latency
- const timeoutId = setTimeout(() => {
- queryClient.invalidateQueries({ queryKey: [STAKING_APR_INFO_QUERY] });
- }, 3000);
- return () => clearTimeout(timeoutId);
+ queryClient.invalidateQueries({ queryKey: rewardPerTokenQuery });
}, [ldyBalance]);
return (
@@ -58,7 +52,7 @@ export const AppStaking: FC = () => {
ldyTokenAddress={ldyTokenAddress || zeroAddress}
ldyTokenBalance={ldyBalance || 0n}
ldyTokenDecimals={ldyDecimals || 18}
- stakingAprInfo={stakingAprInfo ? stakingAprInfo.stakingAPRInfo || undefined : undefined}
+ rewardPerToken={rewardPerToken || 0n}
/>
{
>
diff --git a/src/components/app/staking/AppStakingPane.tsx b/src/components/app/staking/AppStakingPane.tsx
index c9872e4d..a7c17505 100644
--- a/src/components/app/staking/AppStakingPane.tsx
+++ b/src/components/app/staking/AppStakingPane.tsx
@@ -7,20 +7,19 @@ import { useSimulateLdyStakingStake } from "@/generated";
import * as Slider from "@radix-ui/react-slider";
import { StakeDurations } from "@/constants/staking";
import { getAPYCalculation } from "@/lib/getAPYCalculation";
-import { IStakingAPRInfo } from "@/services/graph/hooks/useStakingEvent";
export const AppStakingPane: FC<{
ldyTokenSymbol: string;
ldyTokenAddress: Address;
ldyTokenBalance: bigint;
ldyTokenDecimals: number;
- stakingAprInfo: IStakingAPRInfo | undefined;
+ rewardPerToken: bigint;
}> = ({
ldyTokenSymbol = "LDY",
ldyTokenAddress,
ldyTokenBalance,
ldyTokenDecimals,
- stakingAprInfo,
+ rewardPerToken,
}) => {
const ldyStakingAddress = useContractAddress("LDYStaking");
@@ -42,9 +41,10 @@ export const AppStakingPane: FC<{
// Calculate APY based on stakeIndex and stakingAprInfo.
const APY = useMemo(() => {
return (
- getAPYCalculation(stakingAprInfo ? stakingAprInfo.APR : "0", true, stakeOptionIndex) + "%"
+ getAPYCalculation(formatUnits(rewardPerToken, ldyTokenDecimals!), true, stakeOptionIndex) +
+ "%"
);
- }, [stakeOptionIndex, stakingAprInfo]);
+ }, [stakeOptionIndex, rewardPerToken]);
const preparation = useSimulateLdyStakingStake({
args: [depositedAmount, stakeOptionIndex],
diff --git a/src/components/app/staking/AppStakingPoolPane.tsx b/src/components/app/staking/AppStakingPoolPane.tsx
index 0417343e..2e00f9d4 100644
--- a/src/components/app/staking/AppStakingPoolPane.tsx
+++ b/src/components/app/staking/AppStakingPoolPane.tsx
@@ -11,7 +11,7 @@ import utc from "dayjs/plugin/utc";
import { OneMonth } from "@/constants/staking";
import { getAPYCalculation } from "@/lib/getAPYCalculation";
import { QueryKey } from "@tanstack/react-query";
-import { IStakingAPRInfo, IUserStakingInfo } from "@/services/graph/hooks/useStakingEvent";
+import { IUserStakingInfo } from "@/services/graph/hooks/useStakingEvent";
dayjs.extend(localizedFormat);
dayjs.extend(relativeTime);
dayjs.extend(utc);
@@ -30,7 +30,7 @@ export const AppStakingPoolPane: FC<{
ldyTokenDecimals: number;
userStakingInfo: IUserStakingInfo | undefined;
rewardsArray: readonly bigint[] | undefined;
- stakingAprInfo: IStakingAPRInfo | undefined;
+ rewardPerToken: bigint;
getUserStakesQuery?: QueryKey;
ldyTokenBalanceQuery?: QueryKey;
rewardsArrayQuery?: QueryKey;
@@ -40,7 +40,7 @@ export const AppStakingPoolPane: FC<{
ldyTokenDecimals,
userStakingInfo,
rewardsArray,
- stakingAprInfo,
+ rewardPerToken,
getUserStakesQuery,
ldyTokenBalanceQuery,
rewardsArrayQuery,
@@ -80,7 +80,7 @@ export const AppStakingPoolPane: FC<{
APY
{getAPYCalculation(
- stakingAprInfo ? stakingAprInfo.APR : "0",
+ formatUnits(rewardPerToken, ldyTokenDecimals!),
false,
Number(poolInfo.duration),
)}
diff --git a/src/components/app/staking/AppStakingPools.tsx b/src/components/app/staking/AppStakingPools.tsx
index 6926e197..0ef39826 100644
--- a/src/components/app/staking/AppStakingPools.tsx
+++ b/src/components/app/staking/AppStakingPools.tsx
@@ -8,19 +8,19 @@ import {
} from "@/components/ui/Carousel";
import { useGetUserStakingsByAddress } from "@/services/graph";
import { useAccount, usePublicClient } from "wagmi";
-import { zeroAddress } from "viem";
+import { formatUnits, zeroAddress } from "viem";
import { useReadLdyStakingGetEarnedUser, useReadLdyStakingGetUserStakes } from "@/generated";
import { QueryKey, useQueryClient } from "@tanstack/react-query";
import { twMerge } from "tailwind-merge";
import { USER_STAKING_QUERY } from "@/services/graph/queries";
-import { IStakingAPRInfo } from "@/services/graph/hooks/useStakingEvent";
import { AppStakingPoolPane } from "./AppStakingPoolPane";
export const AppStakingPools: FC<{
ldyTokenDecimals: number;
+ ldyTokenBalance: bigint;
ldyTokenBalanceQuery: QueryKey;
- stakingAprInfo: IStakingAPRInfo | undefined;
-}> = ({ ldyTokenDecimals, ldyTokenBalanceQuery, stakingAprInfo }) => {
+ rewardPerToken: bigint;
+}> = ({ ldyTokenDecimals, ldyTokenBalance, ldyTokenBalanceQuery, rewardPerToken }) => {
const queryClient = useQueryClient();
const account = useAccount();
const publicClient = usePublicClient();
@@ -42,11 +42,11 @@ export const AppStakingPools: FC<{
args: [account.address || zeroAddress],
});
- // Refetch staking info, earned array from subgraph & contracts on wallet, network change
- const queryKeys = [rewardsArrayQuery, getUserStakesQuery, [USER_STAKING_QUERY]];
+ // Refetch staking info, earned array from contracts on wallet, network change
+ const queryKeys = [rewardsArrayQuery, getUserStakesQuery];
useEffect(() => {
queryKeys.forEach((k) => queryClient.invalidateQueries({ queryKey: k }));
- }, [account.address, publicClient]);
+ }, [account.address, publicClient, ldyTokenBalance]);
// Refetch staking info(earned info) on rewardsArray change
useEffect(() => {
@@ -82,7 +82,8 @@ export const AppStakingPools: FC<{
: undefined
}
rewardsArray={rewardsArray ? rewardsArray : undefined}
- stakingAprInfo={stakingAprInfo}
+ // stakingAprInfo={stakingAprInfo}
+ rewardPerToken={rewardPerToken}
getUserStakesQuery={getUserStakesQuery}
ldyTokenBalanceQuery={ldyTokenBalanceQuery}
rewardsArrayQuery={rewardsArrayQuery}
diff --git a/src/lib/getAPYCalculation.ts b/src/lib/getAPYCalculation.ts
index 5807ea2c..bcb0fbff 100644
--- a/src/lib/getAPYCalculation.ts
+++ b/src/lib/getAPYCalculation.ts
@@ -1,17 +1,23 @@
import { StakeDurations, OneMonth } from "@/constants/staking";
export const getAPYCalculation = (
- apr: string,
+ interestRate: string,
useStakeIndex: boolean = true,
stakeDuration: number,
) => {
- // -------- APY Formula ----------- //
- // APY(%) = (((1 + r/n )^n) – 1)*100
- // r: APR(annual interest rate)
- // n: Number of compound periods
+ // -------- APR and APY Formula ----------- //
+ // R: Interest rate(reward per token)
+ // APR(%) = R * stakeDuration(ie. 1 month in sec) / 365 days(in sec) * 100
+ // N: Number of compounds
+ // APY(%) = (((1 + R/N )^N) – 1)*100
- const N = useStakeIndex ? StakeDurations[stakeDuration] * OneMonth : stakeDuration;
- const R = Number(apr);
+ const OneYear = 12 * OneMonth;
+ const Duration = useStakeIndex ? StakeDurations[stakeDuration] * OneMonth : stakeDuration;
+
+ const R = Number(interestRate);
+ const APR = (((R * Duration) / OneYear) * 100).toFixed(2);
+ console.log("APR: ", APR);
+ const N = Duration / OneYear;
const APY = (Math.pow(1 + R / N, N) - 1) * 100;
return APY.toFixed(2);
};
diff --git a/src/services/graph/hooks/useStakingEvent/index.ts b/src/services/graph/hooks/useStakingEvent/index.ts
index 538242c5..4173b602 100644
--- a/src/services/graph/hooks/useStakingEvent/index.ts
+++ b/src/services/graph/hooks/useStakingEvent/index.ts
@@ -13,7 +13,7 @@ export interface IUserStakingInfo {
export interface IStakingAPRInfo {
rewardPerSec: string;
totalStaked: string;
- APR: string;
+ interestRate: string;
}
export const useGetUserStakingsByAddress = (
diff --git a/src/services/graph/queries/staking.ts b/src/services/graph/queries/staking.ts
index 03d64d68..ccfc9bf9 100644
--- a/src/services/graph/queries/staking.ts
+++ b/src/services/graph/queries/staking.ts
@@ -17,7 +17,7 @@ export const STAKING_APR_INFO_QUERY = gql`
stakingAPRInfo(id: $stakeAprId) {
rewardPerSec
totalStaked
- APR
+ interestRate
id
}
}
diff --git a/subgraph/build/LTokenSignaler/LTokenSignaler.wasm b/subgraph/build/LTokenSignaler/LTokenSignaler.wasm
index 8c9c52ff..1e79b458 100644
Binary files a/subgraph/build/LTokenSignaler/LTokenSignaler.wasm and b/subgraph/build/LTokenSignaler/LTokenSignaler.wasm differ
diff --git a/subgraph/build/schema.graphql b/subgraph/build/schema.graphql
index 71284343..50df301e 100644
--- a/subgraph/build/schema.graphql
+++ b/subgraph/build/schema.graphql
@@ -75,5 +75,5 @@ type StakingAPRInfo @entity {
id: ID! # Static ID, "StakingAPRInfo"
rewardPerSec: BigInt!
totalStaked: BigInt!
- APR: BigDecimal!
+ interestRate: BigDecimal! # rewardPerSec/totalStaked
}
diff --git a/subgraph/generated/schema.ts b/subgraph/generated/schema.ts
index a1206136..cbd3868b 100644
--- a/subgraph/generated/schema.ts
+++ b/subgraph/generated/schema.ts
@@ -773,8 +773,8 @@ export class StakingAPRInfo extends Entity {
this.set("totalStaked", Value.fromBigInt(value));
}
- get APR(): BigDecimal {
- let value = this.get("APR");
+ get interestRate(): BigDecimal {
+ let value = this.get("interestRate");
if (!value || value.kind == ValueKind.NULL) {
throw new Error("Cannot return null for a required field.");
} else {
@@ -782,8 +782,8 @@ export class StakingAPRInfo extends Entity {
}
}
- set APR(value: BigDecimal) {
- this.set("APR", Value.fromBigDecimal(value));
+ set interestRate(value: BigDecimal) {
+ this.set("interestRate", Value.fromBigDecimal(value));
}
}
diff --git a/subgraph/helper.ts b/subgraph/helper.ts
index 0f5033fe..c9fb612e 100644
--- a/subgraph/helper.ts
+++ b/subgraph/helper.ts
@@ -2,7 +2,10 @@ import { BigDecimal, BigInt } from "@graphprotocol/graph-ts";
import { StakingAPRInfo } from "./generated/schema";
export const STAKING_APR_INFO_ID = "STAKING_APR_INFO_ID";
-export const SECONDS_PER_YEAR = BigInt.fromI32(365 * 24 * 60 * 60);
+
+export function bigDecimalExp18(): BigDecimal {
+ return BigDecimal.fromString("1000000000000000000");
+}
export function getOrCreateStakingAPRInfo(): StakingAPRInfo {
let stakingAprInfo = StakingAPRInfo.load(STAKING_APR_INFO_ID);
@@ -10,7 +13,7 @@ export function getOrCreateStakingAPRInfo(): StakingAPRInfo {
stakingAprInfo = new StakingAPRInfo(STAKING_APR_INFO_ID);
stakingAprInfo.rewardPerSec = BigInt.fromI32(0);
stakingAprInfo.totalStaked = BigInt.fromI32(0);
- stakingAprInfo.APR = BigDecimal.fromString("0");
+ stakingAprInfo.interestRate = BigDecimal.fromString("0");
}
return stakingAprInfo;
}
@@ -18,11 +21,11 @@ export function getOrCreateStakingAPRInfo(): StakingAPRInfo {
export function updateStakingAPRInfo(): void {
const stakingAPRInfo = getOrCreateStakingAPRInfo();
if (stakingAPRInfo.totalStaked == BigInt.fromI32(0)) {
- stakingAPRInfo.APR = BigDecimal.fromString("0");
+ stakingAPRInfo.interestRate = BigDecimal.fromString("0");
} else {
- stakingAPRInfo.APR = new BigDecimal(stakingAPRInfo.rewardPerSec)
- .times(new BigDecimal(SECONDS_PER_YEAR))
- .div(new BigDecimal(stakingAPRInfo.totalStaked));
+ stakingAPRInfo.interestRate = new BigDecimal(stakingAPRInfo.rewardPerSec)
+ .times(bigDecimalExp18())
+ .div(stakingAPRInfo.totalStaked.toBigDecimal());
}
stakingAPRInfo.save();
}
diff --git a/subgraph/mapping.ts b/subgraph/mapping.ts
index d5d9261c..bf14ee15 100644
--- a/subgraph/mapping.ts
+++ b/subgraph/mapping.ts
@@ -25,7 +25,7 @@ import {
import { Address, BigDecimal, BigInt } from "@graphprotocol/graph-ts";
import { LTokenSignalEvent } from "./generated/LTokenSignaler/LTokenSignaler";
import { store } from "@graphprotocol/graph-ts";
-import { SECONDS_PER_YEAR, getOrCreateStakingAPRInfo, updateStakingAPRInfo } from "./helper";
+import { getOrCreateStakingAPRInfo, updateStakingAPRInfo } from "./helper";
export function handleSignaledLToken(event: LTokenSignalEvent): void {
// Start indexing the signaled LToken
diff --git a/subgraph/schema.graphql b/subgraph/schema.graphql
index 71284343..50df301e 100644
--- a/subgraph/schema.graphql
+++ b/subgraph/schema.graphql
@@ -75,5 +75,5 @@ type StakingAPRInfo @entity {
id: ID! # Static ID, "StakingAPRInfo"
rewardPerSec: BigInt!
totalStaked: BigInt!
- APR: BigDecimal!
+ interestRate: BigDecimal! # rewardPerSec/totalStaked
}