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:
Bob van der Helm 2023-06-26 10:08:45 +02:00 committed by GitHub
parent c69461b95d
commit 415da05b8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
49 changed files with 576 additions and 159 deletions

View File

@ -5,6 +5,7 @@ import { BN } from 'utils/helpers'
import useStore from 'store'
import DisplayCurrency from 'components/DisplayCurrency'
import VaultBorrowings, { VaultBorrowingsProps } from 'components/Modals/Vault/VaultBorrowings'
import { TESTNET_VAULTS_META_DATA } from 'constants/vaults'
jest.mock('hooks/usePrices', () =>
jest.fn(() => ({
@ -20,11 +21,27 @@ jest.mock('hooks/useMarketAssets', () =>
})),
)
jest.mock('hooks/broadcast/useDepositVault', () => jest.fn(() => ({})))
jest.mock('components/DisplayCurrency')
const mockedDisplayCurrency = jest
.mocked(DisplayCurrency)
.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 />', () => {
const defaultProps: VaultBorrowingsProps = {
primaryAsset: ASSETS[0],
@ -38,7 +55,9 @@ describe('<VaultBorrowings />', () => {
vaults: [],
lends: [],
},
vault: mockedVault,
borrowings: [],
deposits: [],
onChangeBorrowings: jest.fn(),
}

View File

@ -21,6 +21,7 @@
"@tippyjs/react": "^4.2.6",
"bignumber.js": "^9.1.1",
"classnames": "^2.3.2",
"debounce-promise": "^3.1.2",
"moment": "^2.29.4",
"next": "^13.4.7",
"react": "^18.2.0",
@ -41,6 +42,7 @@
"@svgr/webpack": "^8.0.1",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/debounce-promise": "^3.1.6",
"@types/node": "^20.3.1",
"@types/react": "18.2.14",
"@types/react-dom": "18.2.4",

View File

@ -1,10 +1,11 @@
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)
if (account) {
return account.debts
return account.debts.map((coin) => new BNCoin(coin))
}
return new Promise((_, reject) => reject('Account not found'))

View File

@ -1,10 +1,11 @@
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)
if (account) {
return account.deposits
return account.deposits.map((coin) => new BNCoin(coin))
}
return new Promise((_, reject) => reject('Account not found'))

View File

@ -8,7 +8,9 @@ import getMarsPrice from 'api/prices/getMarsPrice'
import { ASSETS } from 'constants/assets'
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 {
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 incentivesApy = totalAnnualReturnsValue.dividedBy(marketLiquidityValue).multipliedBy(100)
return incentivesApy.toNumber()
return incentivesApy
} catch (ex) {
console.error(ex)
return null

View File

@ -19,8 +19,8 @@ export default async function getMarketBorrowings(): Promise<BorrowAsset[]> {
...asset,
borrowRate: market.borrowRate ?? 0,
liquidity: {
amount: amount,
value: BN(amount).times(price).toString(),
amount: BN(amount),
value: BN(amount).times(price),
},
}
})

View File

@ -1,7 +1,8 @@
import getMarkets from 'api/markets/getMarkets'
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 {
const markets: Market[] = await getMarkets()
const redBankQueryClient = await getRedBankQueryClient()
@ -14,7 +15,9 @@ export default async function getMarketDebts(): Promise<Coin[]> {
)
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) {
throw ex
}

View File

@ -1,16 +1,20 @@
import getMarkets from 'api/markets/getMarkets'
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 {
const markets: Market[] = await getMarkets()
const depositQueries = markets.map(getUnderlyingLiquidityAmount)
const depositsResults = await Promise.all(depositQueries)
return depositsResults.map<Coin>((deposit, index) => ({
return depositsResults.map<BNCoin>(
(deposit, index) =>
new BNCoin({
denom: markets[index].denom,
amount: deposit,
}))
}),
)
} catch (ex) {
throw ex
}

View File

