diff --git a/src/contexts/TokenContext.tsx b/src/contexts/TokenContext.tsx index 418826865..a9f91d647 100644 --- a/src/contexts/TokenContext.tsx +++ b/src/contexts/TokenContext.tsx @@ -1,5 +1,6 @@ -import React, { createContext, useEffect } from "react" +import React, { createContext } from "react" import { Contract } from "@ethersproject/contracts" +import { AddressZero } from "@ethersproject/constants" import { useWeb3React } from "@web3-react/core" import { useKeep } from "../web3/hooks/useKeep" import { useNu } from "../web3/hooks/useNu" @@ -49,7 +50,7 @@ export const TokenContextProvider: React.FC = ({ children }) => { const fetchBalances = useTokensBalanceCall( [keep.contract!, nu.contract!, t.contract!], - account! + active ? account! : AddressZero ) // diff --git a/src/hooks/__tests__/useFetchTvl.test.tsx b/src/hooks/__tests__/useFetchTvl.test.tsx new file mode 100644 index 000000000..85f2e4b18 --- /dev/null +++ b/src/hooks/__tests__/useFetchTvl.test.tsx @@ -0,0 +1,219 @@ +import { renderHook } from "@testing-library/react-hooks" +import * as ethersUnits from "@ethersproject/units" +import { Token } from "../../enums" +import { + useKeepAssetPoolContract, + useKeepBondingContract, + useKeepTokenStakingContract, + useMulticall, + useMulticallContract, + useTStakingContract, +} from "../../web3/hooks" +import { useETHData } from "../useETHData" +import { useFetchTvl } from "../useFetchTvl" +import * as useTokenModule from "../useToken" +import { TokenContext } from "../../contexts/TokenContext" +import * as usdUtils from "../../utils/getUsdBalance" + +jest.mock("../../web3/hooks", () => ({ + ...(jest.requireActual("../../web3/hooks") as {}), + useKeepAssetPoolContract: jest.fn(), + useKeepBondingContract: jest.fn(), + useKeepTokenStakingContract: jest.fn(), + useMulticall: jest.fn(), + useMulticallContract: jest.fn(), + useTStakingContract: jest.fn(), +})) + +jest.mock("../useETHData", () => ({ + ...(jest.requireActual("../useETHData") as {}), + useETHData: jest.fn(), +})) + +describe("Test `useFetchTvl` hook", () => { + const keepContext = { + contract: {} as any, + usdConversion: 1, + } as any + const tbtcContext = { + contract: {} as any, + usdConversion: 2, + } as any + + const tContext = { + contract: {} as any, + usdConversion: 3, + } as any + + const nuContext = { + contract: {} as any, + usdConversion: 4, + } as any + + const mockedKeepTokenStakingContract = { address: "0x1" } + const mockedKeepBondingContract = { address: "0x0" } + const mockedTStakingContract = { address: "0x2" } + const mockedMultiCallContract = {} + const mockedKeepAssetPoolContract = {} + + const wrapper = ({ children }) => ( + + {children} + + ) + + const multicallRequest = jest.fn() + const mockedETHData = { usdPrice: 20 } + + beforeEach(() => { + ;(useMulticall as jest.Mock).mockReturnValue(multicallRequest) + ;(useETHData as jest.Mock).mockReturnValue(mockedETHData) + ;(useKeepBondingContract as jest.Mock).mockReturnValue( + mockedKeepBondingContract + ) + ;(useMulticallContract as jest.Mock).mockReturnValue( + mockedMultiCallContract + ) + ;(useKeepAssetPoolContract as jest.Mock).mockReturnValue( + mockedKeepAssetPoolContract + ) + ;(useTStakingContract as jest.Mock).mockReturnValue(mockedTStakingContract) + ;(useKeepTokenStakingContract as jest.Mock).mockReturnValue( + mockedKeepTokenStakingContract + ) + }) + + test("should fetch tvl data correctly.", async () => { + // given + const ethInKeepBonding = { raw: "10000000000000000000", format: "10.0" } + const tbtcTokenTotalSupply = { raw: "5000000000000000000", format: "5.0" } + const coveragePoolTvl = { raw: "300000000000000000000", format: "300.0" } + const keepStaking = { raw: "500000000000000000000", format: "500.0" } + const tStaking = { raw: "600000000000000000000", format: "600.0" } + + const multicallRequestResult = [ + ethInKeepBonding.raw, + tbtcTokenTotalSupply.raw, + coveragePoolTvl.raw, + keepStaking.raw, + tStaking.raw, + ] + + multicallRequest.mockResolvedValue(multicallRequestResult) + + const spyOnFormatUnits = jest.spyOn(ethersUnits, "formatUnits") + const spyOnToUsdBalance = jest.spyOn(usdUtils, "toUsdBalance") + const spyOnUseToken = jest.spyOn(useTokenModule, "useToken") + + 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, + } + + // `FixedNumber` from `@ethersproject/bignumber` adds trailing zero so we + // need to do the same here. + const expectedResult = { + ecdsa: `${_expectedResult.ecdsa.toString()}.0`, + tbtc: `${_expectedResult.tbtc.toString()}.0`, + keepCoveragePool: `${_expectedResult.keepCoveragePool.toString()}.0`, + keepStaking: `${_expectedResult.keepStaking.toString()}.0`, + tStaking: `${_expectedResult.tStaking.toString()}.0`, + total: `${ + _expectedResult.ecdsa + + _expectedResult.tbtc + + _expectedResult.keepCoveragePool + + _expectedResult.keepStaking + + _expectedResult.tStaking + }.0`, + } + + // when + const { result, waitForNextUpdate } = renderHook(() => useFetchTvl(), { + wrapper, + }) + + // then + expect(useETHData).toHaveBeenCalled() + expect(spyOnUseToken).toHaveBeenCalledWith(Token.Keep) + expect(spyOnUseToken).toHaveBeenCalledWith(Token.TBTC) + expect(spyOnUseToken).toHaveBeenCalledWith(Token.T) + expect(useKeepBondingContract).toHaveBeenCalled() + expect(useMulticallContract).toHaveBeenCalled() + expect(useKeepAssetPoolContract).toHaveBeenCalled() + expect(useTStakingContract).toHaveBeenCalled() + expect(useKeepTokenStakingContract).toHaveBeenCalled() + expect(useMulticall).toHaveBeenCalledWith([ + { + contract: mockedMultiCallContract, + method: "getEthBalance", + args: [mockedKeepBondingContract.address], + }, + { + contract: tbtcContext.contract, + method: "totalSupply", + }, + { contract: mockedKeepAssetPoolContract, method: "totalValue" }, + { + contract: keepContext.contract, + method: "balanceOf", + args: [mockedKeepTokenStakingContract.address], + }, + { + contract: tContext.contract, + method: "balanceOf", + args: [mockedTStakingContract.address], + }, + ]) + + result.current[1]() + + await waitForNextUpdate() + + expect(multicallRequest).toHaveBeenCalled() + 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, + ethInKeepBonding.format, + mockedETHData.usdPrice + ) + expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( + 7, + tbtcTokenTotalSupply.format, + tbtcContext.usdConversion + ) + expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( + 8, + coveragePoolTvl.format, + keepContext.usdConversion + ) + expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( + 9, + keepStaking.format, + keepContext.usdConversion + ) + expect(spyOnToUsdBalance).toHaveBeenNthCalledWith( + 10, + tStaking.format, + tContext.usdConversion + ) + + expect(result.current[0]).toEqual(expectedResult) + }) +}) diff --git a/src/hooks/useFetchTvl.ts b/src/hooks/useFetchTvl.ts index c5a69fb3d..02976acd7 100644 --- a/src/hooks/useFetchTvl.ts +++ b/src/hooks/useFetchTvl.ts @@ -6,41 +6,59 @@ import { useMulticall, useMulticallContract, useKeepAssetPoolContract, + useTStakingContract, + useKeepTokenStakingContract, } from "../web3/hooks" import { useETHData } from "./useETHData" import { useToken } from "./useToken" import { Token } from "../enums" +import { toUsdBalance } from "../utils/getUsdBalance" interface TVLRawData { ecdsaTVL: string tbtcTVL: string keepCoveragePoolTVL: string - // TODO: add PRE + keepStakingTVL: string + tStakingTVL: string + // TODO: add PRE and NU TVL +} + +interface TVLData { + ecdsa: string + tbtc: string + keepCoveragePool: string + keepStaking: string + tStaking: string + total: string } const initialState = { ecdsaTVL: "0", tbtcTVL: "0", keepCoveragePoolTVL: "0", + keepStakingTVL: "0", + tStakingTVL: "0", } -export const useFetchTvl = (): [ - { - ecdsa: string - tbtc: string - keepCoveragePool: string - total: string - }, - () => Promise -] => { +export const useFetchTvl = (): [TVLData, () => Promise] => { const [rawData, setRawData] = useState(initialState) - const { ecdsaTVL, tbtcTVL, keepCoveragePoolTVL } = rawData + const { + ecdsaTVL, + tbtcTVL, + keepCoveragePoolTVL, + keepStakingTVL, + tStakingTVL, + } = rawData + const eth = useETHData() const keep = useToken(Token.Keep) const tbtc = useToken(Token.TBTC) + const t = useToken(Token.T) const keepBonding = useKeepBondingContract() const multicall = useMulticallContract() const keepAssetPool = useKeepAssetPoolContract() + const tTokenStaking = useTStakingContract() + const keepTokenStaking = useKeepTokenStakingContract() const fetchOnChainData = useMulticall([ { @@ -53,19 +71,36 @@ export const useFetchTvl = (): [ method: "totalSupply", }, { contract: keepAssetPool!, method: "totalValue" }, + { + contract: keep.contract!, + method: "balanceOf", + args: [keepTokenStaking?.address], + }, + { + contract: t.contract!, + method: "balanceOf", + args: [tTokenStaking?.address], + }, ]) const fetchTVLData = useCallback(async () => { const chainData = await fetchOnChainData() if (chainData.length === 0) return initialState - const [ethInKeepBonding, tbtcTokenTotalSupply, coveragePoolTvl] = - chainData.map((amount: string) => formatUnits(amount.toString())) + const [ + ethInKeepBonding, + tbtcTokenTotalSupply, + coveragePoolTvl, + keepStaking, + tStaking, + ] = chainData.map((amount: string) => formatUnits(amount.toString())) - const data = { + const data: TVLRawData = { ecdsaTVL: ethInKeepBonding, tbtcTVL: tbtcTokenTotalSupply, keepCoveragePoolTVL: coveragePoolTvl, + keepStakingTVL: keepStaking, + tStakingTVL: tStaking, } setRawData(data) @@ -73,31 +108,42 @@ export const useFetchTvl = (): [ }, [fetchOnChainData]) const data = useMemo(() => { - const ecdsa = FixedNumber.fromString(ecdsaTVL).mulUnsafe( - FixedNumber.fromString(eth.usdPrice.toString()) - ) + const ecdsa = toUsdBalance(ecdsaTVL, eth.usdPrice) - const tbtcUSD = FixedNumber.fromString(tbtcTVL).mulUnsafe( - FixedNumber.fromString(tbtc.usdConversion.toString()) + const tbtcUSD = toUsdBalance(tbtcTVL, tbtc.usdConversion) + + const keepCoveragePool = toUsdBalance( + keepCoveragePoolTVL, + keep.usdConversion ) - const keepCoveragePool = FixedNumber.fromString( - keepCoveragePoolTVL - ).mulUnsafe(FixedNumber.fromString(keep.usdConversion.toString())) + const keepStaking = toUsdBalance(keepStakingTVL, keep.usdConversion) + + const tStaking = toUsdBalance(tStakingTVL, t.usdConversion) return { ecdsa: ecdsa.toString(), tbtc: tbtcUSD.toString(), keepCoveragePool: keepCoveragePool.toString(), - total: ecdsa.addUnsafe(tbtcUSD).addUnsafe(keepCoveragePool).toString(), - } + keepStaking: keepStaking.toString(), + tStaking: tStaking.toString(), + total: ecdsa + .addUnsafe(tbtcUSD) + .addUnsafe(keepCoveragePool) + .addUnsafe(keepStaking) + .addUnsafe(tStaking) + .toString(), + } as TVLData }, [ ecdsaTVL, keepCoveragePoolTVL, tbtcTVL, + keepStakingTVL, + tStakingTVL, eth.usdPrice, keep.usdConversion, tbtc.usdConversion, + t.usdConversion, ]) return [data, fetchTVLData] diff --git a/src/hooks/useTokenBalance.ts b/src/hooks/useTokenBalance.ts index ebb61bfce..d4a4efdef 100644 --- a/src/hooks/useTokenBalance.ts +++ b/src/hooks/useTokenBalance.ts @@ -1,19 +1,9 @@ import { useMemo } from "react" import { Token } from "../enums/token" -import { useTokenState } from "./useTokenState" +import { useToken } from "./useToken" export const useTokenBalance = (token: Token) => { - const tokenMap = { - [Token.T]: "t", - [Token.Nu]: "nu", - [Token.Keep]: "keep", - [Token.TBTC]: "tbtc", - } - - const tokenState = useTokenState() - - // @ts-ignore - const _token = tokenState[tokenMap[token]] + const _token = useToken(token) return useMemo(() => _token.balance, [_token.balance]) } diff --git a/src/pages/Overview/Network/TotalValueLocked.tsx b/src/pages/Overview/Network/TotalValueLocked.tsx index 8ac772521..8395d393c 100644 --- a/src/pages/Overview/Network/TotalValueLocked.tsx +++ b/src/pages/Overview/Network/TotalValueLocked.tsx @@ -3,23 +3,17 @@ import { Flex } from "@chakra-ui/react" import CardTemplate from "./CardTemplate" import { H1 } from "../../../components/Typography" import { formatFiatCurrencyAmount } from "../../../utils/formatAmount" -import { useWeb3React } from "@web3-react/core" const TotalValueLocked: FC<{ totalValueLocked: number | string }> = ({ totalValueLocked, }) => { const tvl = formatFiatCurrencyAmount(totalValueLocked) - const { account } = useWeb3React() - /* TODO: This is a hack - we need a way to load on-chain data without a connected wallet */ return ( -

- {account ? tvl : "Please connect your wallet"} +

+ {tvl}

diff --git a/src/utils/getUsdBalance.tsx b/src/utils/getUsdBalance.ts similarity index 52% rename from src/utils/getUsdBalance.tsx rename to src/utils/getUsdBalance.ts index c8b4a485b..e791828b3 100644 --- a/src/utils/getUsdBalance.tsx +++ b/src/utils/getUsdBalance.ts @@ -7,9 +7,16 @@ const getUsdBalance = ( usdConversion: number ): string => { return formatFiatCurrencyAmount( - FixedNumber.fromString(usdConversion.toString()) - .mulUnsafe(FixedNumber.fromString(formatUnits(balance))) - .toString() + toUsdBalance(formatUnits(balance), usdConversion).toString() + ) +} + +export const toUsdBalance = ( + balance: string | number, + usdConversion: number +): FixedNumber => { + return FixedNumber.fromString(usdConversion.toString()).mulUnsafe( + FixedNumber.fromString(balance.toString()) ) } diff --git a/src/web3/hooks/__tests__/useContract.test.ts b/src/web3/hooks/__tests__/useContract.test.ts index e9d37d25f..41e202d44 100644 --- a/src/web3/hooks/__tests__/useContract.test.ts +++ b/src/web3/hooks/__tests__/useContract.test.ts @@ -1,22 +1,38 @@ import { renderHook } from "@testing-library/react-hooks" import { useWeb3React } from "@web3-react/core" +import { JsonRpcProvider } from "@ethersproject/providers" import { useContract } from "../useContract" import { getContract } from "../../../utils/getContract" +import { getEnvVariable } from "../../../utils/getEnvVariable" +import { EnvVariable } from "../../../enums" jest.mock("../../../utils/getContract", () => ({ ...(jest.requireActual("../../../utils/getContract") as {}), getContract: jest.fn(() => {}), })) +jest.mock("../../../utils/getEnvVariable", () => ({ + ...(jest.requireActual("../../../utils/getEnvVariable") as {}), + getEnvVariable: jest.fn(), +})) + jest.mock("@web3-react/core", () => ({ ...(jest.requireActual("@web3-react/core") as {}), useWeb3React: jest.fn(), })) +jest.mock("@ethersproject/providers", () => ({ + ...(jest.requireActual("@ethersproject/providers") as {}), + JsonRpcProvider: jest.fn(), +})) + describe("Test the `useContract` hook", () => { const address = "0x3aA3D0Bb15FAdDB154141c92DAaaA9022b2A346d" const abi = ["function balanceOf(address owner) view returns (uint256)"] const mockedLibrary = {} + const mockedJsonRpcProvider = {} + const mockedEthNodeUrl = "http://localhost:8545" + const mockedContract = {} describe("when web3 react context is not active", () => { beforeEach(() => { @@ -24,6 +40,9 @@ describe("Test the `useContract` hook", () => { chainId: null, library: null, }) + // @ts-ignore + ;(JsonRpcProvider as jest.Mock).mockReturnValue(mockedJsonRpcProvider) + ;(getEnvVariable as jest.Mock).mockReturnValue(mockedEthNodeUrl) }) test("should not create a contract instance if an address is not defined", () => { @@ -38,20 +57,24 @@ describe("Test the `useContract` hook", () => { expect(result.current).toBeNull() }) - test("should not create a contract instance if web3 react provider is not active", () => { + test("should create a contract instance with a default provider if web3 react provider is not active", () => { + ;(getContract as jest.Mock).mockReturnValue(mockedContract) + const { result } = renderHook(() => useContract(address, abi)) - expect(result.current).toBeNull() + expect(getEnvVariable).toHaveBeenCalledWith(EnvVariable.ETH_HOSTNAME_HTTP) + expect(JsonRpcProvider).toHaveBeenCalledWith(mockedEthNodeUrl) + expect(result.current).toEqual(mockedContract) }) }) describe("when web3 context provider is active", () => { - const mockedContract = {} const account = "0x086813525A7dC7dafFf015Cdf03896Fd276eab60" beforeEach(() => { ;(useWeb3React as jest.Mock).mockReturnValue({ account, + active: true, chainId: 1, library: mockedLibrary, }) diff --git a/src/web3/hooks/__tests__/useSendTransaction.test.ts b/src/web3/hooks/__tests__/useSendTransaction.test.ts index 5108210d0..e77b5b221 100644 --- a/src/web3/hooks/__tests__/useSendTransaction.test.ts +++ b/src/web3/hooks/__tests__/useSendTransaction.test.ts @@ -46,6 +46,9 @@ describe("Test `useSendTransaction` hook", () => { const userRejectedErrMsg = "MetaMask Tx Signature: User denied transaction signature." + const mockedOnSuccessCallback = jest.fn() + const mockedOnErroCallback = jest.fn() + beforeEach(() => { ;(useWeb3React as jest.Mock).mockReturnValue({ chainId: 1, @@ -84,6 +87,21 @@ describe("Test `useSendTransaction` hook", () => { ) }) + test("should proceed the transaction correctly and call custom on success callback", async () => { + mockedContract[methodName].mockResolvedValue(mockedTx) + + const { result, waitForNextUpdate } = renderHook(() => + useSendTransaction(mockedContract, methodName, mockedOnSuccessCallback) + ) + + expect(result.current.status).toEqual(TransactionStatus.Idle) + + result.current.sendTransaction(from, value) + await waitForNextUpdate() + + expect(mockedOnSuccessCallback).toHaveBeenCalledWith(mockedTx) + }) + test("should do nothing if there is no signer", async () => { ;(useWeb3React as jest.Mock).mockReturnValue({ chainId: 1, @@ -130,6 +148,30 @@ describe("Test `useSendTransaction` hook", () => { expect(mockedOpenModalFn).not.toHaveBeenCalled() }) + test("should call a custom error callback", async () => { + const error = new Error() + mockedContract[methodName].mockRejectedValue(error) + + const { result, waitForNextUpdate } = renderHook(() => + useSendTransaction( + mockedContract, + methodName, + mockedOnSuccessCallback, + mockedOnErroCallback + ) + ) + + expect(result.current.status).toEqual(TransactionStatus.Idle) + + result.current.sendTransaction(from, value) + await waitForNextUpdate() + expect(mockedOnSuccessCallback).not.toHaveBeenCalled() + expect(mockedOnErroCallback).toHaveBeenCalledWith(error) + expect(mockedOpenModalFn).not.toHaveBeenCalledWith( + ModalType.TransactionFailed + ) + }) + test.each` errorMsg | expectedStatus ${"Unexpected error"} | ${TransactionStatus.Failed} @@ -156,7 +198,7 @@ describe("Test `useSendTransaction` hook", () => { expect(result.current.status).toEqual(expectedStatus) expect(mockedOpenModalFn).toHaveBeenCalledWith( ModalType.TransactionFailed, - { error, transactionHash: undefined } + { error, transactionHash: undefined, isExpandableError: true } ) } ) diff --git a/src/web3/hooks/__tests__/useSubscribeToContractEvent.test.ts b/src/web3/hooks/__tests__/useSubscribeToContractEvent.test.ts index ab78a30ad..1e683f1b7 100644 --- a/src/web3/hooks/__tests__/useSubscribeToContractEvent.test.ts +++ b/src/web3/hooks/__tests__/useSubscribeToContractEvent.test.ts @@ -1,7 +1,13 @@ import { act, renderHook } from "@testing-library/react-hooks" +import { useWeb3React } from "@web3-react/core" import { useSubscribeToContractEvent } from "../useSubscribeToContractEvent" import { EventEmitter } from "events" +jest.mock("@web3-react/core", () => ({ + ...(jest.requireActual("@web3-react/core") as {}), + useWeb3React: jest.fn(), +})) + describe("Test `useSubscribeToContractEvent` hook", () => { const eventName = "Transfer" const from = "0x407C3329eA8f6BEFB984D97AE4Fa71945E43170b" @@ -27,6 +33,10 @@ describe("Test `useSubscribeToContractEvent` hook", () => { } beforeEach(() => { + ;(useWeb3React as jest.Mock).mockReturnValue({ + active: true, + }) + // Clear listeners contractEventEmitter.removeAllListeners() providerEventEmitter.removeAllListeners() @@ -59,6 +69,23 @@ describe("Test `useSubscribeToContractEvent` hook", () => { expect(mockedContract.provider.off).not.toHaveBeenCalled() }) + test("should do nothing if a user is not connected to a wallet", () => { + ;(useWeb3React as jest.Mock).mockReturnValue({ + active: false, + }) + + const { unmount } = renderHook(() => + useSubscribeToContractEvent(mockedContract, eventName, mockedCallback) + ) + + expect(mockedCallback).not.toHaveBeenCalled() + expect(mockedContract.on).not.toHaveBeenCalled() + expect(mockedContract.provider.once).not.toHaveBeenCalled() + unmount() + expect(mockedContract.off).not.toHaveBeenCalled() + expect(mockedContract.provider.off).not.toHaveBeenCalled() + }) + test("should off subscription correctly", () => { const { unmount } = renderHook(() => useSubscribeToContractEvent(mockedContract, eventName, mockedCallback) diff --git a/src/web3/hooks/__tests__/useSubscribeToERC20TransferEvent.test.ts b/src/web3/hooks/__tests__/useSubscribeToERC20TransferEvent.test.ts index 21bb88b70..48daab228 100644 --- a/src/web3/hooks/__tests__/useSubscribeToERC20TransferEvent.test.ts +++ b/src/web3/hooks/__tests__/useSubscribeToERC20TransferEvent.test.ts @@ -6,7 +6,7 @@ import { EventEmitter } from "events" import { Token } from "../../../enums" import { useToken } from "../../../hooks/useToken" import { useTokenBalance } from "../../../hooks/useTokenBalance" -import { useReduxToken } from "../../../hooks/useReduxToken" +import { useTokenState } from "../../../hooks/useTokenState" jest.mock("@web3-react/core", () => ({ ...(jest.requireActual("@web3-react/core") as {}), @@ -21,8 +21,8 @@ jest.mock("../../../hooks/useTokenBalance", () => ({ useTokenBalance: jest.fn(), })) -jest.mock("../../../hooks/useReduxToken", () => ({ - useReduxToken: jest.fn(), +jest.mock("../../../hooks/useTokenState", () => ({ + useTokenState: jest.fn(), })) describe("Test `useSubscribeToERC20TransferEvent` hook", () => { @@ -67,10 +67,10 @@ describe("Test `useSubscribeToERC20TransferEvent` hook", () => { mockedContract.on.mockImplementation((eventName, callback) => { contractEventEmitter.addListener(eventName, callback) }) - ;(useWeb3React as jest.Mock).mockReturnValue({ account }) + ;(useWeb3React as jest.Mock).mockReturnValue({ account, active: true }) ;(useToken as jest.Mock).mockReturnValue({ contract: mockedContract }) ;(useTokenBalance as jest.Mock).mockReturnValue(currentTokenBalance) - ;(useReduxToken as jest.Mock).mockReturnValue({ + ;(useTokenState as jest.Mock).mockReturnValue({ setTokenBalance: mockedSetTokenBalance, }) }) diff --git a/src/web3/hooks/index.ts b/src/web3/hooks/index.ts index 2ae98a97c..5ad9fac94 100644 --- a/src/web3/hooks/index.ts +++ b/src/web3/hooks/index.ts @@ -15,3 +15,4 @@ export * from "./useKeepBondingContract" export * from "./useAssetPoolContract" export * from "./useTBTCTokenContract" export * from "./useTStakingContract" +export * from "./useKeepTokenStakingContract" diff --git a/src/web3/hooks/useContract.ts b/src/web3/hooks/useContract.ts index b13cb58ee..2ce6015e9 100644 --- a/src/web3/hooks/useContract.ts +++ b/src/web3/hooks/useContract.ts @@ -1,24 +1,29 @@ import { useMemo } from "react" -import { Contract } from "@ethersproject/contracts" import { useWeb3React } from "@web3-react/core" +import { Contract } from "@ethersproject/contracts" +import { JsonRpcProvider } from "@ethersproject/providers" +import { getEnvVariable } from "../../utils/getEnvVariable" import { getContract } from "../../utils/getContract" +import { EnvVariable } from "../../enums" export function useContract( address: string, ABI: any, withSignerIfPossible = true ): T | null { - const { library, account, chainId } = useWeb3React() + const { library, account, chainId, active } = useWeb3React() return useMemo(() => { - if (!address || !ABI || !library || !chainId) { + if (!address || !ABI) { return null } return getContract( address, ABI, - library, + active + ? library + : new JsonRpcProvider(getEnvVariable(EnvVariable.ETH_HOSTNAME_HTTP)), withSignerIfPossible && account ? account : undefined ) }, [address, ABI, library, chainId, withSignerIfPossible, account]) as T diff --git a/src/web3/hooks/useKeepTokenStakingContract.ts b/src/web3/hooks/useKeepTokenStakingContract.ts new file mode 100644 index 000000000..0ddff5e16 --- /dev/null +++ b/src/web3/hooks/useKeepTokenStakingContract.ts @@ -0,0 +1,10 @@ +import KeepTokenStaking from "@keep-network/keep-core/artifacts/TokenStaking.json" +import { useContract } from "./useContract" +import { getContractAddressFromTruffleArtifact } from "../../utils/getContract" + +const KEEP_TOKEN_STAKING_ADDRESS = + getContractAddressFromTruffleArtifact(KeepTokenStaking) + +export const useKeepTokenStakingContract = () => { + return useContract(KEEP_TOKEN_STAKING_ADDRESS, KeepTokenStaking.abi) +} diff --git a/src/web3/hooks/useSubscribeToContractEvent.ts b/src/web3/hooks/useSubscribeToContractEvent.ts index a3501945c..8a2fb57a6 100644 --- a/src/web3/hooks/useSubscribeToContractEvent.ts +++ b/src/web3/hooks/useSubscribeToContractEvent.ts @@ -1,5 +1,6 @@ import { useEffect, useRef } from "react" import { Contract, EventFilter } from "@ethersproject/contracts" +import { useWeb3React } from "@web3-react/core" // TODO: types export const useSubscribeToContractEvent = ( @@ -12,6 +13,7 @@ export const useSubscribeToContractEvent = ( // by `to` param we need to pass `[null,
]`. indexedFilterParams: string[] = [] ) => { + const { active } = useWeb3React() const callbackRef = useRef<(args: any[]) => void>() const indexedFilterParamsLength = indexedFilterParams.length // An event can have up to 3 indexed params. We want to extract these values @@ -29,7 +31,7 @@ export const useSubscribeToContractEvent = ( }, [callback]) useEffect(() => { - if (!contract) { + if (!contract || !active) { return }