From 201eb279b65d8abf5182514fdffc2854da12db7a Mon Sep 17 00:00:00 2001 From: Jacob Bryant Date: Fri, 19 May 2023 17:46:15 -0400 Subject: [PATCH] APY/APR work --- src/components/selectors/VariableRate.tsx | 13 ++-- src/components/views/Borrow.tsx | 10 ++- src/components/views/Lend.tsx | 5 +- src/components/views/LendPosition.tsx | 2 +- src/contracts/VRInterestRateOracle.d.ts | 46 +++++++++++++ src/contracts/abis/VRInterestRateOracle.json | 12 ++++ .../VRInterestRateOracle__factory.ts | 37 +++++++++++ src/hooks/entities/useVYTokens.ts | 11 ++-- src/hooks/useApr.ts | 66 +------------------ .../useLendHelpers/useLendHelpersVR.ts | 3 +- 10 files changed, 127 insertions(+), 78 deletions(-) diff --git a/src/components/selectors/VariableRate.tsx b/src/components/selectors/VariableRate.tsx index f80639eb3..1542c77ab 100644 --- a/src/components/selectors/VariableRate.tsx +++ b/src/components/selectors/VariableRate.tsx @@ -4,6 +4,7 @@ import styled from 'styled-components'; import { UserContext } from '../../contexts/UserContext'; import { SettingsContext } from '../../contexts/SettingsContext'; import YieldMark from '../logos/YieldMark'; +import SkeletonWrap from '../wraps/SkeletonWrap'; const StyledBox = styled(Box)` -webkit-transition: transform 0.3s ease-in-out; @@ -68,7 +69,7 @@ const ShineyBox = styled(Box)` } `; -const VariableRate = () => { +const VariableRate = ({ rate }: { rate?: string }) => { const { settingsState: { darkMode }, } = useContext(SettingsContext); @@ -139,9 +140,13 @@ const VariableRate = () => { - - {2.7} % {'APR'} - + {rate ? ( + + {parseFloat(rate).toFixed(2)} % {'APR'} + + ) : ( + + )} diff --git a/src/components/views/Borrow.tsx b/src/components/views/Borrow.tsx index 8e96f4196..691286951 100644 --- a/src/components/views/Borrow.tsx +++ b/src/components/views/Borrow.tsx @@ -25,6 +25,7 @@ import InfoBite from '../InfoBite'; import NextButton from '../buttons/NextButton'; import TransactButton from '../buttons/TransactButton'; import { useApr } from '../../hooks/useApr'; +import { useAprVR } from '../../hooks/useAprVR'; import PositionAvatar from '../PositionAvatar'; import VaultDropSelector from '../selectors/VaultDropSelector'; import { useInputValidation } from '../../hooks/useInputValidation'; @@ -89,6 +90,8 @@ const Borrow = () => { const borrow = useBorrow(); const { apr } = useApr(borrowInput, ActionType.BORROW, selectedSeries); + const { apr: aprVR } = useAprVR(borrowInput, ActionType.BORROW); + const { data: assetPair } = useAssetPair(selectedBase?.id, selectedIlk?.id); const { data: basesVR } = useBasesVR(); const { data: vaultsVR } = useVaultsVR(); @@ -168,6 +171,7 @@ const Borrow = () => { const handleMaxAction = (actionCode: ActionCodes) => { actionCode === ActionCodes.ADD_COLLATERAL && setCollatInput(maxCollateral!); actionCode === ActionCodes.BORROW && selectedSeries && setBorrowInput(selectedSeries.sharesReserves_!); + actionCode === ActionCodes.BORROW && selectedVR && setBorrowInput(maxDebtVR_!); logAnalyticsEvent(GA_Event.max_clicked, { view: GA_View.BORROW, action_code: actionCode, @@ -340,7 +344,9 @@ const Borrow = () => { placeholder="Enter amount" value={borrowInput} onChange={(event: any) => - setBorrowInput(cleanValue(event.target.value, selectedSeries?.decimals)) + setBorrowInput( + cleanValue(event.target.value, selectedSeries?.decimals || selectedBase?.decimals) + ) } autoFocus={!mobile} /> @@ -370,7 +376,7 @@ const Borrow = () => { {basesVR?.length && basesVR.includes(selectedBase?.id!) ? ( - + ) : null} diff --git a/src/components/views/Lend.tsx b/src/components/views/Lend.tsx index aa631d2b6..879e40086 100644 --- a/src/components/views/Lend.tsx +++ b/src/components/views/Lend.tsx @@ -48,6 +48,7 @@ import { useLendHelpersFR } from '../../hooks/viewHelperHooks/useLendHelpers/use import { useLendHelpersVR } from '../../hooks/viewHelperHooks/useLendHelpers/useLendHelpersVR'; import useVYTokens from '../../hooks/entities/useVYTokens'; import useBasesVR from '../../hooks/views/useBasesVR'; +import { useAprVR } from '../../hooks/useAprVR'; const Lend = () => { const mobile: boolean = useContext(ResponsiveContext) === 'small'; @@ -65,6 +66,8 @@ const Lend = () => { const [stepPosition, setStepPosition] = useState(0); const [stepDisabled, setStepDisabled] = useState(true); + const { apr: aprVR } = useAprVR(lendInput, ActionType.LEND); + /* HOOK FNS */ const { maxLend_: maxLendFR_, @@ -214,7 +217,7 @@ const Lend = () => { {basesVR?.length && basesVR.includes(selectedBase?.id!) ? ( - + ) : null} diff --git a/src/components/views/LendPosition.tsx b/src/components/views/LendPosition.tsx index 97fbbfa3a..52007eac6 100644 --- a/src/components/views/LendPosition.tsx +++ b/src/components/views/LendPosition.tsx @@ -307,7 +307,7 @@ const LendPosition = () => { /> } /> diff --git a/src/contracts/VRInterestRateOracle.d.ts b/src/contracts/VRInterestRateOracle.d.ts index c0654009a..6006030a4 100644 --- a/src/contracts/VRInterestRateOracle.d.ts +++ b/src/contracts/VRInterestRateOracle.d.ts @@ -171,6 +171,7 @@ interface VRInterestRateOracleInterface extends ethers.utils.Interface { ): Result; events: { + "AccumulationUpdated(bytes6,bytes6,uint256,uint256,uint256)": EventFragment; "InterestRateParamSet(bytes6,bytes6,uint256,uint256,uint256,uint256,address)": EventFragment; "InterestRateParamUpdated(bytes6,bytes6,uint256,uint256,uint256,uint256,address)": EventFragment; "RoleAdminChanged(bytes4,bytes4)": EventFragment; @@ -178,6 +179,7 @@ interface VRInterestRateOracleInterface extends ethers.utils.Interface { "RoleRevoked(bytes4,address,address)": EventFragment; }; + getEvent(nameOrSignatureOrTopic: "AccumulationUpdated"): EventFragment; getEvent(nameOrSignatureOrTopic: "InterestRateParamSet"): EventFragment; getEvent(nameOrSignatureOrTopic: "InterestRateParamUpdated"): EventFragment; getEvent(nameOrSignatureOrTopic: "RoleAdminChanged"): EventFragment; @@ -185,6 +187,16 @@ interface VRInterestRateOracleInterface extends ethers.utils.Interface { getEvent(nameOrSignatureOrTopic: "RoleRevoked"): EventFragment; } +export type AccumulationUpdatedEvent = TypedEvent< + [string, string, BigNumber, BigNumber, BigNumber] & { + baseId: string; + kind: string; + accumulated: BigNumber; + lastUpdateTimestamp: BigNumber; + utilizationRate: BigNumber; + } +>; + export type InterestRateParamSetEvent = TypedEvent< [string, string, BigNumber, BigNumber, BigNumber, BigNumber, string] & { baseId: string; @@ -637,6 +649,40 @@ export class VRInterestRateOracle extends BaseContract { }; filters: { + "AccumulationUpdated(bytes6,bytes6,uint256,uint256,uint256)"( + baseId?: BytesLike | null, + kind?: BytesLike | null, + accumulated?: null, + lastUpdateTimestamp?: null, + utilizationRate?: null + ): TypedEventFilter< + [string, string, BigNumber, BigNumber, BigNumber], + { + baseId: string; + kind: string; + accumulated: BigNumber; + lastUpdateTimestamp: BigNumber; + utilizationRate: BigNumber; + } + >; + + AccumulationUpdated( + baseId?: BytesLike | null, + kind?: BytesLike | null, + accumulated?: null, + lastUpdateTimestamp?: null, + utilizationRate?: null + ): TypedEventFilter< + [string, string, BigNumber, BigNumber, BigNumber], + { + baseId: string; + kind: string; + accumulated: BigNumber; + lastUpdateTimestamp: BigNumber; + utilizationRate: BigNumber; + } + >; + "InterestRateParamSet(bytes6,bytes6,uint256,uint256,uint256,uint256,address)"( baseId?: BytesLike | null, kind?: BytesLike | null, diff --git a/src/contracts/abis/VRInterestRateOracle.json b/src/contracts/abis/VRInterestRateOracle.json index 996dca2a1..92c4dece7 100644 --- a/src/contracts/abis/VRInterestRateOracle.json +++ b/src/contracts/abis/VRInterestRateOracle.json @@ -7,6 +7,18 @@ "stateMutability": "nonpayable", "type": "constructor" }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "bytes6", "name": "baseId", "type": "bytes6" }, + { "indexed": true, "internalType": "bytes6", "name": "kind", "type": "bytes6" }, + { "indexed": false, "internalType": "uint256", "name": "accumulated", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "lastUpdateTimestamp", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "utilizationRate", "type": "uint256" } + ], + "name": "AccumulationUpdated", + "type": "event" + }, { "anonymous": false, "inputs": [ diff --git a/src/contracts/factories/VRInterestRateOracle__factory.ts b/src/contracts/factories/VRInterestRateOracle__factory.ts index b821eab0b..7911e3a60 100644 --- a/src/contracts/factories/VRInterestRateOracle__factory.ts +++ b/src/contracts/factories/VRInterestRateOracle__factory.ts @@ -26,6 +26,43 @@ const _abi = [ stateMutability: "nonpayable", type: "constructor", }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "bytes6", + name: "baseId", + type: "bytes6", + }, + { + indexed: true, + internalType: "bytes6", + name: "kind", + type: "bytes6", + }, + { + indexed: false, + internalType: "uint256", + name: "accumulated", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "lastUpdateTimestamp", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "utilizationRate", + type: "uint256", + }, + ], + name: "AccumulationUpdated", + type: "event", + }, { anonymous: false, inputs: [ diff --git a/src/hooks/entities/useVYTokens.ts b/src/hooks/entities/useVYTokens.ts index 0f4d1c4a6..36fa5e5ad 100644 --- a/src/hooks/entities/useVYTokens.ts +++ b/src/hooks/entities/useVYTokens.ts @@ -9,8 +9,6 @@ import { BigNumber, ethers } from 'ethers'; import { formatUnits } from 'ethers/lib/utils.js'; import { ChainContext } from '../../contexts/ChainContext'; import { MulticallContext } from '../../contexts/MutlicallContext'; -import { useApr } from '../useApr'; -import { ActionType } from '../../types'; export interface IVYToken extends ISignable { id: string; // vyToken address @@ -32,7 +30,6 @@ const useVYTokens = () => { const { address: account } = useAccountPlus(); const { useForkedEnv, provider: forkProvider, forkUrl } = useFork(); const provider = useDefaultProvider(); - const { apr } = useApr(undefined, ActionType.LEND, null); const { chainState: { assetRootMap }, @@ -42,7 +39,6 @@ const useVYTokens = () => { const multicall = useForkedEnv ? forkMulticall : _multicall; const get = useCallback(async () => { - console.log('getting vyToken data'); return await Array.from(assetRootMap.values()) .map((a) => [a.VYTokenProxyAddress, a.VYTokenAddress]) // get asset's vyTokenProxy addr .reduce(async (vyTokens, [proxyAddress, address]) => { @@ -65,11 +61,14 @@ const useVYTokens = () => { let vyTokenBaseVal = balance; try { - vyTokenBaseVal = await proxy.previewRedeem(balance); + vyTokenBaseVal = await proxy.callStatic.previewRedeem(balance); } catch (e) { console.log('Error getting vyTokenBaseVal', e); } + const accumulatedInterestInBase = vyTokenBaseVal.sub(balance); + const accumulatedInterestInBase_ = formatUnits(accumulatedInterestInBase, decimals); + const addr = address.toLowerCase(); const data: IVYToken = { id: addr, @@ -87,7 +86,7 @@ const useVYTokens = () => { vyTokenBaseVal, vyTokenBaseVal_: formatUnits(vyTokenBaseVal, decimals), proxyAddress: proxyAddress.toLowerCase(), - accumulatedInterestInBase_: '0.0', + accumulatedInterestInBase_, }; return (await vyTokens).set(addr, data); diff --git a/src/hooks/useApr.ts b/src/hooks/useApr.ts index 0de033737..dfeb57891 100644 --- a/src/hooks/useApr.ts +++ b/src/hooks/useApr.ts @@ -1,22 +1,11 @@ -import { ethers, BigNumber } from 'ethers'; +import { ethers } from 'ethers'; import { useContext, useEffect, useState } from 'react'; -import { sellBase, buyBase, calculateAPR, bytesToBytes32 } from '@yield-protocol/ui-math'; - +import { sellBase, buyBase, calculateAPR } from '@yield-protocol/ui-math'; import { ETH_BASED_ASSETS } from '../config/assets'; import { UserContext } from '../contexts/UserContext'; import { ActionType, ISeries } from '../types'; import { cleanValue } from '../utils/appUtils'; import useTimeTillMaturity from './useTimeTillMaturity'; -import { useProvider } from 'wagmi'; -import * as contractTypes from '../contracts'; - -import { VRInterestRateOracle__factory } from '../contracts'; -import useContracts from './useContracts'; -import { ContractNames } from '../config/contracts'; -import { RATE, ZERO_BN, CHI } from '../utils/constants'; -import { useConvertValue } from './useConvertValue'; -import useFork from './useFork'; -import { formatUnits } from 'ethers/lib/utils.js'; /* APR hook calculatess APR, min and max aprs for selected series and BORROW or LEND type */ export const useApr = (input: string | undefined, actionType: ActionType, series: ISeries | null) => { @@ -26,9 +15,6 @@ export const useApr = (input: string | undefined, actionType: ActionType, series /* HOOKS */ const { getTimeTillMaturity, isMature } = useTimeTillMaturity(); - const provider = useProvider(); - const contracts = useContracts(); - const { useForkedEnv, forkStartBlock } = useFork(); const _selectedSeries = series || selectedSeries; /* Make sure there won't be an underflow */ @@ -73,53 +59,7 @@ export const useApr = (input: string | undefined, actionType: ActionType, series // figure out what to do with negative apr on borrow for tv series const _apr = calculateAPR(baseAmount, preview, _selectedSeries.maturity); _apr ? setApr(cleanValue(_apr, 2)) : setApr(_selectedSeries.apr); - } else if (selectedBase) { - /* logic for VR */ - const cleanedInput = cleanValue(_input || _fallbackInput, selectedBase.decimals); - const baseAmount = ethers.utils.parseUnits(cleanedInput, selectedBase.decimals); - - const now = Date.now() + 20000; - // trying to call interest rate oracle - // const interestRateOracleAddr = '0xa60eb553b65284e3a221b958c9115d8e558289bf'; - - const getAPY = async () => { - try { - const VRCauldron = contracts?.get(ContractNames.VR_CAULDRON) as contractTypes.VRCauldron; - const interestRateOracleAddr = await VRCauldron.rateOracles(selectedBase.id); - const interestRateOracle = VRInterestRateOracle__factory.connect(interestRateOracleAddr, provider); - const joinAddress = selectedBase.joinAddressVR; - // console.log('INTEREST RATE ORACLE', interestRateOracleAddr, interestRateOracle); - - let rate: any = ethers.constants.Zero; // TODO - fix this type - - if (actionType === 'LEND') { - rate = await interestRateOracle.peek(bytesToBytes32(selectedBase.id, 6), RATE, '0'); - } - - if (actionType === 'BORROW') { - rate = await interestRateOracle.peek( - bytesToBytes32(selectedBase.id, 6), - bytesToBytes32('0x434849000000', 6), // TODO - make this a constant - '0' - ); - } - - // console.log('rate in useAPR', rate); - - return rate; - } catch (e) { - console.log(`Error getting APY for ${selectedBase.symbol}:`, e); - return ethers.constants.Zero; - } - }; - - getAPY().then((res) => { - const rate = res.accumulated - ? formatUnits(BigNumber.from(res.accumulated.toHexString()), selectedBase.decimals) - : ethers.constants.Zero; - - setApr(rate.toString()); - }); + console.log('baseAmount FR', baseAmount, baseAmount.toString(), _fallbackInput, _input); } }, [_selectedSeries, _input, actionType, _fallbackInput, getTimeTillMaturity]); diff --git a/src/hooks/viewHelperHooks/useLendHelpers/useLendHelpersVR.ts b/src/hooks/viewHelperHooks/useLendHelpers/useLendHelpersVR.ts index a54c1e29d..ade596140 100644 --- a/src/hooks/viewHelperHooks/useLendHelpers/useLendHelpersVR.ts +++ b/src/hooks/viewHelperHooks/useLendHelpers/useLendHelpersVR.ts @@ -2,6 +2,7 @@ import { useContext, useMemo } from 'react'; import { UserContext } from '../../../contexts/UserContext'; import { ActionType } from '../../../types'; import { useApr } from '../../useApr'; +import { useAprVR } from '../../useAprVR'; import { Address, useBalance } from 'wagmi'; import { WETH } from '../../../config/assets'; import useAccountPlus from '../../useAccountPlus'; @@ -24,7 +25,7 @@ export const useLendHelpersVR = (vyTokenAddress: string | undefined, input?: str const { data: vyTokens } = useVYTokens(); const vyToken = vyTokens?.get(vyTokenAddress!); - const { apr: apy } = useApr(input, ActionType.LEND, null); // TODO - handle vr apy's + const { apr: apy } = useAprVR(input ? input : vyToken?.balance_, ActionType.LEND); // TODO - handle vr apy's return { maxLend: baseBal?.value,