diff --git a/src/hooks/__tests__/useFetchTvl.test.tsx b/src/hooks/__tests__/useFetchTvl.test.tsx index 85f2e4b18..047c85629 100644 --- a/src/hooks/__tests__/useFetchTvl.test.tsx +++ b/src/hooks/__tests__/useFetchTvl.test.tsx @@ -8,6 +8,8 @@ import { useMulticall, useMulticallContract, useTStakingContract, + useNuStakingEscrowContract, + useNuWorkLockContract, } from "../../web3/hooks" import { useETHData } from "../useETHData" import { useFetchTvl } from "../useFetchTvl" @@ -23,6 +25,8 @@ jest.mock("../../web3/hooks", () => ({ useMulticall: jest.fn(), useMulticallContract: jest.fn(), useTStakingContract: jest.fn(), + useNuStakingEscrowContract: jest.fn(), + useNuWorkLockContract: jest.fn(), })) jest.mock("../useETHData", () => ({ @@ -55,6 +59,8 @@ describe("Test `useFetchTvl` hook", () => { const mockedTStakingContract = { address: "0x2" } const mockedMultiCallContract = {} const mockedKeepAssetPoolContract = {} + const mockedNuWorkLockContrarct = { address: "0x3" } + const mockedNuStakingEscrowContract = { address: "0x4" } const wrapper = ({ children }) => ( { ;(useKeepTokenStakingContract as jest.Mock).mockReturnValue( mockedKeepTokenStakingContract ) + ;(useNuStakingEscrowContract as jest.Mock).mockReturnValue( + mockedNuStakingEscrowContract + ) + ;(useNuWorkLockContract as jest.Mock).mockReturnValue( + mockedNuWorkLockContrarct + ) }) test("should fetch tvl data correctly.", async () => { @@ -97,6 +109,16 @@ describe("Test `useFetchTvl` hook", () => { const coveragePoolTvl = { raw: "300000000000000000000", format: "300.0" } const keepStaking = { raw: "500000000000000000000", format: "500.0" } const tStaking = { raw: "600000000000000000000", format: "600.0" } + const nuTotalSupply = { raw: "7000000000000000000000", format: "7000.0" } + const nuCurrentPeriodSupply = { + raw: "800000000000000000000", + format: "800.0", + } + const nuInStakingEscrow = { + raw: "6500000000000000000000", + format: "6500.0", + } + const ethInWorkLock = { raw: "1000000000000000000000", format: "1000.0" } const multicallRequestResult = [ ethInKeepBonding.raw, @@ -104,6 +126,10 @@ describe("Test `useFetchTvl` hook", () => { coveragePoolTvl.raw, keepStaking.raw, tStaking.raw, + nuTotalSupply.raw, + nuCurrentPeriodSupply.raw, + nuInStakingEscrow.raw, + ethInWorkLock.raw, ] multicallRequest.mockResolvedValue(multicallRequestResult) @@ -112,12 +138,19 @@ describe("Test `useFetchTvl` hook", () => { const spyOnToUsdBalance = jest.spyOn(usdUtils, "toUsdBalance") const spyOnUseToken = jest.spyOn(useTokenModule, "useToken") + const haltedRewards = + Number(nuTotalSupply.format) - Number(nuCurrentPeriodSupply.format) + const stakedNU = Number(nuInStakingEscrow.format) - haltedRewards + const _expectedResult = { ecdsa: ethInKeepBonding.format * mockedETHData.usdPrice, tbtc: tbtcTokenTotalSupply.format * tbtcContext.usdConversion, keepCoveragePool: coveragePoolTvl.format * keepContext.usdConversion, keepStaking: keepStaking.format * keepContext.usdConversion, tStaking: tStaking.format * tContext.usdConversion, + nu: + stakedNU * nuContext.usdConversion + + Number(ethInWorkLock.format) * mockedETHData.usdPrice, } // `FixedNumber` from `@ethersproject/bignumber` adds trailing zero so we @@ -128,12 +161,14 @@ describe("Test `useFetchTvl` hook", () => { keepCoveragePool: `${_expectedResult.keepCoveragePool.toString()}.0`, keepStaking: `${_expectedResult.keepStaking.toString()}.0`, tStaking: `${_expectedResult.tStaking.toString()}.0`, + nu: `${_expectedResult.nu.toString()}.0`, total: `${ _expectedResult.ecdsa + _expectedResult.tbtc + _expectedResult.keepCoveragePool + _expectedResult.keepStaking + - _expectedResult.tStaking + _expectedResult.tStaking + + _expectedResult.nu }.0`, } @@ -147,6 +182,9 @@ describe("Test `useFetchTvl` hook", () => { expect(spyOnUseToken).toHaveBeenCalledWith(Token.Keep) expect(spyOnUseToken).toHaveBeenCalledWith(Token.TBTC) expect(spyOnUseToken).toHaveBeenCalledWith(Token.T) + expect(spyOnUseToken).toHaveBeenCalledWith(Token.Nu) + expect(useNuStakingEscrowContract).toHaveBeenCalled() + expect(useNuWorkLockContract).toHaveBeenCalled() expect(useKeepBondingContract).toHaveBeenCalled() expect(useMulticallContract).toHaveBeenCalled() expect(useKeepAssetPoolContract).toHaveBeenCalled() @@ -173,6 +211,24 @@ describe("Test `useFetchTvl` hook", () => { method: "balanceOf", args: [mockedTStakingContract.address], }, + { + contract: nuContext.contract, + method: "totalSupply", + }, + { + contract: mockedNuStakingEscrowContract, + method: "currentPeriodSupply", + }, + { + contract: nuContext.contract, + method: "balanceOf", + args: [mockedNuStakingEscrowContract.address], + }, + { + contract: mockedMultiCallContract, + method: "getEthBalance", + args: [mockedNuWorkLockContrarct.address], + }, ]) result.current[1]() @@ -183,36 +239,42 @@ describe("Test `useFetchTvl` hook", () => { expect(spyOnFormatUnits).toHaveBeenCalledTimes( multicallRequestResult.length ) - // The `toUsdBalance` function was called 2x times because it was called - // first on mount for every value and then after fetching on-chain data. - expect(spyOnToUsdBalance).toHaveBeenCalledTimes( - multicallRequestResult.length * 2 - ) + expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( - 6, + 8, ethInKeepBonding.format, mockedETHData.usdPrice ) expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( - 7, + 9, tbtcTokenTotalSupply.format, tbtcContext.usdConversion ) expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( - 8, + 10, coveragePoolTvl.format, keepContext.usdConversion ) expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( - 9, + 11, keepStaking.format, keepContext.usdConversion ) expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( - 10, + 12, tStaking.format, tContext.usdConversion ) + expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( + 13, + `${stakedNU.toString()}.0`, + nuContext.usdConversion + ) + expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( + 14, + ethInWorkLock.format, + mockedETHData.usdPrice + ) expect(result.current[0]).toEqual(expectedResult) }) diff --git a/src/hooks/useFetchTvl.ts b/src/hooks/useFetchTvl.ts index 02976acd7..6d741adf4 100644 --- a/src/hooks/useFetchTvl.ts +++ b/src/hooks/useFetchTvl.ts @@ -8,6 +8,8 @@ import { useKeepAssetPoolContract, useTStakingContract, useKeepTokenStakingContract, + useNuStakingEscrowContract, + useNuWorkLockContract, } from "../web3/hooks" import { useETHData } from "./useETHData" import { useToken } from "./useToken" @@ -20,6 +22,8 @@ interface TVLRawData { keepCoveragePoolTVL: string keepStakingTVL: string tStakingTVL: string + stakedNU: string + ethInNuNetwork: string // TODO: add PRE and NU TVL } @@ -38,6 +42,8 @@ const initialState = { keepCoveragePoolTVL: "0", keepStakingTVL: "0", tStakingTVL: "0", + stakedNU: "0", + ethInNuNetwork: "0", } export const useFetchTvl = (): [TVLData, () => Promise] => { @@ -48,12 +54,17 @@ export const useFetchTvl = (): [TVLData, () => Promise] => { keepCoveragePoolTVL, keepStakingTVL, tStakingTVL, + stakedNU, + ethInNuNetwork, } = rawData const eth = useETHData() const keep = useToken(Token.Keep) const tbtc = useToken(Token.TBTC) const t = useToken(Token.T) + const nu = useToken(Token.Nu) + const nuStakingEscrow = useNuStakingEscrowContract() + const nuWorkLock = useNuWorkLockContract() const keepBonding = useKeepBondingContract() const multicall = useMulticallContract() const keepAssetPool = useKeepAssetPoolContract() @@ -81,6 +92,24 @@ export const useFetchTvl = (): [TVLData, () => Promise] => { method: "balanceOf", args: [tTokenStaking?.address], }, + { + contract: nu.contract!, + method: "totalSupply", + }, + { + contract: nuStakingEscrow!, + method: "currentPeriodSupply", + }, + { + contract: nu.contract!, + method: "balanceOf", + args: [nuStakingEscrow?.address], + }, + { + contract: multicall!, + method: "getEthBalance", + args: [nuWorkLock?.address], + }, ]) const fetchTVLData = useCallback(async () => { @@ -93,14 +122,27 @@ export const useFetchTvl = (): [TVLData, () => Promise] => { coveragePoolTvl, keepStaking, tStaking, + nuTotalSupply, + nuCurrentPeriodSupply, + nuInEscrow, + ethInNuNetwork, ] = chainData.map((amount: string) => formatUnits(amount.toString())) + const haltedRewards = FixedNumber.fromString(nuTotalSupply).subUnsafe( + FixedNumber.fromString(nuCurrentPeriodSupply) + ) + const stakedNU = FixedNumber.fromString(nuInEscrow) + .subUnsafe(haltedRewards) + .toString() + const data: TVLRawData = { ecdsaTVL: ethInKeepBonding, tbtcTVL: tbtcTokenTotalSupply, keepCoveragePoolTVL: coveragePoolTvl, keepStakingTVL: keepStaking, tStakingTVL: tStaking, + stakedNU, + ethInNuNetwork, } setRawData(data) @@ -121,17 +163,23 @@ export const useFetchTvl = (): [TVLData, () => Promise] => { const tStaking = toUsdBalance(tStakingTVL, t.usdConversion) + const stakedNuInUSD = toUsdBalance(stakedNU, nu.usdConversion) + const ethInNu = toUsdBalance(ethInNuNetwork, eth.usdPrice) + const totalNu = stakedNuInUSD.addUnsafe(ethInNu) + return { ecdsa: ecdsa.toString(), tbtc: tbtcUSD.toString(), keepCoveragePool: keepCoveragePool.toString(), keepStaking: keepStaking.toString(), tStaking: tStaking.toString(), + nu: totalNu.toString(), total: ecdsa .addUnsafe(tbtcUSD) .addUnsafe(keepCoveragePool) .addUnsafe(keepStaking) .addUnsafe(tStaking) + .addUnsafe(totalNu) .toString(), } as TVLData }, [ @@ -144,6 +192,9 @@ export const useFetchTvl = (): [TVLData, () => Promise] => { keep.usdConversion, tbtc.usdConversion, t.usdConversion, + stakedNU, + ethInNuNetwork, + nu.usdConversion, ]) return [data, fetchTVLData] diff --git a/src/web3/hooks/index.ts b/src/web3/hooks/index.ts index 5ad9fac94..ef8505570 100644 --- a/src/web3/hooks/index.ts +++ b/src/web3/hooks/index.ts @@ -16,3 +16,5 @@ export * from "./useAssetPoolContract" export * from "./useTBTCTokenContract" export * from "./useTStakingContract" export * from "./useKeepTokenStakingContract" +export * from "./useNuStakingEscrowContract" +export * from "./useNuWorkLockContract" diff --git a/src/web3/hooks/useNuStakingEscrowContract.ts b/src/web3/hooks/useNuStakingEscrowContract.ts new file mode 100644 index 000000000..39ae69c31 --- /dev/null +++ b/src/web3/hooks/useNuStakingEscrowContract.ts @@ -0,0 +1,18 @@ +import { useContract } from "./useContract" + +// TODO: Get contract abi from the package and figure out how to use these +// contracts on ropsten and local network. This only works on mainnet. +const ESCROW_ABI = [ + { + inputs: [], + name: "currentPeriodSupply", + outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + stateMutability: "view", + type: "function", + }, +] +const ESCROW_ADDRESS = "0xbbD3C0C794F40c4f993B03F65343aCC6fcfCb2e2" + +export const useNuStakingEscrowContract = () => { + return useContract(ESCROW_ADDRESS, ESCROW_ABI) +} diff --git a/src/web3/hooks/useNuWorkLockContract.ts b/src/web3/hooks/useNuWorkLockContract.ts new file mode 100644 index 000000000..1b7c00bfe --- /dev/null +++ b/src/web3/hooks/useNuWorkLockContract.ts @@ -0,0 +1,24 @@ +import { useContract } from "./useContract" + +// TODO: Get contract abi from the package and figure out how to use these +// contracts on ropsten and local network. This only works on mainnet. +const WORK_LOCK_ADDRESS = "0xe9778E69a961e64d3cdBB34CF6778281d34667c2" +const ABI = [ + { + inputs: [], + name: "escrow", + outputs: [ + { + internalType: "contract StakingEscrow", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, +] + +export const useNuWorkLockContract = () => { + return useContract(WORK_LOCK_ADDRESS, ABI) +}