Skip to content

Commit

Permalink
Merge pull request #62 from Anchor-Protocol/feature/expected-interest
Browse files Browse the repository at this point in the history
Fix: Expected Interest
  • Loading branch information
Seo Yeon, Lee authored Mar 23, 2021
2 parents f9a38d7 + becc666 commit 89a8173
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 16 deletions.
60 changes: 44 additions & 16 deletions app/src/pages/earn/components/InterestSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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 {
Expand All @@ -32,10 +33,6 @@ interface Item {
}

const tabItems: Item[] = [
{
label: 'TOTAL',
value: 'total',
},
{
label: 'YEAR',
value: 'year',
Expand Down Expand Up @@ -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<Big>;
}, [
aUSTBalance,
blocksPerYear,
moneyMarketEpochState,
overseerEpochState,
tab.value,
]);

const apy = useMemo(() => currentAPY(marketStatus, blocksPerYear), [
blocksPerYear,
marketStatus,
Expand Down Expand Up @@ -154,16 +182,16 @@ export function InterestSection({ className }: InterestSectionProps) {

<div className="amount">
<span>
<AnimateNumber format={formatUST}>
{interestEarned ? demicrofy(interestEarned) : (0 as UST<number>)}
<AnimateNumber format={formatUSTWithPostfixUnits}>
{expectedReturn ? demicrofy(expectedReturn) : (0 as UST<number>)}
</AnimateNumber>{' '}
UST
</span>
<p>
<IconSpan>
Interest earned{' '}
Expected Interest{' '}
<InfoTooltip>
Interest accrued for the selected time period
Estimated interest for the selected time period
</InfoTooltip>
</IconSpan>
</p>
Expand Down
191 changes: 191 additions & 0 deletions app/src/pages/earn/queries/expectedInterest.ts
Original file line number Diff line number Diff line change
@@ -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<cw20.BalanceResponse<uaUST>>;
moneyMarketEpochState: WASMContractResult<moneyMarket.market.EpochStateResponse>;
overseerEpochState: WASMContractResult<moneyMarket.overseer.EpochStateResponse>;
}

export const dataMap = createMap<RawData, Data>({
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<RawData, RawVariables>(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,
};
}

3 comments on commit 89a8173

@vercel
Copy link

@vercel vercel bot commented on 89a8173 Mar 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 89a8173 Mar 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 89a8173 Mar 23, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

anchor-storybook – ./packages

anchor-storybook-git-master-anchor-protocol.vercel.app
anchor-storybook-anchor-protocol.vercel.app

Please sign in to comment.