diff --git a/app/src/pages/earn/components/InterestSection.tsx b/app/src/pages/earn/components/InterestSection.tsx index a2a0eeaa9..bee6a6094 100644 --- a/app/src/pages/earn/components/InterestSection.tsx +++ b/app/src/pages/earn/components/InterestSection.tsx @@ -2,9 +2,9 @@ import { AnimateNumber, demicrofy, formatRate, - formatUST, + formatUSTWithPostfixUnits, } from '@anchor-protocol/notation'; -import { Rate, UST } from '@anchor-protocol/types'; +import { Rate, UST, uUST } from '@anchor-protocol/types'; import { APYChart, APYChartItem, @@ -15,11 +15,12 @@ import { Section } from '@terra-dev/neumorphism-ui/components/Section'; import { Tab } from '@terra-dev/neumorphism-ui/components/Tab'; import { TooltipLabel } from '@terra-dev/neumorphism-ui/components/TooltipLabel'; import { useConstants } from 'base/contexts/contants'; -import big from 'big.js'; +import big, { Big } from 'big.js'; import { currentAPY } from 'pages/earn/logics/currentAPY'; import { useAPYHistory } from 'pages/earn/queries/apyHistory'; +import { useExpectedInterest } from 'pages/earn/queries/expectedInterest'; import { useInterest } from 'pages/earn/queries/interest'; -import { Period, useInterestEarned } from 'pages/earn/queries/interestEarned'; +import { Period } from 'pages/earn/queries/interestEarned'; import { useMemo, useState } from 'react'; export interface InterestSectionProps { @@ -32,10 +33,6 @@ interface Item { } const tabItems: Item[] = [ - { - label: 'TOTAL', - value: 'total', - }, { label: 'YEAR', value: 'year', @@ -69,17 +66,48 @@ export function InterestSection({ className }: InterestSectionProps) { data: { marketStatus }, } = useInterest(); - const { - data: { interestEarned }, - } = useInterestEarned(tab.value); - const { data: { apyHistory }, } = useAPYHistory(); + const { + data: { aUSTBalance, moneyMarketEpochState, overseerEpochState }, + } = useExpectedInterest(); + // --------------------------------------------- // logics // --------------------------------------------- + const expectedReturn = useMemo(() => { + if (!aUSTBalance || !moneyMarketEpochState || !overseerEpochState) { + return undefined; + } + + const ustBalance = big(aUSTBalance.balance).mul( + moneyMarketEpochState.exchange_rate, + ); + const annualizedInterestRate = big(overseerEpochState.deposit_rate).mul( + blocksPerYear, + ); + + return ustBalance + .mul(annualizedInterestRate) + .div( + tab.value === 'month' + ? 12 + : tab.value === 'week' + ? 52 + : tab.value === 'day' + ? 365 + : 1, + ) as uUST; + }, [ + aUSTBalance, + blocksPerYear, + moneyMarketEpochState, + overseerEpochState, + tab.value, + ]); + const apy = useMemo(() => currentAPY(marketStatus, blocksPerYear), [ blocksPerYear, marketStatus, @@ -154,16 +182,16 @@ export function InterestSection({ className }: InterestSectionProps) {
- - {interestEarned ? demicrofy(interestEarned) : (0 as UST)} + + {expectedReturn ? demicrofy(expectedReturn) : (0 as UST)} {' '} UST

- Interest earned{' '} + Expected Interest{' '} - Interest accrued for the selected time period + Estimated interest for the selected time period

diff --git a/app/src/pages/earn/queries/expectedInterest.ts b/app/src/pages/earn/queries/expectedInterest.ts new file mode 100644 index 000000000..f5c99bd90 --- /dev/null +++ b/app/src/pages/earn/queries/expectedInterest.ts @@ -0,0 +1,191 @@ +import { + cw20, + CW20Addr, + HumanAddr, + moneyMarket, + uaUST, + WASMContractResult, +} from '@anchor-protocol/types'; +import { useWallet } from '@anchor-protocol/wallet-provider'; +import { gql, useQuery } from '@apollo/client'; +import { useEventBus } from '@terra-dev/event-bus'; +import { createMap, useMap } from '@terra-dev/use-map'; +import { useContractAddress } from 'base/contexts/contract'; +import { useLastSyncedHeight } from 'base/queries/lastSyncedHeight'; +import { parseResult } from 'base/queries/parseResult'; +import { MappedQueryResult } from 'base/queries/types'; +import { useQueryErrorHandler } from 'base/queries/useQueryErrorHandler'; +import { useRefetch } from 'base/queries/useRefetch'; +import { useEffect, useMemo } from 'react'; + +export interface RawData { + aUSTBalance: WASMContractResult; + moneyMarketEpochState: WASMContractResult; + overseerEpochState: WASMContractResult; +} + +export interface Data { + aUSTBalance: WASMContractResult>; + moneyMarketEpochState: WASMContractResult; + overseerEpochState: WASMContractResult; +} + +export const dataMap = createMap({ + aUSTBalance: (existing, { aUSTBalance }) => { + return parseResult(existing.aUSTBalance, aUSTBalance.Result); + }, + moneyMarketEpochState: (existing, { moneyMarketEpochState }) => { + return parseResult( + existing.moneyMarketEpochState, + moneyMarketEpochState.Result, + ); + }, + overseerEpochState: (existing, { overseerEpochState }) => { + return parseResult(existing.overseerEpochState, overseerEpochState.Result); + }, +}); + +export interface RawVariables { + anchorTokenContract: string; + anchorTokenQuery: string; + moneyMarketContract: string; + moneyMarketEpochStateQuery: string; + overseerContract: string; + overseerEpochStateQuery: string; +} + +export interface Variables { + anchorTokenContract: CW20Addr; + anchorTokenQuery: cw20.Balance; + moneyMarketContract: HumanAddr; + moneyMarketEpochStateQuery: moneyMarket.market.EpochState; + overseerContract: HumanAddr; + overseerEpochStateQuery: moneyMarket.overseer.EpochState; +} + +export function mapVariables({ + anchorTokenContract, + anchorTokenQuery, + moneyMarketContract, + moneyMarketEpochStateQuery, + overseerContract, + overseerEpochStateQuery, +}: Variables): RawVariables { + return { + anchorTokenContract, + anchorTokenQuery: JSON.stringify(anchorTokenQuery), + moneyMarketContract, + moneyMarketEpochStateQuery: JSON.stringify(moneyMarketEpochStateQuery), + overseerContract, + overseerEpochStateQuery: JSON.stringify(overseerEpochStateQuery), + }; +} + +export const query = gql` + query __expectedInterest( + $anchorTokenContract: String! + $anchorTokenQuery: String! + $moneyMarketContract: String! + $moneyMarketEpochStateQuery: String! + $overseerContract: String! + $overseerEpochStateQuery: String! + ) { + aUSTBalance: WasmContractsContractAddressStore( + ContractAddress: $anchorTokenContract + QueryMsg: $anchorTokenQuery + ) { + Result + } + + moneyMarketEpochState: WasmContractsContractAddressStore( + ContractAddress: $moneyMarketContract + QueryMsg: $moneyMarketEpochStateQuery + ) { + Result + } + + overseerEpochState: WasmContractsContractAddressStore( + ContractAddress: $overseerContract + QueryMsg: $overseerEpochStateQuery + ) { + Result + } + } +`; + +export function useExpectedInterest(): MappedQueryResult< + RawVariables, + RawData, + Data +> { + const { status } = useWallet(); + + const { data: lastSyncedHeight } = useLastSyncedHeight(); + + const address = useContractAddress(); + + const { dispatch } = useEventBus(); + + const variables = useMemo(() => { + if (status.status !== 'ready' || lastSyncedHeight === 0) { + return undefined; + } + + return mapVariables({ + anchorTokenContract: address.cw20.aUST, + anchorTokenQuery: { + balance: { + address: status.walletAddress, + }, + }, + moneyMarketContract: address.moneyMarket.market, + moneyMarketEpochStateQuery: { + epoch_state: { + block_height: lastSyncedHeight, + }, + }, + overseerContract: address.moneyMarket.overseer, + overseerEpochStateQuery: { + epoch_state: { + block_height: lastSyncedHeight, + }, + }, + }); + }, [ + address.cw20.aUST, + address.moneyMarket.market, + address.moneyMarket.overseer, + lastSyncedHeight, + status, + ]); + + const onError = useQueryErrorHandler(); + + const { + previousData, + data: _data = previousData, + refetch: _refetch, + error, + ...result + } = useQuery(query, { + skip: !variables, + fetchPolicy: 'network-only', + nextFetchPolicy: 'cache-first', + //pollInterval: 1000 * 60, + variables, + onError, + }); + + const data = useMap(_data, dataMap); + const refetch = useRefetch(_refetch, dataMap); + + useEffect(() => { + dispatch('interest-earned-updated'); + }, [variables, dispatch]); + + return { + ...result, + data, + refetch, + }; +}