Mp 2548 deposit into vault (#264)
* Implement vault deposit message * Merge custom Coin into BNCoin * Fix build errors * fixed tests
This commit is contained in:
parent
c69461b95d
commit
415da05b8d
@ -5,6 +5,7 @@ import { BN } from 'utils/helpers'
|
|||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import DisplayCurrency from 'components/DisplayCurrency'
|
import DisplayCurrency from 'components/DisplayCurrency'
|
||||||
import VaultBorrowings, { VaultBorrowingsProps } from 'components/Modals/Vault/VaultBorrowings'
|
import VaultBorrowings, { VaultBorrowingsProps } from 'components/Modals/Vault/VaultBorrowings'
|
||||||
|
import { TESTNET_VAULTS_META_DATA } from 'constants/vaults'
|
||||||
|
|
||||||
jest.mock('hooks/usePrices', () =>
|
jest.mock('hooks/usePrices', () =>
|
||||||
jest.fn(() => ({
|
jest.fn(() => ({
|
||||||
@ -20,11 +21,27 @@ jest.mock('hooks/useMarketAssets', () =>
|
|||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
jest.mock('hooks/broadcast/useDepositVault', () => jest.fn(() => ({})))
|
||||||
|
|
||||||
jest.mock('components/DisplayCurrency')
|
jest.mock('components/DisplayCurrency')
|
||||||
|
|
||||||
const mockedDisplayCurrency = jest
|
const mockedDisplayCurrency = jest
|
||||||
.mocked(DisplayCurrency)
|
.mocked(DisplayCurrency)
|
||||||
.mockImplementation(() => <div>Display currency</div>)
|
.mockImplementation(() => <div>Display currency</div>)
|
||||||
|
|
||||||
|
const mockedVault: Vault = {
|
||||||
|
...TESTNET_VAULTS_META_DATA[0],
|
||||||
|
apy: 0,
|
||||||
|
ltv: {
|
||||||
|
liq: 0.2,
|
||||||
|
max: 0.1,
|
||||||
|
},
|
||||||
|
cap: {
|
||||||
|
denom: 'test',
|
||||||
|
max: 10,
|
||||||
|
used: 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
describe('<VaultBorrowings />', () => {
|
describe('<VaultBorrowings />', () => {
|
||||||
const defaultProps: VaultBorrowingsProps = {
|
const defaultProps: VaultBorrowingsProps = {
|
||||||
primaryAsset: ASSETS[0],
|
primaryAsset: ASSETS[0],
|
||||||
@ -38,7 +55,9 @@ describe('<VaultBorrowings />', () => {
|
|||||||
vaults: [],
|
vaults: [],
|
||||||
lends: [],
|
lends: [],
|
||||||
},
|
},
|
||||||
|
vault: mockedVault,
|
||||||
borrowings: [],
|
borrowings: [],
|
||||||
|
deposits: [],
|
||||||
onChangeBorrowings: jest.fn(),
|
onChangeBorrowings: jest.fn(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
"@tippyjs/react": "^4.2.6",
|
"@tippyjs/react": "^4.2.6",
|
||||||
"bignumber.js": "^9.1.1",
|
"bignumber.js": "^9.1.1",
|
||||||
"classnames": "^2.3.2",
|
"classnames": "^2.3.2",
|
||||||
|
"debounce-promise": "^3.1.2",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"next": "^13.4.7",
|
"next": "^13.4.7",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
@ -41,6 +42,7 @@
|
|||||||
"@svgr/webpack": "^8.0.1",
|
"@svgr/webpack": "^8.0.1",
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@testing-library/react": "^14.0.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
|
"@types/debounce-promise": "^3.1.6",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
"@types/react": "18.2.14",
|
"@types/react": "18.2.14",
|
||||||
"@types/react-dom": "18.2.4",
|
"@types/react-dom": "18.2.4",
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import getAccount from 'api/accounts/getAccount'
|
import getAccount from 'api/accounts/getAccount'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
export default async function getAccountDebts(accountId: string): Promise<Coin[]> {
|
export default async function getAccountDebts(accountId: string): Promise<BNCoin[]> {
|
||||||
const account = await getAccount(accountId)
|
const account = await getAccount(accountId)
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
return account.debts
|
return account.debts.map((coin) => new BNCoin(coin))
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((_, reject) => reject('Account not found'))
|
return new Promise((_, reject) => reject('Account not found'))
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import getAccount from 'api/accounts/getAccount'
|
import getAccount from 'api/accounts/getAccount'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
export default async function getAccountDeposits(accountId: string) {
|
export default async function getAccountDeposits(accountId: string): Promise<BNCoin[]> {
|
||||||
const account = await getAccount(accountId)
|
const account = await getAccount(accountId)
|
||||||
|
|
||||||
if (account) {
|
if (account) {
|
||||||
return account.deposits
|
return account.deposits.map((coin) => new BNCoin(coin))
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise((_, reject) => reject('Account not found'))
|
return new Promise((_, reject) => reject('Account not found'))
|
||||||
|
@ -8,7 +8,9 @@ import getMarsPrice from 'api/prices/getMarsPrice'
|
|||||||
import { ASSETS } from 'constants/assets'
|
import { ASSETS } from 'constants/assets'
|
||||||
import { byDenom } from 'utils/array'
|
import { byDenom } from 'utils/array'
|
||||||
|
|
||||||
export default async function calculateAssetIncentivesApy(denom: string): Promise<number | null> {
|
export default async function calculateAssetIncentivesApy(
|
||||||
|
denom: string,
|
||||||
|
): Promise<BigNumber | null> {
|
||||||
try {
|
try {
|
||||||
const [assetIncentive, market] = await Promise.all([getAssetIncentive(denom), getMarket(denom)])
|
const [assetIncentive, market] = await Promise.all([getAssetIncentive(denom), getMarket(denom)])
|
||||||
|
|
||||||
@ -38,7 +40,7 @@ export default async function calculateAssetIncentivesApy(denom: string): Promis
|
|||||||
const totalAnnualReturnsValue = annualEmission.plus(marketReturns)
|
const totalAnnualReturnsValue = annualEmission.plus(marketReturns)
|
||||||
const incentivesApy = totalAnnualReturnsValue.dividedBy(marketLiquidityValue).multipliedBy(100)
|
const incentivesApy = totalAnnualReturnsValue.dividedBy(marketLiquidityValue).multipliedBy(100)
|
||||||
|
|
||||||
return incentivesApy.toNumber()
|
return incentivesApy
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
console.error(ex)
|
console.error(ex)
|
||||||
return null
|
return null
|
||||||
|
@ -19,8 +19,8 @@ export default async function getMarketBorrowings(): Promise<BorrowAsset[]> {
|
|||||||
...asset,
|
...asset,
|
||||||
borrowRate: market.borrowRate ?? 0,
|
borrowRate: market.borrowRate ?? 0,
|
||||||
liquidity: {
|
liquidity: {
|
||||||
amount: amount,
|
amount: BN(amount),
|
||||||
value: BN(amount).times(price).toString(),
|
value: BN(amount).times(price),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import getMarkets from 'api/markets/getMarkets'
|
import getMarkets from 'api/markets/getMarkets'
|
||||||
import { getRedBankQueryClient } from 'api/cosmwasm-client'
|
import { getRedBankQueryClient } from 'api/cosmwasm-client'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
export default async function getMarketDebts(): Promise<Coin[]> {
|
export default async function getMarketDebts(): Promise<BNCoin[]> {
|
||||||
try {
|
try {
|
||||||
const markets: Market[] = await getMarkets()
|
const markets: Market[] = await getMarkets()
|
||||||
const redBankQueryClient = await getRedBankQueryClient()
|
const redBankQueryClient = await getRedBankQueryClient()
|
||||||
@ -14,7 +15,9 @@ export default async function getMarketDebts(): Promise<Coin[]> {
|
|||||||
)
|
)
|
||||||
const debtsResults = await Promise.all(debtQueries)
|
const debtsResults = await Promise.all(debtQueries)
|
||||||
|
|
||||||
return debtsResults.map<Coin>((debt, index) => ({ denom: markets[index].denom, amount: debt }))
|
return debtsResults.map<BNCoin>(
|
||||||
|
(debt, index) => new BNCoin({ denom: markets[index].denom, amount: debt }),
|
||||||
|
)
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
throw ex
|
throw ex
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,20 @@
|
|||||||
import getMarkets from 'api/markets/getMarkets'
|
import getMarkets from 'api/markets/getMarkets'
|
||||||
import getUnderlyingLiquidityAmount from 'api/markets/getMarketUnderlyingLiquidityAmount'
|
import getUnderlyingLiquidityAmount from 'api/markets/getMarketUnderlyingLiquidityAmount'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
export default async function getMarketDeposits(): Promise<Coin[]> {
|
export default async function getMarketDeposits(): Promise<BNCoin[]> {
|
||||||
try {
|
try {
|
||||||
const markets: Market[] = await getMarkets()
|
const markets: Market[] = await getMarkets()
|
||||||
const depositQueries = markets.map(getUnderlyingLiquidityAmount)
|
const depositQueries = markets.map(getUnderlyingLiquidityAmount)
|
||||||
const depositsResults = await Promise.all(depositQueries)
|
const depositsResults = await Promise.all(depositQueries)
|
||||||
|
|
||||||
return depositsResults.map<Coin>((deposit, index) => ({
|
return depositsResults.map<BNCoin>(
|
||||||
|
(deposit, index) =>
|
||||||
|
new BNCoin({
|
||||||
denom: markets[index].denom,
|
denom: markets[index].denom,
|
||||||
amount: deposit,
|
amount: deposit,
|
||||||
}))
|
}),
|
||||||
|
)
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
throw ex
|
throw ex
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,26 @@
|
|||||||
import { BN } from 'utils/helpers'
|
import { BN } from 'utils/helpers'
|
||||||
import getMarketDeposits from 'api/markets/getMarketDeposits'
|
import getMarketDeposits from 'api/markets/getMarketDeposits'
|
||||||
import getMarketDebts from 'api/markets/getMarketDebts'
|
import getMarketDebts from 'api/markets/getMarketDebts'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
export default async function getMarketLiquidities(): Promise<Coin[]> {
|
export default async function getMarketLiquidities(): Promise<BNCoin[]> {
|
||||||
const deposits = await getMarketDeposits()
|
const deposits = await getMarketDeposits()
|
||||||
const debts = await getMarketDebts()
|
const debts = await getMarketDebts()
|
||||||
|
|
||||||
const liquidity: Coin[] = deposits.map((deposit) => {
|
const liquidity: BNCoin[] = deposits.map((deposit) => {
|
||||||
const debt = debts.find((debt) => debt.denom === deposit.denom)
|
const debt = debts.find((debt) => debt.denom === deposit.denom)
|
||||||
|
|
||||||
if (debt) {
|
if (debt) {
|
||||||
return {
|
return new BNCoin({
|
||||||
denom: deposit.denom,
|
denom: deposit.denom,
|
||||||
amount: BN(deposit.amount).minus(debt.amount).toString(),
|
amount: deposit.amount.minus(debt.amount).toString(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return new BNCoin({
|
||||||
denom: deposit.denom,
|
denom: deposit.denom,
|
||||||
amount: '0',
|
amount: '0',
|
||||||
}
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (liquidity) {
|
if (liquidity) {
|
||||||
|
19
src/api/vaults/getMinLpToReceive.ts
Normal file
19
src/api/vaults/getMinLpToReceive.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { getCreditManagerQueryClient } from 'api/cosmwasm-client'
|
||||||
|
import { ENV } from 'constants/env'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
|
export default async function getVaultConfigs(coins: Coin[], lpDenom: string): Promise<BigNumber> {
|
||||||
|
if (!ENV.ADDRESS_CREDIT_MANAGER) return BN(0)
|
||||||
|
|
||||||
|
const creditManagerQueryClient = await getCreditManagerQueryClient()
|
||||||
|
try {
|
||||||
|
return BN(
|
||||||
|
await creditManagerQueryClient.estimateProvideLiquidity({
|
||||||
|
coinsIn: coins,
|
||||||
|
lpTokenOut: lpDenom,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
} catch (ex) {
|
||||||
|
throw ex
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,7 @@ import Text from 'components/Text'
|
|||||||
import { ASSETS } from 'constants/assets'
|
import { ASSETS } from 'constants/assets'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { convertToDisplayAmount, demagnify } from 'utils/formatters'
|
import { convertToDisplayAmount, demagnify } from 'utils/formatters'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
data: Account
|
data: Account
|
||||||
@ -43,7 +44,7 @@ export const AccountBalancesTable = (props: Props) => {
|
|||||||
{ amount: deposit.amount, denom: deposit.denom },
|
{ amount: deposit.amount, denom: deposit.denom },
|
||||||
displayCurrency,
|
displayCurrency,
|
||||||
prices,
|
prices,
|
||||||
),
|
).toString(),
|
||||||
apy,
|
apy,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -60,7 +61,7 @@ export const AccountBalancesTable = (props: Props) => {
|
|||||||
{ amount: lending.amount, denom: lending.denom },
|
{ amount: lending.amount, denom: lending.denom },
|
||||||
displayCurrency,
|
displayCurrency,
|
||||||
prices,
|
prices,
|
||||||
),
|
).toString(),
|
||||||
apy,
|
apy,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -104,9 +105,11 @@ export const AccountBalancesTable = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
className='text-right text-xs'
|
className='text-right text-xs'
|
||||||
amount={demagnify(
|
amount={BN(
|
||||||
|
demagnify(
|
||||||
row.original.amount,
|
row.original.amount,
|
||||||
ASSETS.find((asset) => asset.denom === row.original.denom) ?? ASSETS[0],
|
ASSETS.find((asset) => asset.denom === row.original.denom) ?? ASSETS[0],
|
||||||
|
),
|
||||||
)}
|
)}
|
||||||
options={{ maxDecimals: 4 }}
|
options={{ maxDecimals: 4 }}
|
||||||
/>
|
/>
|
||||||
@ -121,7 +124,7 @@ export const AccountBalancesTable = (props: Props) => {
|
|||||||
return (
|
return (
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
className='text-xs'
|
className='text-xs'
|
||||||
amount={row.original.apy}
|
amount={BN(row.original.apy)}
|
||||||
options={{ maxDecimals: 2, minDecimals: 2, suffix: '%' }}
|
options={{ maxDecimals: 2, minDecimals: 2, suffix: '%' }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -90,7 +90,7 @@ function Item(props: ItemProps) {
|
|||||||
<div className='flex flex-grow items-center justify-end gap-2'>
|
<div className='flex flex-grow items-center justify-end gap-2'>
|
||||||
{props.isPercentage ? (
|
{props.isPercentage ? (
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
amount={props.current.toString()}
|
amount={props.current}
|
||||||
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }}
|
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }}
|
||||||
className='text-sm'
|
className='text-sm'
|
||||||
/>
|
/>
|
||||||
@ -107,7 +107,7 @@ function Item(props: ItemProps) {
|
|||||||
</span>
|
</span>
|
||||||
{props.isPercentage ? (
|
{props.isPercentage ? (
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
amount={props.change.toString()}
|
amount={props.change}
|
||||||
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }}
|
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }}
|
||||||
className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')}
|
className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')}
|
||||||
/>
|
/>
|
||||||
|
@ -13,17 +13,18 @@ import { FormattedNumber } from 'components/FormattedNumber'
|
|||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { formatValue } from 'utils/formatters'
|
import { formatValue } from 'utils/formatters'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
export const RiskChart = ({ data }: RiskChartProps) => {
|
export const RiskChart = ({ data }: RiskChartProps) => {
|
||||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||||
const accountStats = null
|
const accountStats = null
|
||||||
const currentRisk = 0
|
const currentRisk = BN(0)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex w-full flex-wrap overflow-hidden py-2'>
|
<div className='flex w-full flex-wrap overflow-hidden py-2'>
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
className='px-3 pb-2 text-lg'
|
className='px-3 pb-2 text-lg'
|
||||||
amount={currentRisk * 100}
|
amount={currentRisk.times(100)}
|
||||||
options={{
|
options={{
|
||||||
maxDecimals: 0,
|
maxDecimals: 0,
|
||||||
minDecimals: 0,
|
minDecimals: 0,
|
||||||
|
@ -10,7 +10,7 @@ export async function AccountDebtTable(props: Props) {
|
|||||||
return debtData.map((debt) => {
|
return debtData.map((debt) => {
|
||||||
return (
|
return (
|
||||||
<p key={debt.denom}>
|
<p key={debt.denom}>
|
||||||
{debt.denom} {debt.amount}
|
{debt.denom} {debt.amount.toString()}
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||||
import DisplayCurrency from 'components/DisplayCurrency'
|
import DisplayCurrency from 'components/DisplayCurrency'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
asset: Asset
|
asset: Asset
|
||||||
amount: string
|
amount: BigNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AmountAndValue(props: Props) {
|
export default function AmountAndValue(props: Props) {
|
||||||
@ -16,7 +17,11 @@ export default function AmountAndValue(props: Props) {
|
|||||||
options={{ decimals: props.asset.decimals, abbreviated: true }}
|
options={{ decimals: props.asset.decimals, abbreviated: true }}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
sub={<DisplayCurrency coin={{ amount: props.amount, denom: props.asset.denom }} />}
|
sub={
|
||||||
|
<DisplayCurrency
|
||||||
|
coin={new BNCoin({ amount: props.amount.toString(), denom: props.asset.denom })}
|
||||||
|
/>
|
||||||
|
}
|
||||||
className='justify-end'
|
className='justify-end'
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -7,7 +7,6 @@ import {
|
|||||||
useReactTable,
|
useReactTable,
|
||||||
} from '@tanstack/react-table'
|
} from '@tanstack/react-table'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import Image from 'next/image'
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import AmountAndValue from 'components/AmountAndValue'
|
import AmountAndValue from 'components/AmountAndValue'
|
||||||
|
@ -5,6 +5,7 @@ import { FormattedNumber } from 'components/FormattedNumber'
|
|||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import { Tooltip } from 'components/Tooltip'
|
import { Tooltip } from 'components/Tooltip'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
balance: number
|
balance: number
|
||||||
@ -68,7 +69,7 @@ export const BorrowCapacity = ({
|
|||||||
limitPercentOfMax ? 'opacity-50' : 'opacity-0',
|
limitPercentOfMax ? 'opacity-50' : 'opacity-0',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<FormattedNumber animate amount={limit} />
|
<FormattedNumber animate amount={BN(limit)} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -126,7 +127,7 @@ export const BorrowCapacity = ({
|
|||||||
maxDecimals: decimals,
|
maxDecimals: decimals,
|
||||||
suffix: '%',
|
suffix: '%',
|
||||||
}}
|
}}
|
||||||
amount={percentOfMaxRound}
|
amount={BN(percentOfMaxRound)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
@ -138,9 +139,9 @@ export const BorrowCapacity = ({
|
|||||||
</Tooltip>
|
</Tooltip>
|
||||||
{!hideValues && (
|
{!hideValues && (
|
||||||
<div className='mt-2 flex opacity-50 text-3xs-caps'>
|
<div className='mt-2 flex opacity-50 text-3xs-caps'>
|
||||||
<FormattedNumber animate amount={balance} className='mr-1' />
|
<FormattedNumber animate amount={BN(balance)} className='mr-1' />
|
||||||
<span className='mr-1'>of</span>
|
<span className='mr-1'>of</span>
|
||||||
<FormattedNumber animate amount={max} />
|
<FormattedNumber animate amount={BN(max)} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
import { convertToDisplayAmount } from 'utils/formatters'
|
import { convertToDisplayAmount } from 'utils/formatters'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
coin: Coin
|
coin: BNCoin | Coin
|
||||||
className?: string
|
className?: string
|
||||||
isApproximation?: boolean
|
isApproximation?: boolean
|
||||||
}
|
}
|
||||||
|
@ -64,7 +64,7 @@ function LendingMarketsTable(props: Props) {
|
|||||||
accessorKey: 'accountDepositValue',
|
accessorKey: 'accountDepositValue',
|
||||||
header: 'Deposited',
|
header: 'Deposited',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const accountDepositValue = (row.original.accountLentValue as BigNumber).toNumber()
|
const accountDepositValue = row.original.accountLentValue as BigNumber
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
|
@ -4,9 +4,10 @@ import { animated, useSpring } from 'react-spring'
|
|||||||
|
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { FormatOptions, formatValue } from 'utils/formatters'
|
import { FormatOptions, formatValue } from 'utils/formatters'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
amount: number | string
|
amount: BigNumber
|
||||||
options?: FormatOptions
|
options?: FormatOptions
|
||||||
className?: string
|
className?: string
|
||||||
animate?: boolean
|
animate?: boolean
|
||||||
@ -14,23 +15,23 @@ interface Props {
|
|||||||
|
|
||||||
export const FormattedNumber = React.memo((props: Props) => {
|
export const FormattedNumber = React.memo((props: Props) => {
|
||||||
const enableAnimations = useStore((s) => s.enableAnimations)
|
const enableAnimations = useStore((s) => s.enableAnimations)
|
||||||
const prevAmountRef = useRef<number>(0)
|
const prevAmountRef = useRef<BigNumber>(BN(0))
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (prevAmountRef.current !== Number(props.amount)) prevAmountRef.current = Number(props.amount)
|
if (!prevAmountRef.current.eq(props.amount)) prevAmountRef.current = props.amount
|
||||||
}, [props.amount])
|
}, [props.amount])
|
||||||
|
|
||||||
const springAmount = useSpring({
|
const springAmount = useSpring({
|
||||||
number: Number(props.amount),
|
number: props.amount.toNumber(),
|
||||||
from: { number: prevAmountRef.current },
|
from: { number: prevAmountRef.current.toNumber() },
|
||||||
config: { duration: 1000 },
|
config: { duration: 1000 },
|
||||||
})
|
})
|
||||||
|
|
||||||
return (prevAmountRef.current === props.amount && props.amount === 0) ||
|
return (prevAmountRef.current.eq(props.amount) && props.amount.isZero()) ||
|
||||||
!props.animate ||
|
!props.animate ||
|
||||||
!enableAnimations ? (
|
!enableAnimations ? (
|
||||||
<span className={classNames('number', props.className)}>
|
<span className={classNames('number', props.className)}>
|
||||||
{formatValue(props.amount, {
|
{formatValue(props.amount.toString(), {
|
||||||
minDecimals: props.options?.minDecimals,
|
minDecimals: props.options?.minDecimals,
|
||||||
maxDecimals: props.options?.maxDecimals,
|
maxDecimals: props.options?.maxDecimals,
|
||||||
thousandSeparator: props.options?.thousandSeparator,
|
thousandSeparator: props.options?.thousandSeparator,
|
||||||
|
@ -4,6 +4,7 @@ import { ReactElement, ReactNode } from 'react'
|
|||||||
import { Tooltip } from 'components/Tooltip'
|
import { Tooltip } from 'components/Tooltip'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
tooltip: string | ReactNode
|
tooltip: string | ReactNode
|
||||||
@ -87,7 +88,7 @@ export const Gauge = ({
|
|||||||
)}
|
)}
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
className={classNames(labelClassName, 'text-2xs')}
|
className={classNames(labelClassName, 'text-2xs')}
|
||||||
amount={Math.round(percentage)}
|
amount={BN(Math.round(percentage))}
|
||||||
options={{ maxDecimals: 0, minDecimals: 0 }}
|
options={{ maxDecimals: 0, minDecimals: 0 }}
|
||||||
animate
|
animate
|
||||||
/>
|
/>
|
||||||
|
@ -2,6 +2,7 @@ import classNames from 'classnames'
|
|||||||
|
|
||||||
import { FormattedNumber } from 'components/FormattedNumber'
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
interface ValueData extends FormattedNumberProps {
|
interface ValueData extends FormattedNumberProps {
|
||||||
format?: 'number' | 'string'
|
format?: 'number' | 'string'
|
||||||
@ -19,7 +20,11 @@ export const LabelValuePair = ({ label, value, className }: Props) => (
|
|||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
<Text size='xs' className='text-white/60'>
|
<Text size='xs' className='text-white/60'>
|
||||||
{value.format === 'number' ? <FormattedNumber animate {...value} /> : value.amount || ''}
|
{value.format === 'number' ? (
|
||||||
|
<FormattedNumber animate {...value} amount={BN(value.amount)} />
|
||||||
|
) : (
|
||||||
|
value.amount || ''
|
||||||
|
)}
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@ -6,27 +6,28 @@ import {
|
|||||||
SortingState,
|
SortingState,
|
||||||
useReactTable,
|
useReactTable,
|
||||||
} from '@tanstack/react-table'
|
} from '@tanstack/react-table'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useMemo, useState } from 'react'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
|
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import useStore from 'store'
|
|
||||||
import useAddVaultAssetTableColumns from 'components/Modals/AddVaultAssets/useAddVaultAssetTableColumns'
|
import useAddVaultAssetTableColumns from 'components/Modals/AddVaultAssets/useAddVaultAssetTableColumns'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
assets: BorrowAsset[]
|
assets: BorrowAsset[]
|
||||||
|
selectedDenoms: string[]
|
||||||
onChangeSelected: (denoms: string[]) => void
|
onChangeSelected: (denoms: string[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function AddVaultAssetTable(props: Props) {
|
export default function AddVaultAssetTable(props: Props) {
|
||||||
const selectedDenoms = useStore((s) => s.addVaultBorrowingsModal?.selectedDenoms) || []
|
const defaultSelected = useMemo(() => {
|
||||||
const defaultSelected = props.assets.reduce((acc, asset, index) => {
|
return props.assets.reduce((acc, asset, index) => {
|
||||||
if (selectedDenoms.includes(asset.denom)) {
|
if (props.selectedDenoms?.includes(asset.denom)) {
|
||||||
acc[index] = true
|
acc[index] = true
|
||||||
}
|
}
|
||||||
return acc
|
return acc
|
||||||
}, {} as { [key: number]: boolean })
|
}, {} as { [key: number]: boolean })
|
||||||
|
}, [props.selectedDenoms, props.assets])
|
||||||
|
|
||||||
const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }])
|
const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }])
|
||||||
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)
|
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)
|
||||||
@ -46,11 +47,16 @@ export default function AddVaultAssetTable(props: Props) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const selectedDenoms = props.assets
|
const newSelectedDenoms = props.assets
|
||||||
.filter((_, index) => selected[index])
|
.filter((_, index) => selected[index])
|
||||||
.map((asset) => asset.denom)
|
.map((asset) => asset.denom)
|
||||||
|
|
||||||
props.onChangeSelected(selectedDenoms)
|
if (
|
||||||
|
props.selectedDenoms.length === newSelectedDenoms.length &&
|
||||||
|
newSelectedDenoms.every((denom) => props.selectedDenoms.includes(denom))
|
||||||
|
)
|
||||||
|
return
|
||||||
|
props.onChangeSelected(newSelectedDenoms)
|
||||||
}, [selected, props])
|
}, [selected, props])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -4,6 +4,7 @@ import SearchBar from 'components/SearchBar'
|
|||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import useMarketBorrowings from 'hooks/useMarketBorrowings'
|
import useMarketBorrowings from 'hooks/useMarketBorrowings'
|
||||||
import AddVaultAssetTable from 'components/Modals/AddVaultAssets/AddVaultAssetTable'
|
import AddVaultAssetTable from 'components/Modals/AddVaultAssets/AddVaultAssetTable'
|
||||||
|
import useStore from 'store'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
vault: Vault
|
vault: Vault
|
||||||
@ -14,8 +15,6 @@ interface Props {
|
|||||||
export default function AddVaultAssetsModalContent(props: Props) {
|
export default function AddVaultAssetsModalContent(props: Props) {
|
||||||
const [searchString, setSearchString] = useState<string>('')
|
const [searchString, setSearchString] = useState<string>('')
|
||||||
const { data: borrowAssets } = useMarketBorrowings()
|
const { data: borrowAssets } = useMarketBorrowings()
|
||||||
const [selectedPoolDenoms, setSelectedPoolDenoms] = useState<string[]>([])
|
|
||||||
const [selectedOtherDenoms, setSelectedOtherDenoms] = useState<string[]>([])
|
|
||||||
|
|
||||||
const filteredBorrowAssets: BorrowAsset[] = useMemo(() => {
|
const filteredBorrowAssets: BorrowAsset[] = useMemo(() => {
|
||||||
return borrowAssets.filter(
|
return borrowAssets.filter(
|
||||||
@ -49,6 +48,15 @@ export default function AddVaultAssetsModalContent(props: Props) {
|
|||||||
[filteredBorrowAssets, props.vault.denoms.primary, props.vault.denoms.secondary],
|
[filteredBorrowAssets, props.vault.denoms.primary, props.vault.denoms.secondary],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const selectedDenoms = useStore((s) => s.addVaultBorrowingsModal?.selectedDenoms)
|
||||||
|
const [selectedPoolDenoms, setSelectedPoolDenoms] = useState<string[]>(
|
||||||
|
selectedDenoms?.filter((denom) => poolAssets.map((asset) => asset.denom).includes(denom)) || [],
|
||||||
|
)
|
||||||
|
const [selectedOtherDenoms, setSelectedOtherDenoms] = useState<string[]>(
|
||||||
|
selectedDenoms?.filter((denom) => stableAssets.map((asset) => asset.denom).includes(denom)) ||
|
||||||
|
[],
|
||||||
|
)
|
||||||
|
|
||||||
const onChangePoolDenoms = useCallback(
|
const onChangePoolDenoms = useCallback(
|
||||||
(denoms: string[]) => {
|
(denoms: string[]) => {
|
||||||
setSelectedPoolDenoms(denoms)
|
setSelectedPoolDenoms(denoms)
|
||||||
@ -81,7 +89,11 @@ export default function AddVaultAssetsModalContent(props: Props) {
|
|||||||
Leverage will be set at 50% for both assets by default
|
Leverage will be set at 50% for both assets by default
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<AddVaultAssetTable assets={poolAssets} onChangeSelected={onChangePoolDenoms} />
|
<AddVaultAssetTable
|
||||||
|
assets={poolAssets}
|
||||||
|
onChangeSelected={onChangePoolDenoms}
|
||||||
|
selectedDenoms={selectedPoolDenoms}
|
||||||
|
/>
|
||||||
<div className='p-4'>
|
<div className='p-4'>
|
||||||
<Text>Assets not in the liquidity pool</Text>
|
<Text>Assets not in the liquidity pool</Text>
|
||||||
<Text size='xs' className='mt-1 text-white/60'>
|
<Text size='xs' className='mt-1 text-white/60'>
|
||||||
@ -89,7 +101,11 @@ export default function AddVaultAssetsModalContent(props: Props) {
|
|||||||
these assets below.
|
these assets below.
|
||||||
</Text>
|
</Text>
|
||||||
</div>
|
</div>
|
||||||
<AddVaultAssetTable assets={stableAssets} onChangeSelected={onChangeOtherDenoms} />
|
<AddVaultAssetTable
|
||||||
|
assets={stableAssets}
|
||||||
|
onChangeSelected={onChangeOtherDenoms}
|
||||||
|
selectedDenoms={selectedOtherDenoms}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
|
@ -83,12 +83,12 @@ export default function BorrowModal() {
|
|||||||
useStore.setState({ borrowModal: null })
|
useStore.setState({ borrowModal: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
const liquidityAmountString = formatValue(modal?.marketData?.liquidity?.amount || 0, {
|
const liquidityAmountString = formatValue(modal?.marketData?.liquidity?.amount.toString() || 0, {
|
||||||
abbreviated: true,
|
abbreviated: true,
|
||||||
decimals: 6,
|
decimals: 6,
|
||||||
})
|
})
|
||||||
|
|
||||||
const liquidityValueString = formatValue(modal?.marketData?.liquidity?.value || 0, {
|
const liquidityValueString = formatValue(modal?.marketData?.liquidity?.value.toString() || 0, {
|
||||||
abbreviated: true,
|
abbreviated: true,
|
||||||
decimals: 6,
|
decimals: 6,
|
||||||
})
|
})
|
||||||
|
@ -23,7 +23,7 @@ function DetailsHeader({ data }: Props) {
|
|||||||
<FormattedNumber amount={assetApy} options={{ suffix: '%' }} />
|
<FormattedNumber amount={assetApy} options={{ suffix: '%' }} />
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
className='ml-2 text-xs'
|
className='ml-2 text-xs'
|
||||||
amount={assetApy / 365}
|
amount={assetApy.div(365)}
|
||||||
options={{ suffix: '%/day' }}
|
options={{ suffix: '%/day' }}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -7,7 +7,7 @@ import useCurrentAccount from 'hooks/useCurrentAccount'
|
|||||||
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
|
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
|
||||||
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
|
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
|
||||||
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
|
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
|
||||||
import AccountBalanceSettableCoin from 'types/classes/AccountBalanceSettableCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
function LendAndReclaimModalController() {
|
function LendAndReclaimModalController() {
|
||||||
const currentAccount = useCurrentAccount()
|
const currentAccount = useCurrentAccount()
|
||||||
@ -48,15 +48,12 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
|
|||||||
async (value: BigNumber, isMax: boolean) => {
|
async (value: BigNumber, isMax: boolean) => {
|
||||||
setIsConfirming(true)
|
setIsConfirming(true)
|
||||||
|
|
||||||
const coin = new AccountBalanceSettableCoin(
|
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value)
|
||||||
asset.denom,
|
|
||||||
value.integerValue().toString(),
|
|
||||||
isMax,
|
|
||||||
)
|
|
||||||
const options = {
|
const options = {
|
||||||
fee: hardcodedFee,
|
fee: hardcodedFee,
|
||||||
accountId: currentAccount.id,
|
accountId: currentAccount.id,
|
||||||
coin,
|
coin,
|
||||||
|
isMax,
|
||||||
}
|
}
|
||||||
await (isLendAction ? lend : reclaim)(options)
|
await (isLendAction ? lend : reclaim)(options)
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import useStore from 'store'
|
|||||||
import DisplayCurrency from 'components/DisplayCurrency'
|
import DisplayCurrency from 'components/DisplayCurrency'
|
||||||
import usePrice from 'hooks/usePrice'
|
import usePrice from 'hooks/usePrice'
|
||||||
import { BNCoin } from 'types/classes/BNCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import useDepositVault from 'hooks/broadcast/useDepositVault'
|
||||||
|
|
||||||
export interface VaultBorrowingsProps {
|
export interface VaultBorrowingsProps {
|
||||||
account: Account
|
account: Account
|
||||||
@ -26,6 +27,8 @@ export interface VaultBorrowingsProps {
|
|||||||
secondaryAmount: BigNumber
|
secondaryAmount: BigNumber
|
||||||
primaryAsset: Asset
|
primaryAsset: Asset
|
||||||
secondaryAsset: Asset
|
secondaryAsset: Asset
|
||||||
|
deposits: BNCoin[]
|
||||||
|
vault: Vault
|
||||||
onChangeBorrowings: (borrowings: BNCoin[]) => void
|
onChangeBorrowings: (borrowings: BNCoin[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,6 +39,13 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
|||||||
const secondaryPrice = usePrice(props.secondaryAsset.denom)
|
const secondaryPrice = usePrice(props.secondaryAsset.denom)
|
||||||
const baseCurrency = useStore((s) => s.baseCurrency)
|
const baseCurrency = useStore((s) => s.baseCurrency)
|
||||||
const vaultModal = useStore((s) => s.vaultModal)
|
const vaultModal = useStore((s) => s.vaultModal)
|
||||||
|
const depositIntoVault = useStore((s) => s.depositIntoVault)
|
||||||
|
|
||||||
|
const { actions: depositActions, fee: depositFee } = useDepositVault({
|
||||||
|
vault: props.vault,
|
||||||
|
deposits: props.deposits.filter((borrowing) => borrowing.amount.gt(0)),
|
||||||
|
borrowings: props.borrowings.filter((borrowing) => borrowing.amount.gt(0)),
|
||||||
|
})
|
||||||
|
|
||||||
const primaryValue = useMemo(
|
const primaryValue = useMemo(
|
||||||
() => props.primaryAmount.times(primaryPrice),
|
() => props.primaryAmount.times(primaryPrice),
|
||||||
@ -139,6 +149,10 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onConfirm() {
|
||||||
|
depositIntoVault({ fee: depositFee, accountId: props.account.id, actions: depositActions })
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex flex-grow flex-col gap-4 p-4'>
|
<div className='flex flex-grow flex-col gap-4 p-4'>
|
||||||
{props.borrowings.map((coin) => {
|
{props.borrowings.map((coin) => {
|
||||||
@ -191,7 +205,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<Button color='primary' text='Deposit' rightIcon={<ArrowRight />} />
|
<Button onClick={onConfirm} color='primary' text='Deposit' rightIcon={<ArrowRight />} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ export default function VaultDeposit(props: Props) {
|
|||||||
() =>
|
() =>
|
||||||
props.isCustomRatio
|
props.isCustomRatio
|
||||||
? availableSecondaryAmount
|
? availableSecondaryAmount
|
||||||
: maxAssetValueNonCustom.dividedBy(secondaryPrice),
|
: maxAssetValueNonCustom.dividedBy(secondaryPrice).decimalPlaces(0),
|
||||||
[props.isCustomRatio, availableSecondaryAmount, secondaryPrice, maxAssetValueNonCustom],
|
[props.isCustomRatio, availableSecondaryAmount, secondaryPrice, maxAssetValueNonCustom],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useMemo, useState } from 'react'
|
||||||
|
|
||||||
import Accordion from 'components/Accordion'
|
import Accordion from 'components/Accordion'
|
||||||
import AccountSummary from 'components/Account/AccountSummary'
|
import AccountSummary from 'components/Account/AccountSummary'
|
||||||
@ -10,6 +10,7 @@ import VaultBorrowingsSubTitle from 'components/Modals/Vault/VaultBorrowingsSubT
|
|||||||
import VaultDeposit from 'components/Modals/Vault/VaultDeposits'
|
import VaultDeposit from 'components/Modals/Vault/VaultDeposits'
|
||||||
import VaultBorrowings from 'components/Modals/Vault/VaultBorrowings'
|
import VaultBorrowings from 'components/Modals/Vault/VaultBorrowings'
|
||||||
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
|
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
vault: Vault
|
vault: Vault
|
||||||
@ -28,12 +29,24 @@ export default function VaultModalContent(props: Props) {
|
|||||||
const [secondaryAmount, setSecondaryAmount] = useState<BigNumber>(BN(0))
|
const [secondaryAmount, setSecondaryAmount] = useState<BigNumber>(BN(0))
|
||||||
const [isCustomRatio, setIsCustomRatio] = useState(false)
|
const [isCustomRatio, setIsCustomRatio] = useState(false)
|
||||||
|
|
||||||
|
const deposits: BNCoin[] = useMemo(() => {
|
||||||
|
const primaryBNCoin = new BNCoin({
|
||||||
|
denom: props.vault.denoms.primary,
|
||||||
|
amount: primaryAmount.toString(),
|
||||||
|
})
|
||||||
|
const secondaryBNCoin = new BNCoin({
|
||||||
|
denom: props.vault.denoms.secondary,
|
||||||
|
amount: secondaryAmount.toString(),
|
||||||
|
})
|
||||||
|
return [primaryBNCoin, secondaryBNCoin]
|
||||||
|
}, [primaryAmount, secondaryAmount, props.vault.denoms.primary, props.vault.denoms.secondary])
|
||||||
|
|
||||||
const onChangePrimaryAmount = useCallback(
|
const onChangePrimaryAmount = useCallback(
|
||||||
(amount: BigNumber) => setPrimaryAmount(amount),
|
(amount: BigNumber) => setPrimaryAmount(amount.decimalPlaces(0)),
|
||||||
[setPrimaryAmount],
|
[setPrimaryAmount],
|
||||||
)
|
)
|
||||||
const onChangeSecondaryAmount = useCallback(
|
const onChangeSecondaryAmount = useCallback(
|
||||||
(amount: BigNumber) => setSecondaryAmount(amount),
|
(amount: BigNumber) => setSecondaryAmount(amount.decimalPlaces(0)),
|
||||||
[setSecondaryAmount],
|
[setSecondaryAmount],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -84,6 +97,8 @@ export default function VaultModalContent(props: Props) {
|
|||||||
primaryAsset={props.primaryAsset}
|
primaryAsset={props.primaryAsset}
|
||||||
secondaryAsset={props.secondaryAsset}
|
secondaryAsset={props.secondaryAsset}
|
||||||
onChangeBorrowings={onChangeBorrowings}
|
onChangeBorrowings={onChangeBorrowings}
|
||||||
|
deposits={deposits}
|
||||||
|
vault={props.vault}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
title: 'Borrow',
|
title: 'Borrow',
|
||||||
|
@ -14,6 +14,7 @@ import Button from 'components/Button'
|
|||||||
import { ExclamationMarkTriangle, TrashBin } from 'components/Icons'
|
import { ExclamationMarkTriangle, TrashBin } from 'components/Icons'
|
||||||
import { Tooltip } from 'components/Tooltip'
|
import { Tooltip } from 'components/Tooltip'
|
||||||
import AssetImage from 'components/AssetImage'
|
import AssetImage from 'components/AssetImage'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
amount: BigNumber
|
amount: BigNumber
|
||||||
@ -108,7 +109,7 @@ export default function TokenInput(props: Props) {
|
|||||||
</Text>
|
</Text>
|
||||||
<FormattedNumber
|
<FormattedNumber
|
||||||
className='mr-1 text-xs text-white/50'
|
className='mr-1 text-xs text-white/50'
|
||||||
amount={props.max.toNumber()}
|
amount={props.max}
|
||||||
options={{ decimals: props.asset.decimals }}
|
options={{ decimals: props.asset.decimals }}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
@ -127,8 +128,8 @@ export default function TokenInput(props: Props) {
|
|||||||
<div className='flex'>
|
<div className='flex'>
|
||||||
<DisplayCurrency
|
<DisplayCurrency
|
||||||
isApproximation
|
isApproximation
|
||||||
className='inline pl-0.5 text-xs text-white/50'
|
className='inline pl-1 text-xs text-white/50'
|
||||||
coin={{ denom: props.asset.denom, amount: props.amount.toString() }}
|
coin={new BNCoin({ denom: props.asset.denom, amount: props.amount.toString() })}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -21,6 +21,7 @@ import useStore from 'store'
|
|||||||
import { getBaseAsset, getEnabledMarketAssets } from 'utils/assets'
|
import { getBaseAsset, getEnabledMarketAssets } from 'utils/assets'
|
||||||
import { formatValue, truncate } from 'utils/formatters'
|
import { formatValue, truncate } from 'utils/formatters'
|
||||||
import useWalletBalances from 'hooks/useWalletBalances'
|
import useWalletBalances from 'hooks/useWalletBalances'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
export default function ConnectedButton() {
|
export default function ConnectedButton() {
|
||||||
// ---------------
|
// ---------------
|
||||||
@ -38,7 +39,7 @@ export default function ConnectedButton() {
|
|||||||
// LOCAL STATE
|
// LOCAL STATE
|
||||||
// ---------------
|
// ---------------
|
||||||
const [showDetails, setShowDetails] = useToggle()
|
const [showDetails, setShowDetails] = useToggle()
|
||||||
const [walletAmount, setWalletAmount] = useState(0)
|
const [walletAmount, setWalletAmount] = useState(BN(0))
|
||||||
const [isCopied, setCopied] = useClipboard(address || '', {
|
const [isCopied, setCopied] = useClipboard(address || '', {
|
||||||
successDuration: 1000 * 5,
|
successDuration: 1000 * 5,
|
||||||
})
|
})
|
||||||
@ -62,9 +63,9 @@ export default function ConnectedButton() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!walletBalances || walletBalances.length === 0) return
|
if (!walletBalances || walletBalances.length === 0) return
|
||||||
setWalletAmount(
|
setWalletAmount(
|
||||||
BigNumber(walletBalances?.find((coin: Coin) => coin.denom === baseAsset.denom)?.amount ?? 0)
|
BigNumber(
|
||||||
.div(10 ** baseAsset.decimals)
|
walletBalances?.find((coin: Coin) => coin.denom === baseAsset.denom)?.amount ?? 0,
|
||||||
.toNumber(),
|
).div(10 ** baseAsset.decimals),
|
||||||
)
|
)
|
||||||
|
|
||||||
const assetDenoms = marketAssets.map((asset) => asset.denom)
|
const assetDenoms = marketAssets.map((asset) => asset.denom)
|
||||||
@ -103,7 +104,7 @@ export default function ConnectedButton() {
|
|||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
<CircularProgress size={12} />
|
<CircularProgress size={12} />
|
||||||
) : (
|
) : (
|
||||||
`${formatValue(walletAmount, { suffix: ` ${baseAsset.symbol}` })}`
|
`${formatValue(walletAmount.toString(), { suffix: ` ${baseAsset.symbol}` })}`
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Button>
|
</Button>
|
||||||
|
81
src/hooks/broadcast/useDepositVault.ts
Normal file
81
src/hooks/broadcast/useDepositVault.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import debounce from 'debounce-promise'
|
||||||
|
import { useMemo, useState } from 'react'
|
||||||
|
|
||||||
|
import { hardcodedFee } from 'utils/constants'
|
||||||
|
import getMinLpToReceive from 'api/vaults/getMinLpToReceive'
|
||||||
|
import usePrices from 'hooks/usePrices'
|
||||||
|
import useStore from 'store'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||||
|
import {
|
||||||
|
getEnterVaultActions,
|
||||||
|
getVaultDepositCoinsAndValue,
|
||||||
|
getVaultSwapActions,
|
||||||
|
} from 'utils/vaults'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
vault: Vault
|
||||||
|
deposits: BNCoin[]
|
||||||
|
borrowings: BNCoin[]
|
||||||
|
}
|
||||||
|
export default function useDepositVault(props: Props): { actions: Action[]; fee: StdFee } {
|
||||||
|
const [minLpToReceive, setMinLpToReceive] = useState<BigNumber>(BN(0))
|
||||||
|
const { data: prices } = usePrices()
|
||||||
|
const slippage = useStore((s) => s.slippage)
|
||||||
|
|
||||||
|
const debouncedGetMinLpToReceive = useMemo(() => debounce(getMinLpToReceive, 500), [])
|
||||||
|
|
||||||
|
const { primaryCoin, secondaryCoin, totalValue } = useMemo(
|
||||||
|
() => getVaultDepositCoinsAndValue(props.vault, props.deposits, props.borrowings, prices),
|
||||||
|
[props.deposits, props.borrowings, props.vault, prices],
|
||||||
|
)
|
||||||
|
|
||||||
|
const borrowActions: Action[] = useMemo(() => {
|
||||||
|
return props.borrowings.map((bnCoin) => ({
|
||||||
|
borrow: bnCoin.toCoin(),
|
||||||
|
}))
|
||||||
|
}, [props.borrowings])
|
||||||
|
|
||||||
|
const swapActions: Action[] = useMemo(
|
||||||
|
() =>
|
||||||
|
getVaultSwapActions(
|
||||||
|
props.vault,
|
||||||
|
props.deposits,
|
||||||
|
props.borrowings,
|
||||||
|
prices,
|
||||||
|
slippage,
|
||||||
|
totalValue,
|
||||||
|
),
|
||||||
|
[totalValue, prices, props.vault, props.deposits, props.borrowings, slippage],
|
||||||
|
)
|
||||||
|
|
||||||
|
useMemo(async () => {
|
||||||
|
if (primaryCoin.amount.isZero() || secondaryCoin.amount.isZero()) return
|
||||||
|
|
||||||
|
const lpAmount = await debouncedGetMinLpToReceive(
|
||||||
|
[secondaryCoin.toCoin(), primaryCoin.toCoin()],
|
||||||
|
props.vault.denoms.lp,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!lpAmount || lpAmount === minLpToReceive) return
|
||||||
|
setMinLpToReceive(lpAmount)
|
||||||
|
}, [
|
||||||
|
primaryCoin,
|
||||||
|
secondaryCoin,
|
||||||
|
props.vault.denoms.lp,
|
||||||
|
debouncedGetMinLpToReceive,
|
||||||
|
minLpToReceive,
|
||||||
|
])
|
||||||
|
|
||||||
|
const enterVaultActions: Action[] = useMemo(() => {
|
||||||
|
return getEnterVaultActions(props.vault, primaryCoin, secondaryCoin, minLpToReceive)
|
||||||
|
}, [props.vault, primaryCoin, secondaryCoin, minLpToReceive])
|
||||||
|
|
||||||
|
const actions = useMemo(
|
||||||
|
() => [...borrowActions, ...swapActions, ...enterVaultActions],
|
||||||
|
[borrowActions, swapActions, enterVaultActions],
|
||||||
|
)
|
||||||
|
|
||||||
|
return { actions, fee: hardcodedFee }
|
||||||
|
}
|
14
src/hooks/useMinLpToReceive.tsx
Normal file
14
src/hooks/useMinLpToReceive.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import useSWR from 'swr'
|
||||||
|
|
||||||
|
import getMinLpToReceive from 'api/vaults/getMinLpToReceive'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
|
export default function useMinLpToReceive(coins: Coin[], lpDenom: string) {
|
||||||
|
return useSWR(
|
||||||
|
`minLpToReceive-${JSON.stringify(coins)}`,
|
||||||
|
() => getMinLpToReceive(coins, lpDenom),
|
||||||
|
{
|
||||||
|
fallbackData: BN(0),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
@ -6,7 +6,8 @@ import { ENV } from 'constants/env'
|
|||||||
import { Store } from 'store'
|
import { Store } from 'store'
|
||||||
import { getSingleValueFromBroadcastResult } from 'utils/broadcast'
|
import { getSingleValueFromBroadcastResult } from 'utils/broadcast'
|
||||||
import { formatAmountWithSymbol } from 'utils/formatters'
|
import { formatAmountWithSymbol } from 'utils/formatters'
|
||||||
import AccountBalanceSettableCoin from 'types/classes/AccountBalanceSettableCoin'
|
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
export default function createBroadcastSlice(
|
export default function createBroadcastSlice(
|
||||||
set: SetState<Store>,
|
set: SetState<Store>,
|
||||||
@ -127,6 +128,30 @@ export default function createBroadcastSlice(
|
|||||||
handleResponseMessages(response, `Requested unlock for ${options.vault.name}`)
|
handleResponseMessages(response, `Requested unlock for ${options.vault.name}`)
|
||||||
return !!response.result
|
return !!response.result
|
||||||
},
|
},
|
||||||
|
depositIntoVault: async (options: { fee: StdFee; accountId: string; actions: Action[] }) => {
|
||||||
|
const msg = {
|
||||||
|
update_credit_account: {
|
||||||
|
account_id: options.accountId,
|
||||||
|
actions: options.actions,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const response = await get().executeMsg({ msg, fee: options.fee })
|
||||||
|
if (response.result) {
|
||||||
|
set({
|
||||||
|
toast: {
|
||||||
|
message: `Deposited into vault`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
set({
|
||||||
|
toast: {
|
||||||
|
message: response.error ?? `Transaction failed: ${response.error}`,
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return !!response.result
|
||||||
|
},
|
||||||
withdraw: async (options: { fee: StdFee; accountId: string; coin: Coin }) => {
|
withdraw: async (options: { fee: StdFee; accountId: string; coin: Coin }) => {
|
||||||
const msg = {
|
const msg = {
|
||||||
update_credit_account: {
|
update_credit_account: {
|
||||||
@ -213,13 +238,13 @@ export default function createBroadcastSlice(
|
|||||||
)
|
)
|
||||||
return !!response.result
|
return !!response.result
|
||||||
},
|
},
|
||||||
lend: async (options: { fee: StdFee; accountId: string; coin: AccountBalanceSettableCoin }) => {
|
lend: async (options: { fee: StdFee; accountId: string; coin: BNCoin; isMax?: boolean }) => {
|
||||||
const msg = {
|
const msg = {
|
||||||
update_credit_account: {
|
update_credit_account: {
|
||||||
account_id: options.accountId,
|
account_id: options.accountId,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
lend: options.coin,
|
lend: options.coin.toActionCoin(options.isMax),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -229,21 +254,17 @@ export default function createBroadcastSlice(
|
|||||||
|
|
||||||
handleResponseMessages(
|
handleResponseMessages(
|
||||||
response,
|
response,
|
||||||
`Successfully deposited ${formatAmountWithSymbol(options.coin)}`,
|
`Successfully deposited ${formatAmountWithSymbol(options.coin.toCoin())}`,
|
||||||
)
|
)
|
||||||
return !!response.result
|
return !!response.result
|
||||||
},
|
},
|
||||||
reclaim: async (options: {
|
reclaim: async (options: { fee: StdFee; accountId: string; coin: BNCoin; isMax?: boolean }) => {
|
||||||
fee: StdFee
|
|
||||||
accountId: string
|
|
||||||
coin: AccountBalanceSettableCoin
|
|
||||||
}) => {
|
|
||||||
const msg = {
|
const msg = {
|
||||||
update_credit_account: {
|
update_credit_account: {
|
||||||
account_id: options.accountId,
|
account_id: options.accountId,
|
||||||
actions: [
|
actions: [
|
||||||
{
|
{
|
||||||
reclaim: options.coin.toActionCoin(),
|
reclaim: options.coin.toActionCoin(options.isMax),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -253,7 +274,7 @@ export default function createBroadcastSlice(
|
|||||||
|
|
||||||
handleResponseMessages(
|
handleResponseMessages(
|
||||||
response,
|
response,
|
||||||
`Successfully withdrew ${formatAmountWithSymbol(options.coin)}`,
|
`Successfully withdrew ${formatAmountWithSymbol(options.coin.toCoin())}`,
|
||||||
)
|
)
|
||||||
return !!response.result
|
return !!response.result
|
||||||
},
|
},
|
||||||
|
@ -9,6 +9,7 @@ export default function createCommonSlice(set: SetState<CommonSlice>, get: GetSt
|
|||||||
enableAnimations: true,
|
enableAnimations: true,
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
selectedAccount: null,
|
selectedAccount: null,
|
||||||
|
slippage: 0.02,
|
||||||
status: WalletConnectionStatus.Unconnected,
|
status: WalletConnectionStatus.Unconnected,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import { ActionCoin } from 'types/generated'
|
|
||||||
|
|
||||||
class AccountBalanceSettableCoin implements Coin {
|
|
||||||
public denom: string
|
|
||||||
public amount: string
|
|
||||||
public setAccountBalance: boolean
|
|
||||||
|
|
||||||
constructor(denom: string, amount: string, setAccountBalance: boolean) {
|
|
||||||
this.denom = denom
|
|
||||||
this.amount = amount
|
|
||||||
this.setAccountBalance = setAccountBalance
|
|
||||||
}
|
|
||||||
|
|
||||||
toActionCoin(): ActionCoin {
|
|
||||||
return {
|
|
||||||
denom: this.denom,
|
|
||||||
amount: this.setAccountBalance ? 'account_balance' : { exact: this.amount },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default AccountBalanceSettableCoin
|
|
@ -1,3 +1,4 @@
|
|||||||
|
import { ActionCoin } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||||
import { BN } from 'utils/helpers'
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
export class BNCoin {
|
export class BNCoin {
|
||||||
@ -9,10 +10,25 @@ export class BNCoin {
|
|||||||
this.amount = BN(coin.amount)
|
this.amount = BN(coin.amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromDenomAndBigNumber(denom: string, amount: BigNumber) {
|
||||||
|
return new BNCoin({ denom, amount: amount.toString() })
|
||||||
|
}
|
||||||
|
|
||||||
toCoin(): Coin {
|
toCoin(): Coin {
|
||||||
return {
|
return {
|
||||||
denom: this.denom,
|
denom: this.denom,
|
||||||
amount: this.amount.toString(),
|
amount: this.amount.toString(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toActionCoin(max?: boolean): ActionCoin {
|
||||||
|
return {
|
||||||
|
denom: this.denom,
|
||||||
|
amount: max
|
||||||
|
? 'account_balance'
|
||||||
|
: {
|
||||||
|
exact: this.amount.toString(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,19 +5,20 @@
|
|||||||
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
|
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from '@cosmjs/cosmwasm-stargate'
|
import { CosmWasmClient, ExecuteResult, SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||||
import { Coin, StdFee } from '@cosmjs/amino'
|
import { Coin, StdFee } from '@cosmjs/amino'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
InstantiateMsg,
|
|
||||||
ExecuteMsg,
|
|
||||||
Uint128,
|
|
||||||
Addr,
|
Addr,
|
||||||
|
ArrayOfAssetIncentiveResponse,
|
||||||
|
AssetIncentiveResponse,
|
||||||
|
ConfigResponse,
|
||||||
|
Decimal,
|
||||||
|
ExecuteMsg,
|
||||||
|
InstantiateMsg,
|
||||||
OwnerUpdate,
|
OwnerUpdate,
|
||||||
QueryMsg,
|
QueryMsg,
|
||||||
Decimal,
|
Uint128,
|
||||||
AssetIncentiveResponse,
|
|
||||||
ArrayOfAssetIncentiveResponse,
|
|
||||||
ConfigResponse,
|
|
||||||
} from './MarsIncentives.types'
|
} from './MarsIncentives.types'
|
||||||
export interface MarsIncentivesReadOnlyInterface {
|
export interface MarsIncentivesReadOnlyInterface {
|
||||||
contractAddress: string
|
contractAddress: string
|
||||||
|
@ -5,22 +5,23 @@
|
|||||||
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
|
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { UseQueryOptions, useQuery, useMutation, UseMutationOptions } from '@tanstack/react-query'
|
import { useMutation, UseMutationOptions, useQuery, UseQueryOptions } from '@tanstack/react-query'
|
||||||
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
|
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
|
||||||
import { StdFee, Coin } from '@cosmjs/amino'
|
import { Coin, StdFee } from '@cosmjs/amino'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
InstantiateMsg,
|
|
||||||
ExecuteMsg,
|
|
||||||
Uint128,
|
|
||||||
Addr,
|
Addr,
|
||||||
|
ArrayOfAssetIncentiveResponse,
|
||||||
|
AssetIncentiveResponse,
|
||||||
|
ConfigResponse,
|
||||||
|
Decimal,
|
||||||
|
ExecuteMsg,
|
||||||
|
InstantiateMsg,
|
||||||
OwnerUpdate,
|
OwnerUpdate,
|
||||||
QueryMsg,
|
QueryMsg,
|
||||||
Decimal,
|
Uint128,
|
||||||
AssetIncentiveResponse,
|
|
||||||
ArrayOfAssetIncentiveResponse,
|
|
||||||
ConfigResponse,
|
|
||||||
} from './MarsIncentives.types'
|
} from './MarsIncentives.types'
|
||||||
import { MarsIncentivesQueryClient, MarsIncentivesClient } from './MarsIncentives.client'
|
import { MarsIncentivesClient, MarsIncentivesQueryClient } from './MarsIncentives.client'
|
||||||
export const marsIncentivesQueryKeys = {
|
export const marsIncentivesQueryKeys = {
|
||||||
contract: [
|
contract: [
|
||||||
{
|
{
|
||||||
|
12
src/types/interfaces/account.d.ts
vendored
12
src/types/interfaces/account.d.ts
vendored
@ -1,15 +1,15 @@
|
|||||||
interface Account extends AccountChange {
|
interface Account extends AccountChange {
|
||||||
id: string
|
id: string
|
||||||
deposits: Coin[]
|
deposits: BNCoin[]
|
||||||
debts: Coin[]
|
debts: BNCoin[]
|
||||||
lends: Coin[]
|
lends: BNCoin[]
|
||||||
vaults: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
|
vaults: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AccountChange {
|
interface AccountChange {
|
||||||
deposits?: Coin[]
|
deposits?: BNCoin[]
|
||||||
debts?: Coin[]
|
debts?: BNCoin[]
|
||||||
lends?: Coin[]
|
lends?: BNCoin[]
|
||||||
vaults?: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
|
vaults?: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
|
6
src/types/interfaces/asset.d.ts
vendored
6
src/types/interfaces/asset.d.ts
vendored
@ -23,13 +23,13 @@ interface OtherAsset extends Omit<Asset, 'symbol'> {
|
|||||||
interface BorrowAsset extends Asset {
|
interface BorrowAsset extends Asset {
|
||||||
borrowRate: number | null
|
borrowRate: number | null
|
||||||
liquidity: {
|
liquidity: {
|
||||||
amount: string
|
amount: BigNumber
|
||||||
value: string
|
value: BigNumber
|
||||||
} | null
|
} | null
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BorrowAssetActive extends BorrowAsset {
|
interface BorrowAssetActive extends BorrowAsset {
|
||||||
debt: string
|
debt: BigNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BigNumberCoin {
|
interface BigNumberCoin {
|
||||||
|
13
src/types/interfaces/store/broadcast.d.ts
vendored
13
src/types/interfaces/store/broadcast.d.ts
vendored
@ -1,5 +1,3 @@
|
|||||||
type AccountBalanceSettableCoin = import('types/classes/AccountBalanceSettableCoin')
|
|
||||||
|
|
||||||
interface BroadcastResult {
|
interface BroadcastResult {
|
||||||
result?: import('@marsprotocol/wallet-connector').TxBroadcastResult
|
result?: import('@marsprotocol/wallet-connector').TxBroadcastResult
|
||||||
error?: string
|
error?: string
|
||||||
@ -17,16 +15,23 @@ interface BroadcastSlice {
|
|||||||
deleteAccount: (options: { fee: StdFee; accountId: string }) => Promise<boolean>
|
deleteAccount: (options: { fee: StdFee; accountId: string }) => Promise<boolean>
|
||||||
deposit: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
|
deposit: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
|
||||||
unlock: (options: { fee: StdFee; vault: Vault; amount: string }) => Promise<boolean>
|
unlock: (options: { fee: StdFee; vault: Vault; amount: string }) => Promise<boolean>
|
||||||
|
depositIntoVault: (options: {
|
||||||
|
fee: StdFee
|
||||||
|
accountId: string
|
||||||
|
actions: Action[]
|
||||||
|
}) => Promise<boolean>
|
||||||
withdraw: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
|
withdraw: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
|
||||||
lend: (options: {
|
lend: (options: {
|
||||||
fee: StdFee
|
fee: StdFee
|
||||||
accountId: string
|
accountId: string
|
||||||
coin: AccountBalanceSettableCoin
|
coin: BNCoin
|
||||||
|
isMax?: boolean
|
||||||
}) => Promise<boolean>
|
}) => Promise<boolean>
|
||||||
reclaim: (options: {
|
reclaim: (options: {
|
||||||
fee: StdFee
|
fee: StdFee
|
||||||
accountId: string
|
accountId: string
|
||||||
coin: AccountBalanceSettableCoin
|
coin: BNCoin
|
||||||
|
isMax?: boolean
|
||||||
}) => Promise<boolean>
|
}) => Promise<boolean>
|
||||||
repay: (options: {
|
repay: (options: {
|
||||||
fee: StdFee
|
fee: StdFee
|
||||||
|
1
src/types/interfaces/store/common.d.ts
vendored
1
src/types/interfaces/store/common.d.ts
vendored
@ -6,5 +6,6 @@ interface CommonSlice {
|
|||||||
enableAnimations: boolean
|
enableAnimations: boolean
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
selectedAccount: string | null
|
selectedAccount: string | null
|
||||||
|
slippage: number
|
||||||
status: import('@marsprotocol/wallet-connector').WalletConnectionStatus
|
status: import('@marsprotocol/wallet-connector').WalletConnectionStatus
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ export const calculateAccountDebt = (
|
|||||||
if (!account.debts) return BN(0)
|
if (!account.debts) return BN(0)
|
||||||
return account.debts.reduce((acc, debt) => {
|
return account.debts.reduce((acc, debt) => {
|
||||||
const price = prices.find((price) => price.denom === debt.denom)?.amount ?? 0
|
const price = prices.find((price) => price.denom === debt.denom)?.amount ?? 0
|
||||||
const debtValue = BN(debt.amount).multipliedBy(price)
|
const debtValue = debt.amount.multipliedBy(price)
|
||||||
return acc.plus(debtValue)
|
return acc.plus(debtValue)
|
||||||
}, BN(0))
|
}, BN(0))
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
|
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
import { getEnabledMarketAssets } from 'utils/assets'
|
import { getEnabledMarketAssets } from 'utils/assets'
|
||||||
import { BN } from 'utils/helpers'
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
@ -155,18 +156,21 @@ export function demagnify(amount: number | string | BigNumber, asset: Asset) {
|
|||||||
return value.isZero() ? 0 : value.shiftedBy(-1 * asset.decimals).toNumber()
|
return value.isZero() ? 0 : value.shiftedBy(-1 * asset.decimals).toNumber()
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToDisplayAmount(coin: Coin, displayCurrency: Asset, prices: Coin[]) {
|
export function convertToDisplayAmount(
|
||||||
|
coin: BNCoin | Coin,
|
||||||
|
displayCurrency: Asset,
|
||||||
|
prices: Coin[],
|
||||||
|
) {
|
||||||
const price = prices.find((price) => price.denom === coin.denom)
|
const price = prices.find((price) => price.denom === coin.denom)
|
||||||
const asset = getEnabledMarketAssets().find((asset) => asset.denom === coin.denom)
|
const asset = getEnabledMarketAssets().find((asset) => asset.denom === coin.denom)
|
||||||
const displayPrice = prices.find((price) => price.denom === displayCurrency.denom)
|
const displayPrice = prices.find((price) => price.denom === displayCurrency.denom)
|
||||||
|
|
||||||
if (!price || !asset || !displayPrice) return '0'
|
if (!price || !asset || !displayPrice) return BN(0)
|
||||||
|
|
||||||
return BN(coin.amount)
|
return BN(coin.amount)
|
||||||
.shiftedBy(-1 * asset.decimals)
|
.shiftedBy(-1 * asset.decimals)
|
||||||
.times(price.amount)
|
.times(price.amount)
|
||||||
.div(displayPrice.amount)
|
.div(displayPrice.amount)
|
||||||
.toNumber()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertLiquidityRateToAPR(rate: number) {
|
export function convertLiquidityRateToAPR(rate: number) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import BigNumber from 'bignumber.js'
|
import BigNumber from 'bignumber.js'
|
||||||
|
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
import { getBaseAsset } from 'utils/assets'
|
import { getBaseAsset } from 'utils/assets'
|
||||||
import { BN } from 'utils/helpers'
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
@ -15,7 +16,12 @@ export const getTokenIcon = (denom: string, marketAssets: Asset[]) =>
|
|||||||
export const getTokenInfo = (denom: string, marketAssets: Asset[]) =>
|
export const getTokenInfo = (denom: string, marketAssets: Asset[]) =>
|
||||||
marketAssets.find((asset) => asset.denom.toLowerCase() === denom.toLowerCase()) || getBaseAsset()
|
marketAssets.find((asset) => asset.denom.toLowerCase() === denom.toLowerCase()) || getBaseAsset()
|
||||||
|
|
||||||
export function getTokenValue(coin: Coin, prices: Coin[]): BigNumber {
|
export function getTokenValue(coin: BNCoin, prices: Coin[]): BigNumber {
|
||||||
const price = prices.find((price) => price.denom === coin.denom)?.amount || '0'
|
const price = prices.find((price) => price.denom === coin.denom)?.amount || '0'
|
||||||
return BN(price).times(coin.amount)
|
return BN(price).times(coin.amount).decimalPlaces(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getTokenPrice(denom: string, prices: Coin[]): BigNumber {
|
||||||
|
const price = prices.find((price) => price.denom === denom)?.amount || '0'
|
||||||
|
return BN(price)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,8 @@ import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults'
|
|||||||
import { BN } from 'utils/helpers'
|
import { BN } from 'utils/helpers'
|
||||||
import { getNetCollateralValue } from 'utils/accounts'
|
import { getNetCollateralValue } from 'utils/accounts'
|
||||||
import { BNCoin } from 'types/classes/BNCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||||
|
import { getTokenPrice, getTokenValue } from 'utils/tokens'
|
||||||
|
|
||||||
export function getVaultMetaData(address: string) {
|
export function getVaultMetaData(address: string) {
|
||||||
const vaults = IS_TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA
|
const vaults = IS_TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA
|
||||||
@ -33,3 +35,160 @@ export function calculateMaxBorrowAmounts(
|
|||||||
|
|
||||||
return maxAmounts
|
return maxAmounts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getVaultDepositCoinsAndValue(
|
||||||
|
vault: Vault,
|
||||||
|
deposits: BNCoin[],
|
||||||
|
borrowings: BNCoin[],
|
||||||
|
prices: Coin[],
|
||||||
|
) {
|
||||||
|
const totalValue = [...deposits, ...borrowings].reduce((prev, bnCoin) => {
|
||||||
|
const price = prices.find((coin) => coin.denom === bnCoin.denom)?.amount
|
||||||
|
if (!price) return prev
|
||||||
|
|
||||||
|
return prev.plus(bnCoin.amount.times(price))
|
||||||
|
}, BN(0))
|
||||||
|
|
||||||
|
const primaryDepositAmount = getTokenPrice(vault.denoms.primary, prices)
|
||||||
|
.times(totalValue)
|
||||||
|
.div(2)
|
||||||
|
.integerValue()
|
||||||
|
|
||||||
|
const secondaryDepositAmount = getTokenPrice(vault.denoms.secondary, prices)
|
||||||
|
.times(totalValue)
|
||||||
|
.div(2)
|
||||||
|
.integerValue()
|
||||||
|
|
||||||
|
return {
|
||||||
|
primaryCoin: new BNCoin({
|
||||||
|
denom: vault.denoms.primary,
|
||||||
|
amount: primaryDepositAmount.toString(),
|
||||||
|
}),
|
||||||
|
secondaryCoin: new BNCoin({
|
||||||
|
denom: vault.denoms.secondary,
|
||||||
|
amount: secondaryDepositAmount.toString(),
|
||||||
|
}),
|
||||||
|
totalValue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getVaultSwapActions(
|
||||||
|
vault: Vault,
|
||||||
|
deposits: BNCoin[],
|
||||||
|
borrowings: BNCoin[],
|
||||||
|
prices: Coin[],
|
||||||
|
slippage: number,
|
||||||
|
totalValue: BigNumber,
|
||||||
|
): Action[] {
|
||||||
|
const swapActions: Action[] = []
|
||||||
|
const coins = [...deposits, ...borrowings]
|
||||||
|
|
||||||
|
let primaryLeftoverValue = totalValue.div(2).integerValue()
|
||||||
|
let secondaryLeftoverValue = totalValue.div(2).integerValue()
|
||||||
|
|
||||||
|
const [primaryCoins, secondaryCoins, otherCoins] = coins.reduce(
|
||||||
|
(prev, bnCoin) => {
|
||||||
|
switch (bnCoin.denom) {
|
||||||
|
case vault.denoms.primary:
|
||||||
|
prev[0].push(bnCoin)
|
||||||
|
break
|
||||||
|
case vault.denoms.secondary:
|
||||||
|
prev[1].push(bnCoin)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
prev[2].push(bnCoin)
|
||||||
|
}
|
||||||
|
return prev
|
||||||
|
},
|
||||||
|
[[], [], []] as [BNCoin[], BNCoin[], BNCoin[]],
|
||||||
|
)
|
||||||
|
|
||||||
|
primaryCoins.forEach((bnCoin) => {
|
||||||
|
let value = getTokenValue(bnCoin, prices)
|
||||||
|
if (value.isLessThanOrEqualTo(primaryLeftoverValue)) {
|
||||||
|
primaryLeftoverValue = primaryLeftoverValue.minus(value)
|
||||||
|
} else {
|
||||||
|
value = value.minus(primaryLeftoverValue)
|
||||||
|
primaryLeftoverValue = primaryLeftoverValue.minus(primaryLeftoverValue)
|
||||||
|
otherCoins.push(new BNCoin({ denom: bnCoin.denom, amount: value.toString() }))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
secondaryCoins.forEach((bnCoin) => {
|
||||||
|
let value = getTokenValue(bnCoin, prices)
|
||||||
|
if (value.isLessThanOrEqualTo(secondaryLeftoverValue)) {
|
||||||
|
secondaryLeftoverValue = secondaryLeftoverValue.minus(value)
|
||||||
|
} else {
|
||||||
|
value = value.minus(secondaryLeftoverValue)
|
||||||
|
secondaryLeftoverValue = secondaryLeftoverValue.minus(secondaryLeftoverValue)
|
||||||
|
otherCoins.push(new BNCoin({ denom: bnCoin.denom, amount: value.toString() }))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
otherCoins.forEach((bnCoin) => {
|
||||||
|
let value = getTokenValue(bnCoin, prices)
|
||||||
|
let amount = bnCoin.amount
|
||||||
|
|
||||||
|
if (primaryLeftoverValue.isGreaterThan(0)) {
|
||||||
|
const swapValue = value.isLessThan(primaryLeftoverValue) ? value : primaryLeftoverValue
|
||||||
|
const swapAmount = swapValue
|
||||||
|
.dividedBy(prices.find((coin) => coin.denom === bnCoin.denom)?.amount || 1)
|
||||||
|
.integerValue()
|
||||||
|
value = value.minus(swapValue)
|
||||||
|
amount = amount.minus(swapAmount)
|
||||||
|
primaryLeftoverValue = primaryLeftoverValue.minus(swapValue)
|
||||||
|
swapActions.push(getSwapAction(bnCoin.denom, vault.denoms.primary, swapAmount, slippage))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (secondaryLeftoverValue.isGreaterThan(0)) {
|
||||||
|
secondaryLeftoverValue = secondaryLeftoverValue.minus(value)
|
||||||
|
swapActions.push(getSwapAction(bnCoin.denom, vault.denoms.secondary, amount, slippage))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return swapActions
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getEnterVaultActions(
|
||||||
|
vault: Vault,
|
||||||
|
primaryCoin: BNCoin,
|
||||||
|
secondaryCoin: BNCoin,
|
||||||
|
minLpToReceive: BigNumber,
|
||||||
|
): Action[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
provide_liquidity: {
|
||||||
|
// Smart Contact demands that secondary coin is first
|
||||||
|
coins_in: [secondaryCoin.toActionCoin(), primaryCoin.toActionCoin()],
|
||||||
|
lp_token_out: vault.denoms.lp,
|
||||||
|
minimum_receive: minLpToReceive.toString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enter_vault: {
|
||||||
|
coin: {
|
||||||
|
denom: vault.denoms.lp,
|
||||||
|
amount: 'account_balance',
|
||||||
|
},
|
||||||
|
vault: {
|
||||||
|
address: vault.address,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSwapAction(denomIn: string, denomOut: string, amount: BigNumber, slippage: number) {
|
||||||
|
return {
|
||||||
|
swap_exact_in: {
|
||||||
|
coin_in: {
|
||||||
|
denom: denomIn,
|
||||||
|
amount: {
|
||||||
|
exact: amount.toString(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
denom_out: denomOut,
|
||||||
|
slippage: slippage.toString(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
12
yarn.lock
12
yarn.lock
@ -3408,6 +3408,11 @@
|
|||||||
resolved "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz"
|
resolved "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz"
|
||||||
integrity sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==
|
integrity sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==
|
||||||
|
|
||||||
|
"@types/debounce-promise@^3.1.6":
|
||||||
|
version "3.1.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/debounce-promise/-/debounce-promise-3.1.6.tgz#873e838574011095ed0debf73eed3538e1261d75"
|
||||||
|
integrity sha512-DowqK95aku+OxMCeG2EQSeXeGeE8OCwLpMsUfIbP7hMF8Otj8eQXnzpwdtIKV+UqQBtkMcF6vbi4Otbh8P/wmg==
|
||||||
|
|
||||||
"@types/estree@*", "@types/estree@^1.0.0":
|
"@types/estree@*", "@types/estree@^1.0.0":
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz"
|
||||||
@ -4795,6 +4800,11 @@ data-urls@^3.0.2:
|
|||||||
whatwg-mimetype "^3.0.0"
|
whatwg-mimetype "^3.0.0"
|
||||||
whatwg-url "^11.0.0"
|
whatwg-url "^11.0.0"
|
||||||
|
|
||||||
|
debounce-promise@^3.1.2:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/debounce-promise/-/debounce-promise-3.1.2.tgz#320fb8c7d15a344455cd33cee5ab63530b6dc7c5"
|
||||||
|
integrity sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==
|
||||||
|
|
||||||
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
|
debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4:
|
||||||
version "4.3.4"
|
version "4.3.4"
|
||||||
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
|
resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz"
|
||||||
@ -7029,7 +7039,7 @@ locate-path@^6.0.0:
|
|||||||
|
|
||||||
lodash.debounce@^4.0.8:
|
lodash.debounce@^4.0.8:
|
||||||
version "4.0.8"
|
version "4.0.8"
|
||||||
resolved "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz"
|
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
|
||||||
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==
|
||||||
|
|
||||||
lodash.merge@^4.6.2:
|
lodash.merge@^4.6.2:
|
||||||
|
Loading…
Reference in New Issue
Block a user