@ -1,25 +1,26 @@
import { BN } from 'utils/helpers'
import getMarketDeposits from 'api/markets/getMarketDeposits'
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 debts = await getMarketDebts()
const liquidity: Coin[] = deposits.map((deposit) => {
const liquidity: BNCoin[] = deposits.map((deposit) => {
const debt = debts.find((debt) => debt.denom === deposit.denom)
if (debt) {
return {
return new BNCoin({
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,
amount: '0',
}
})
})
if (liquidity) {

View 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
}
}

View File

@ -16,6 +16,7 @@ import Text from 'components/Text'
import { ASSETS } from 'constants/assets'
import useStore from 'store'
import { convertToDisplayAmount, demagnify } from 'utils/formatters'
import { BN } from 'utils/helpers'
interface Props {
data: Account
@ -43,7 +44,7 @@ export const AccountBalancesTable = (props: Props) => {
{ amount: deposit.amount, denom: deposit.denom },
displayCurrency,
prices,
),
).toString(),
apy,
}
})
@ -60,7 +61,7 @@ export const AccountBalancesTable = (props: Props) => {
{ amount: lending.amount, denom: lending.denom },
displayCurrency,
prices,
),
).toString(),
apy,
}
})
@ -104,9 +105,11 @@ export const AccountBalancesTable = (props: Props) => {
return (
<FormattedNumber
className='text-right text-xs'
amount={demagnify(
amount={BN(
demagnify(
row.original.amount,
ASSETS.find((asset) => asset.denom === row.original.denom) ?? ASSETS[0],
),
)}
options={{ maxDecimals: 4 }}
/>
@ -121,7 +124,7 @@ export const AccountBalancesTable = (props: Props) => {
return (
<FormattedNumber
className='text-xs'
amount={row.original.apy}
amount={BN(row.original.apy)}
options={{ maxDecimals: 2, minDecimals: 2, suffix: '%' }}
/>
)

View File

@ -90,7 +90,7 @@ function Item(props: ItemProps) {
<div className='flex flex-grow items-center justify-end gap-2'>
{props.isPercentage ? (
<FormattedNumber
amount={props.current.toString()}
amount={props.current}
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }}
className='text-sm'
/>
@ -107,7 +107,7 @@ function Item(props: ItemProps) {
</span>
{props.isPercentage ? (
<FormattedNumber
amount={props.change.toString()}
amount={props.change}
options={{ suffix: '%', minDecimals: 2, maxDecimals: 2 }}
className={classNames('text-sm', increase ? 'text-profit' : 'text-loss')}
/>

View File

@ -13,17 +13,18 @@ import { FormattedNumber } from 'components/FormattedNumber'
import Text from 'components/Text'
import useStore from 'store'
import { formatValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
export const RiskChart = ({ data }: RiskChartProps) => {
const enableAnimations = useStore((s) => s.enableAnimations)
const accountStats = null
const currentRisk = 0
const currentRisk = BN(0)
return (
<div className='flex w-full flex-wrap overflow-hidden py-2'>
<FormattedNumber
className='px-3 pb-2 text-lg'
amount={currentRisk * 100}
amount={currentRisk.times(100)}
options={{
maxDecimals: 0,
minDecimals: 0,

View File

@ -10,7 +10,7 @@ export async function AccountDebtTable(props: Props) {
return debtData.map((debt) => {
return (
<p key={debt.denom}>
{debt.denom} {debt.amount}
{debt.denom} {debt.amount.toString()}
</p>
)
})

View File

@ -1,10 +1,11 @@
import { FormattedNumber } from 'components/FormattedNumber'
import TitleAndSubCell from 'components/TitleAndSubCell'
import DisplayCurrency from 'components/DisplayCurrency'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
asset: Asset
amount: string
amount: BigNumber
}
export default function AmountAndValue(props: Props) {
@ -16,7 +17,11 @@ export default function AmountAndValue(props: Props) {
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'
/>
)

View File

@ -7,7 +7,6 @@ import {
useReactTable,
} from '@tanstack/react-table'
import classNames from 'classnames'
import Image from 'next/image'
import React from 'react'
import AmountAndValue from 'components/AmountAndValue'

View File

@ -5,6 +5,7 @@ import { FormattedNumber } from 'components/FormattedNumber'
import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import useStore from 'store'
import { BN } from 'utils/helpers'
interface Props {
balance: number
@ -68,7 +69,7 @@ export const BorrowCapacity = ({
limitPercentOfMax ? 'opacity-50' : 'opacity-0',
)}
>
<FormattedNumber animate amount={limit} />
<FormattedNumber animate amount={BN(limit)} />
</div>
)}
</div>
@ -126,7 +127,7 @@ export const BorrowCapacity = ({
maxDecimals: decimals,
suffix: '%',
}}
amount={percentOfMaxRound}
amount={BN(percentOfMaxRound)}
/>
)}
</span>
@ -138,9 +139,9 @@ export const BorrowCapacity = ({
</Tooltip>
{!hideValues && (
<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>
<FormattedNumber animate amount={max} />
<FormattedNumber animate amount={BN(max)} />
</div>
)}
</div>

View File

@ -1,9 +1,10 @@
import { FormattedNumber } from 'components/FormattedNumber'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
import { convertToDisplayAmount } from 'utils/formatters'
interface Props {
coin: Coin
coin: BNCoin | Coin
className?: string
isApproximation?: boolean
}

View File

@ -64,7 +64,7 @@ function LendingMarketsTable(props: Props) {
accessorKey: 'accountDepositValue',
header: 'Deposited',
cell: ({ row }) => {
const accountDepositValue = (row.original.accountLentValue as BigNumber).toNumber()
const accountDepositValue = row.original.accountLentValue as BigNumber
return (
<FormattedNumber

View File

@ -4,9 +4,10 @@ import { animated, useSpring } from 'react-spring'
import useStore from 'store'
import { FormatOptions, formatValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
interface Props {
amount: number | string
amount: BigNumber
options?: FormatOptions
className?: string
animate?: boolean
@ -14,23 +15,23 @@ interface Props {
export const FormattedNumber = React.memo((props: Props) => {
const enableAnimations = useStore((s) => s.enableAnimations)
const prevAmountRef = useRef<number>(0)
const prevAmountRef = useRef<BigNumber>(BN(0))
useEffect(() => {
if (prevAmountRef.current !== Number(props.amount)) prevAmountRef.current = Number(props.amount)
if (!prevAmountRef.current.eq(props.amount)) prevAmountRef.current = props.amount
}, [props.amount])
const springAmount = useSpring({
number: Number(props.amount),
from: { number: prevAmountRef.current },
number: props.amount.toNumber(),
from: { number: prevAmountRef.current.toNumber() },
config: { duration: 1000 },
})
return (prevAmountRef.current === props.amount && props.amount === 0) ||
return (prevAmountRef.current.eq(props.amount) && props.amount.isZero()) ||
!props.animate ||
!enableAnimations ? (
<span className={classNames('number', props.className)}>
{formatValue(props.amount, {
{formatValue(props.amount.toString(), {
minDecimals: props.options?.minDecimals,
maxDecimals: props.options?.maxDecimals,
thousandSeparator: props.options?.thousandSeparator,

View File

@ -4,6 +4,7 @@ import { ReactElement, ReactNode } from 'react'
import { Tooltip } from 'components/Tooltip'
import useStore from 'store'
import { FormattedNumber } from 'components/FormattedNumber'
import { BN } from 'utils/helpers'
interface Props {
tooltip: string | ReactNode
@ -87,7 +88,7 @@ export const Gauge = ({
)}
<FormattedNumber
className={classNames(labelClassName, 'text-2xs')}
amount={Math.round(percentage)}
amount={BN(Math.round(percentage))}
options={{ maxDecimals: 0, minDecimals: 0 }}
animate
/>

View File

@ -2,6 +2,7 @@ import classNames from 'classnames'
import { FormattedNumber } from 'components/FormattedNumber'
import Text from 'components/Text'
import { BN } from 'utils/helpers'
interface ValueData extends FormattedNumberProps {
format?: 'number' | 'string'
@ -19,7 +20,11 @@ export const LabelValuePair = ({ label, value, className }: Props) => (
{label}
</Text>
<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>
</div>
)

View File

@ -6,27 +6,28 @@ import {
SortingState,
useReactTable,
} from '@tanstack/react-table'
import { useEffect, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import classNames from 'classnames'
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
import Text from 'components/Text'
import useStore from 'store'
import useAddVaultAssetTableColumns from 'components/Modals/AddVaultAssets/useAddVaultAssetTableColumns'
interface Props {
assets: BorrowAsset[]
selectedDenoms: string[]
onChangeSelected: (denoms: string[]) => void
}
export default function AddVaultAssetTable(props: Props) {
const selectedDenoms = useStore((s) => s.addVaultBorrowingsModal?.selectedDenoms) || []
const defaultSelected = props.assets.reduce((acc, asset, index) => {
if (selectedDenoms.includes(asset.denom)) {
const defaultSelected = useMemo(() => {
return props.assets.reduce((acc, asset, index) => {
if (props.selectedDenoms?.includes(asset.denom)) {
acc[index] = true
}
return acc
}, {} as { [key: number]: boolean })
}, [props.selectedDenoms, props.assets])
const [sorting, setSorting] = useState<SortingState>([{ id: 'symbol', desc: false }])
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)
@ -46,11 +47,16 @@ export default function AddVaultAssetTable(props: Props) {
})
useEffect(() => {
const selectedDenoms = props.assets
const newSelectedDenoms = props.assets
.filter((_, index) => selected[index])
.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])
return (

View File

@ -4,6 +4,7 @@ import SearchBar from 'components/SearchBar'
import Text from 'components/Text'
import useMarketBorrowings from 'hooks/useMarketBorrowings'
import AddVaultAssetTable from 'components/Modals/AddVaultAssets/AddVaultAssetTable'
import useStore from 'store'
interface Props {
vault: Vault
@ -14,8 +15,6 @@ interface Props {
export default function AddVaultAssetsModalContent(props: Props) {
const [searchString, setSearchString] = useState<string>('')
const { data: borrowAssets } = useMarketBorrowings()
const [selectedPoolDenoms, setSelectedPoolDenoms] = useState<string[]>([])
const [selectedOtherDenoms, setSelectedOtherDenoms] = useState<string[]>([])
const filteredBorrowAssets: BorrowAsset[] = useMemo(() => {
return borrowAssets.filter(
@ -49,6 +48,15 @@ export default function AddVaultAssetsModalContent(props: Props) {
[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(
(denoms: string[]) => {
setSelectedPoolDenoms(denoms)
@ -81,7 +89,11 @@ export default function AddVaultAssetsModalContent(props: Props) {
Leverage will be set at 50% for both assets by default
</Text>
</div>
<AddVaultAssetTable assets={poolAssets} onChangeSelected={onChangePoolDenoms} />
<AddVaultAssetTable
assets={poolAssets}
onChangeSelected={onChangePoolDenoms}
selectedDenoms={selectedPoolDenoms}
/>
<div className='p-4'>
<Text>Assets not in the liquidity pool</Text>
<Text size='xs' className='mt-1 text-white/60'>
@ -89,7 +101,11 @@ export default function AddVaultAssetsModalContent(props: Props) {
these assets below.
</Text>
</div>
<AddVaultAssetTable assets={stableAssets} onChangeSelected={onChangeOtherDenoms} />
<AddVaultAssetTable
assets={stableAssets}
onChangeSelected={onChangeOtherDenoms}
selectedDenoms={selectedOtherDenoms}
/>
</div>
</>
)

View File

@ -83,12 +83,12 @@ export default function BorrowModal() {
useStore.setState({ borrowModal: null })
}
const liquidityAmountString = formatValue(modal?.marketData?.liquidity?.amount || 0, {
const liquidityAmountString = formatValue(modal?.marketData?.liquidity?.amount.toString() || 0, {
abbreviated: true,
decimals: 6,
})
const liquidityValueString = formatValue(modal?.marketData?.liquidity?.value || 0, {
const liquidityValueString = formatValue(modal?.marketData?.liquidity?.value.toString() || 0, {
abbreviated: true,
decimals: 6,
})

View File

@ -23,7 +23,7 @@ function DetailsHeader({ data }: Props) {
<FormattedNumber amount={assetApy} options={{ suffix: '%' }} />
<FormattedNumber
className='ml-2 text-xs'
amount={assetApy / 365}
amount={assetApy.div(365)}
options={{ suffix: '%/day' }}
/>
</>

View File

@ -7,7 +7,7 @@ import useCurrentAccount from 'hooks/useCurrentAccount'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
import AccountBalanceSettableCoin from 'types/classes/AccountBalanceSettableCoin'
import { BNCoin } from 'types/classes/BNCoin'
function LendAndReclaimModalController() {
const currentAccount = useCurrentAccount()
@ -48,15 +48,12 @@ function LendAndReclaimModal({ currentAccount, config }: Props) {
async (value: BigNumber, isMax: boolean) => {
setIsConfirming(true)
const coin = new AccountBalanceSettableCoin(
asset.denom,
value.integerValue().toString(),
isMax,
)
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, value)
const options = {
fee: hardcodedFee,
accountId: currentAccount.id,
coin,
isMax,
}
await (isLendAction ? lend : reclaim)(options)

View File

@ -18,6 +18,7 @@ import useStore from 'store'
import DisplayCurrency from 'components/DisplayCurrency'
import usePrice from 'hooks/usePrice'
import { BNCoin } from 'types/classes/BNCoin'
import useDepositVault from 'hooks/broadcast/useDepositVault'
export interface VaultBorrowingsProps {
account: Account
@ -26,6 +27,8 @@ export interface VaultBorrowingsProps {
secondaryAmount: BigNumber
primaryAsset: Asset
secondaryAsset: Asset
deposits: BNCoin[]
vault: Vault
onChangeBorrowings: (borrowings: BNCoin[]) => void
}
@ -36,6 +39,13 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
const secondaryPrice = usePrice(props.secondaryAsset.denom)
const baseCurrency = useStore((s) => s.baseCurrency)
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(
() => 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 (
<div className='flex flex-grow flex-col gap-4 p-4'>
{props.borrowings.map((coin) => {
@ -191,7 +205,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
)
})}
</div>
<Button color='primary' text='Deposit' rightIcon={<ArrowRight />} />
<Button onClick={onConfirm} color='primary' text='Deposit' rightIcon={<ArrowRight />} />
</div>
)
}

View File

@ -77,7 +77,7 @@ export default function VaultDeposit(props: Props) {
() =>
props.isCustomRatio
? availableSecondaryAmount
: maxAssetValueNonCustom.dividedBy(secondaryPrice),
: maxAssetValueNonCustom.dividedBy(secondaryPrice).decimalPlaces(0),
[props.isCustomRatio, availableSecondaryAmount, secondaryPrice, maxAssetValueNonCustom],
)

View File

@ -1,5 +1,5 @@
import BigNumber from 'bignumber.js'
import { useCallback, useState } from 'react'
import { useCallback, useMemo, useState } from 'react'
import Accordion from 'components/Accordion'
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 VaultBorrowings from 'components/Modals/Vault/VaultBorrowings'
import VaultDepositSubTitle from 'components/Modals/Vault/VaultDepositsSubTitle'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
vault: Vault
@ -28,12 +29,24 @@ export default function VaultModalContent(props: Props) {
const [secondaryAmount, setSecondaryAmount] = useState<BigNumber>(BN(0))
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(
(amount: BigNumber) => setPrimaryAmount(amount),
(amount: BigNumber) => setPrimaryAmount(amount.decimalPlaces(0)),
[setPrimaryAmount],
)
const onChangeSecondaryAmount = useCallback(
(amount: BigNumber) => setSecondaryAmount(amount),
(amount: BigNumber) => setSecondaryAmount(amount.decimalPlaces(0)),
[setSecondaryAmount],
)
@ -84,6 +97,8 @@ export default function VaultModalContent(props: Props) {
primaryAsset={props.primaryAsset}
secondaryAsset={props.secondaryAsset}
onChangeBorrowings={onChangeBorrowings}
deposits={deposits}
vault={props.vault}
/>
),
title: 'Borrow',

View File

@ -14,6 +14,7 @@ import Button from 'components/Button'
import { ExclamationMarkTriangle, TrashBin } from 'components/Icons'
import { Tooltip } from 'components/Tooltip'
import AssetImage from 'components/AssetImage'
import { BNCoin } from 'types/classes/BNCoin'
interface Props {
amount: BigNumber
@ -108,7 +109,7 @@ export default function TokenInput(props: Props) {
</Text>
<FormattedNumber
className='mr-1 text-xs text-white/50'
amount={props.max.toNumber()}
amount={props.max}
options={{ decimals: props.asset.decimals }}
/>
<Button
@ -127,8 +128,8 @@ export default function TokenInput(props: Props) {
<div className='flex'>
<DisplayCurrency
isApproximation
className='inline pl-0.5 text-xs text-white/50'
coin={{ denom: props.asset.denom, amount: props.amount.toString() }}
className='inline pl-1 text-xs text-white/50'
coin={new BNCoin({ denom: props.asset.denom, amount: props.amount.toString() })}
/>
</div>
</div>

View File

@ -21,6 +21,7 @@ import useStore from 'store'
import { getBaseAsset, getEnabledMarketAssets } from 'utils/assets'
import { formatValue, truncate } from 'utils/formatters'
import useWalletBalances from 'hooks/useWalletBalances'
import { BN } from 'utils/helpers'
export default function ConnectedButton() {
// ---------------
@ -38,7 +39,7 @@ export default function ConnectedButton() {
// LOCAL STATE
// ---------------
const [showDetails, setShowDetails] = useToggle()
const [walletAmount, setWalletAmount] = useState(0)
const [walletAmount, setWalletAmount] = useState(BN(0))
const [isCopied, setCopied] = useClipboard(address || '', {
successDuration: 1000 * 5,
})
@ -62,9 +63,9 @@ export default function ConnectedButton() {
useEffect(() => {
if (!walletBalances || walletBalances.length === 0) return
setWalletAmount(
BigNumber(walletBalances?.find((coin: Coin) => coin.denom === baseAsset.denom)?.amount ?? 0)
.div(10 ** baseAsset.decimals)
.toNumber(),
BigNumber(
walletBalances?.find((coin: Coin) => coin.denom === baseAsset.denom)?.amount ?? 0,
).div(10 ** baseAsset.decimals),
)
const assetDenoms = marketAssets.map((asset) => asset.denom)
@ -103,7 +104,7 @@ export default function ConnectedButton() {
{isLoading ? (
<CircularProgress size={12} />
) : (
`${formatValue(walletAmount, { suffix: ` ${baseAsset.symbol}` })}`
`${formatValue(walletAmount.toString(), { suffix: ` ${baseAsset.symbol}` })}`
)}
</div>
</Button>

View 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 }
}

View 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),
},
)
}

View File

@ -6,7 +6,8 @@ import { ENV } from 'constants/env'
import { Store } from 'store'
import { getSingleValueFromBroadcastResult } from 'utils/broadcast'
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(
set: SetState<Store>,
@ -127,6 +128,30 @@ export default function createBroadcastSlice(
handleResponseMessages(response, `Requested unlock for ${options.vault.name}`)
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 }) => {
const msg = {
update_credit_account: {
@ -213,13 +238,13 @@ export default function createBroadcastSlice(
)
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 = {
update_credit_account: {
account_id: options.accountId,
actions: [
{
lend: options.coin,
lend: options.coin.toActionCoin(options.isMax),
},
],
},
@ -229,21 +254,17 @@ export default function createBroadcastSlice(
handleResponseMessages(
response,
`Successfully deposited ${formatAmountWithSymbol(options.coin)}`,
`Successfully deposited ${formatAmountWithSymbol(options.coin.toCoin())}`,
)
return !!response.result
},
reclaim: async (options: {
fee: StdFee
accountId: string
coin: AccountBalanceSettableCoin
}) => {
reclaim: async (options: { fee: StdFee; accountId: string; coin: BNCoin; isMax?: boolean }) => {
const msg = {
update_credit_account: {
account_id: options.accountId,
actions: [
{
reclaim: options.coin.toActionCoin(),
reclaim: options.coin.toActionCoin(options.isMax),
},
],
},
@ -253,7 +274,7 @@ export default function createBroadcastSlice(
handleResponseMessages(
response,
`Successfully withdrew ${formatAmountWithSymbol(options.coin)}`,
`Successfully withdrew ${formatAmountWithSymbol(options.coin.toCoin())}`,
)
return !!response.result
},

View File

@ -9,6 +9,7 @@ export default function createCommonSlice(set: SetState<CommonSlice>, get: GetSt
enableAnimations: true,
isOpen: true,
selectedAccount: null,
slippage: 0.02,
status: WalletConnectionStatus.Unconnected,
}
}

View File

@ -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

View File

@ -1,3 +1,4 @@
import { ActionCoin } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { BN } from 'utils/helpers'
export class BNCoin {
@ -9,10 +10,25 @@ export class BNCoin {
this.amount = BN(coin.amount)
}
static fromDenomAndBigNumber(denom: string, amount: BigNumber) {
return new BNCoin({ denom, amount: amount.toString() })
}
toCoin(): Coin {
return {
denom: this.denom,
amount: this.amount.toString(),
}
}
toActionCoin(max?: boolean): ActionCoin {
return {
denom: this.denom,
amount: max
? 'account_balance'
: {
exact: this.amount.toString(),
},
}
}
}

View File

@ -5,19 +5,20 @@
* 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 {
InstantiateMsg,
ExecuteMsg,
Uint128,
Addr,
ArrayOfAssetIncentiveResponse,
AssetIncentiveResponse,
ConfigResponse,
Decimal,
ExecuteMsg,
InstantiateMsg,
OwnerUpdate,
QueryMsg,
Decimal,
AssetIncentiveResponse,
ArrayOfAssetIncentiveResponse,
ConfigResponse,
Uint128,
} from './MarsIncentives.types'
export interface MarsIncentivesReadOnlyInterface {
contractAddress: string

View File

@ -5,22 +5,23 @@
* 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 { StdFee, Coin } from '@cosmjs/amino'
import { Coin, StdFee } from '@cosmjs/amino'
import {
InstantiateMsg,
ExecuteMsg,
Uint128,
Addr,
ArrayOfAssetIncentiveResponse,
AssetIncentiveResponse,
ConfigResponse,
Decimal,
ExecuteMsg,
InstantiateMsg,
OwnerUpdate,
QueryMsg,
Decimal,
AssetIncentiveResponse,
ArrayOfAssetIncentiveResponse,
ConfigResponse,
Uint128,
} from './MarsIncentives.types'
import { MarsIncentivesQueryClient, MarsIncentivesClient } from './MarsIncentives.client'
import { MarsIncentivesClient, MarsIncentivesQueryClient } from './MarsIncentives.client'
export const marsIncentivesQueryKeys = {
contract: [
{

View File

@ -1,15 +1,15 @@
interface Account extends AccountChange {
id: string
deposits: Coin[]
debts: Coin[]
lends: Coin[]
deposits: BNCoin[]
debts: BNCoin[]
lends: BNCoin[]
vaults: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
}
interface AccountChange {
deposits?: Coin[]
debts?: Coin[]
lends?: Coin[]
deposits?: BNCoin[]
debts?: BNCoin[]
lends?: BNCoin[]
vaults?: import('types/generated/mars-mock-credit-manager/MarsMockCreditManager.types').ArrayOfVaultInfoResponse
}

View File

@ -23,13 +23,13 @@ interface OtherAsset extends Omit<Asset, 'symbol'> {
interface BorrowAsset extends Asset {
borrowRate: number | null
liquidity: {
amount: string
value: string
amount: BigNumber
value: BigNumber
} | null
}
interface BorrowAssetActive extends BorrowAsset {
debt: string
debt: BigNumber
}
interface BigNumberCoin {

View File

@ -1,5 +1,3 @@
type AccountBalanceSettableCoin = import('types/classes/AccountBalanceSettableCoin')
interface BroadcastResult {
result?: import('@marsprotocol/wallet-connector').TxBroadcastResult
error?: string
@ -17,16 +15,23 @@ interface BroadcastSlice {
deleteAccount: (options: { fee: StdFee; accountId: string }) => Promise<boolean>
deposit: (options: { fee: StdFee; accountId: string; coin: Coin }) => 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>
lend: (options: {
fee: StdFee
accountId: string
coin: AccountBalanceSettableCoin
coin: BNCoin
isMax?: boolean
}) => Promise<boolean>
reclaim: (options: {
fee: StdFee
accountId: string
coin: AccountBalanceSettableCoin
coin: BNCoin
isMax?: boolean
}) => Promise<boolean>
repay: (options: {
fee: StdFee

View File

@ -6,5 +6,6 @@ interface CommonSlice {
enableAnimations: boolean
isOpen: boolean
selectedAccount: string | null
slippage: number
status: import('@marsprotocol/wallet-connector').WalletConnectionStatus
}

View File

@ -31,7 +31,7 @@ export const calculateAccountDebt = (
if (!account.debts) return BN(0)
return account.debts.reduce((acc, debt) => {
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)
}, BN(0))
}

View File

@ -1,5 +1,6 @@
import BigNumber from 'bignumber.js'
import { BNCoin } from 'types/classes/BNCoin'
import { getEnabledMarketAssets } from 'utils/assets'
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()
}
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 asset = getEnabledMarketAssets().find((asset) => asset.denom === coin.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)
.shiftedBy(-1 * asset.decimals)
.times(price.amount)
.div(displayPrice.amount)
.toNumber()
}
export function convertLiquidityRateToAPR(rate: number) {

View File

@ -1,5 +1,6 @@
import BigNumber from 'bignumber.js'
import { BNCoin } from 'types/classes/BNCoin'
import { getBaseAsset } from 'utils/assets'
import { BN } from 'utils/helpers'
@ -15,7 +16,12 @@ export const getTokenIcon = (denom: string, marketAssets: Asset[]) =>
export const getTokenInfo = (denom: string, marketAssets: Asset[]) =>
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'
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)
}

View File

@ -3,6 +3,8 @@ import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults'
import { BN } from 'utils/helpers'
import { getNetCollateralValue } from 'utils/accounts'
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) {
const vaults = IS_TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA
@ -33,3 +35,160 @@ export function calculateMaxBorrowAmounts(
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(),
},
}
}

View File

@ -3408,6 +3408,11 @@
resolved "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz"
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":
version "1.0.0"
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-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:
version "4.3.4"
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:
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==
lodash.merge@^4.6.2: