Scope Funding to actions, enable fund for USDC (and others) (#426)

* fix vault test and vaulttable pos value

* fix vault test and vaulttable pos value updated

* Scope lend for deposit to action only

* fix build error

* fix build error
This commit is contained in:
Bob van der Helm 2023-09-05 12:45:55 +02:00 committed by GitHub
parent 19e06aa7d4
commit 46d4113d98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 96 additions and 51 deletions

View File

@ -22,13 +22,12 @@ describe('getVaultMetaData()', () => {
expect(getVaultMetaData(testAddress)?.name).toBe(testVaultName)
})
// TODO: Update the following test suite accordingly after new testnet vaults placed in constants
// it('returns the TESTNET vault of given address WHEN environment configured to testnet', () => {
// jest.spyOn(constants, 'IS_TESTNET', 'get').mockReturnValue(true)
it('returns the TESTNET vault of given address WHEN environment configured to testnet', () => {
jest.spyOn(constants, 'IS_TESTNET', 'get').mockReturnValue(true)
// const testAddress = 'osmo1q40xvrzpldwq5he4ftsf7zm2jf80tj373qaven38yqrvhex8r9rs8n94kv'
// const testVaultName = 'OSMO-USDC.n'
const testAddress = 'osmo1m45ap4rq4m2mfjkcqu9ks9mxmyx2hvx0cdca9sjmrg46q7lghzqqhxxup5'
const testVaultName = 'OSMO-ATOM'
// expect(getVaultMetaData(testAddress)?.name).toBe(testVaultName)
// })
})
expect(getVaultMetaData(testAddress)?.name).toBe(testVaultName)
})
})

View File

@ -3,13 +3,15 @@ import moment from 'moment'
import { getClient, getCreditManagerQueryClient, getVaultQueryClient } from 'api/cosmwasm-client'
import getPrice from 'api/prices/getPrice'
import getVaults from 'api/vaults/getVaults'
import { BN_ZERO } from 'constants/math'
import { BNCoin } from 'types/classes/BNCoin'
import { VaultStatus } from 'types/enums/vault'
import {
VaultPosition,
VaultPositionAmount,
} from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { getCoinValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
import { BN_ZERO } from 'constants/math'
async function getUnlocksAtTimestamp(unlockingId: number, vaultAddress: string) {
try {
@ -130,8 +132,12 @@ async function getVaultValuesAndAmounts(
secondary: BN(secondaryLpToken.amount),
},
values: {
primary: BN(primaryLpToken.amount).multipliedBy(primaryPrice),
secondary: BN(secondaryLpToken.amount).multipliedBy(secondaryPrice),
primary: getCoinValue(new BNCoin(primaryLpToken), [
BNCoin.fromDenomAndBigNumber(primaryLpToken.denom, primaryPrice),
]),
secondary: getCoinValue(new BNCoin(secondaryLpToken), [
BNCoin.fromDenomAndBigNumber(secondaryLpToken.denom, secondaryPrice),
]),
},
}
} catch (ex) {
@ -173,4 +179,4 @@ async function getDepositedVaults(accountId: string): Promise<DepositedVault[]>
}
}
export default getDepositedVaults
export default getDepositedVaults

View File

@ -40,6 +40,7 @@ export default function AccountFund() {
const hasFundingAssets =
fundingAssets.length > 0 && fundingAssets.every((a) => a.toCoin().amount !== '0')
const { data: marketAssets } = useMarketAssets()
const [isLending, toggleIsLending] = useToggle(false)
const baseBalance = useMemo(
() => walletBalances.find(byDenom(baseAsset.denom))?.amount ?? '0',
@ -58,10 +59,11 @@ export default function AccountFund() {
const result = await deposit({
accountId,
coins: fundingAssets,
lend: isLending,
})
setIsFunding(false)
if (result) useStore.setState({ focusComponent: null, walletAssetsModal: null })
}, [fundingAssets, accountId, setIsFunding, deposit])
}, [setIsFunding, accountId, deposit, fundingAssets, isLending])
const handleSelectAssetsClick = useCallback(() => {
useStore.setState({
@ -174,6 +176,8 @@ export default function AccountFund() {
<SwitchAutoLend
className='pt-4 mt-4 border border-transparent border-t-white/10'
accountId={selectedAccountId}
value={isLending}
onChange={toggleIsLending}
/>
<Button
className='w-full mt-4'
@ -187,4 +191,4 @@ export default function AccountFund() {
</Card>
</FullOverlayContent>
)
}
}

View File

@ -22,6 +22,7 @@ import { defaultFee } from 'utils/constants'
import { BN } from 'utils/helpers'
import { isNumber } from 'utils/parsers'
import { getPage, getRoute } from 'utils/route'
const menuClasses = 'absolute isolate flex w-full flex-wrap scrollbar-hide'
const ACCOUNT_MENU_BUTTON_ID = 'account-menu-button'
@ -73,7 +74,16 @@ export default function AccountMenuContent(props: Props) {
},
})
}
}, [createAccount, navigate, pathname, address, setShowMenu, setIsCreating])
}, [
setShowMenu,
setIsCreating,
createAccount,
navigate,
pathname,
address,
lendAssets,
enableAutoLendAccountId,
])
const handleCreateAccountClick = useCallback(() => {
setShowMenu(!showMenu)
@ -154,4 +164,4 @@ export default function AccountMenuContent(props: Props) {
)
}
export { ACCOUNT_MENU_BUTTON_ID }
export { ACCOUNT_MENU_BUTTON_ID }

View File

@ -20,6 +20,7 @@ import { ChevronDown, SortAsc, SortDesc, SortNone } from 'components/Icons'
import Loading from 'components/Loading'
import Text from 'components/Text'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { ORACLE_DENOM } from 'constants/oracle'
import { VAULT_DEPOSIT_BUFFER } from 'constants/vaults'
import useStore from 'store'
import { BNCoin } from 'types/classes/BNCoin'
@ -107,7 +108,7 @@ export const VaultTable = (props: Props) => {
cell: ({ row }: { row: Row<DepositedVault | Vault> }) => {
const vault = row.original as DepositedVault
const positionValue = vault.values.primary.plus(vault.values.secondary)
const coin = BNCoin.fromDenomAndBigNumber(baseCurrency.denom, positionValue)
const coin = BNCoin.fromDenomAndBigNumber(ORACLE_DENOM, positionValue)
return <DisplayCurrency coin={coin} className='text-xs' />
},
},
@ -229,7 +230,7 @@ export const VaultTable = (props: Props) => {
},
},
]
}, [baseCurrency.denom, props.data, props.isLoading])
}, [props.data, props.isLoading])
const table = useReactTable({
data: props.data,
@ -305,4 +306,4 @@ export const VaultTable = (props: Props) => {
</tbody>
</table>
)
}
}

View File

@ -251,4 +251,4 @@ function BorrowModal(props: Props) {
</div>
</Modal>
)
}
}

View File

@ -39,7 +39,7 @@ export default function FundAccount(props: Props) {
const hasFundingAssets =
fundingAssets.length > 0 && fundingAssets.every((a) => a.toCoin().amount !== '0')
const { autoLendEnabledAccountIds } = useAutoLend()
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(accountId)
const [isLending, toggleIsLending] = useToggle(false)
const { simulateDeposits } = useUpdatedAccount(account)
const { data: marketAssets } = useMarketAssets()
const baseBalance = useMemo(
@ -59,10 +59,11 @@ export default function FundAccount(props: Props) {
const result = await deposit({
accountId,
coins: fundingAssets,
lend: isLending,
})
setIsFunding(false)
if (result) useStore.setState({ fundAndWithdrawModal: null, walletAssetsModal: null })
}, [fundingAssets, accountId, setIsFunding, deposit])
}, [setIsFunding, accountId, deposit, fundingAssets, isLending])
const handleSelectAssetsClick = useCallback(() => {
useStore.setState({
@ -74,6 +75,10 @@ export default function FundAccount(props: Props) {
})
}, [selectedDenoms])
useEffect(() => {
toggleIsLending(autoLendEnabledAccountIds.includes(accountId))
}, [accountId, autoLendEnabledAccountIds, toggleIsLending])
useEffect(() => {
const currentSelectedDenom = fundingAssets.map((asset) => asset.denom)
@ -101,8 +106,8 @@ export default function FundAccount(props: Props) {
}, [])
useEffect(() => {
simulateDeposits(isAutoLendEnabled ? 'lend' : 'deposit', fundingAssets)
}, [isAutoLendEnabled, fundingAssets, simulateDeposits])
simulateDeposits(isLending ? 'lend' : 'deposit', fundingAssets)
}, [isLending, fundingAssets, simulateDeposits])
useEffect(() => {
if (BN(baseBalance).isLessThan(defaultFee.amount[0].amount)) {
@ -165,6 +170,8 @@ export default function FundAccount(props: Props) {
<SwitchAutoLend
className='pt-4 mt-4 border border-transparent border-t-white/10'
accountId={accountId}
onChange={toggleIsLending}
value={isLending}
/>
</div>
<Button
@ -177,4 +184,4 @@ export default function FundAccount(props: Props) {
/>
</>
)
}
}

View File

@ -6,6 +6,8 @@ import useAutoLend from 'hooks/useAutoLend'
interface Props {
accountId: string
className?: string
onChange?: () => void
value?: boolean
}
export default function SwitchAutoLend(props: Props) {
@ -13,15 +15,21 @@ export default function SwitchAutoLend(props: Props) {
const { autoLendEnabledAccountIds, toggleAutoLend } = useAutoLend()
const isAutoLendEnabled = autoLendEnabledAccountIds.includes(accountId)
function handleToggle() {
if (props.onChange) return props.onChange()
toggleAutoLend(accountId)
}
return (
<div className={classNames('w-full', className)}>
<SwitchWithLabel
name='isLending'
label='Lend assets to earn yield'
value={isAutoLendEnabled}
onChange={() => toggleAutoLend(accountId)}
value={props.value !== undefined ? props.value : isAutoLendEnabled}
onChange={handleToggle}
tooltip={`Fund your account and lend assets effortlessly! By lending, you'll earn attractive interest (APY) without impacting your LTV. It's a win-win situation - don't miss out on this easy opportunity to grow your holdings!`}
/>
</div>
)
}
}

View File

@ -21,7 +21,16 @@ export default function SwitchWithLabel(props: Props) {
<Text className='mr-2 text-white/70' size='sm'>
{props.label}
</Text>
{props.tooltip && <Tooltip type='info' content={<Text size='sm'>{props.tooltip}</Text>} />}
{props.tooltip && (
<Tooltip
type='info'
content={
<Text size='sm' className='px-2 py-3'>
{props.tooltip}
</Text>
}
/>
)}
</div>
<Switch
name={props.name}
@ -31,4 +40,4 @@ export default function SwitchWithLabel(props: Props) {
/>
</div>
)
}
}

View File

@ -67,6 +67,7 @@ export const ASSETS: Asset[] = [
isEnabled: !IS_TESTNET,
isMarket: !IS_TESTNET,
isDisplayCurrency: !IS_TESTNET,
isAutoLendEnabled: true,
poolId: 712,
pythPriceFeedId: 'e62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43',
},
@ -83,6 +84,7 @@ export const ASSETS: Asset[] = [
isEnabled: !IS_TESTNET,
isMarket: !IS_TESTNET,
isDisplayCurrency: !IS_TESTNET,
isAutoLendEnabled: true,
poolId: 704,
pythPriceFeedId: 'ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace',
},
@ -119,6 +121,7 @@ export const ASSETS: Asset[] = [
isMarket: true,
isDisplayCurrency: true,
isStable: true,
isAutoLendEnabled: true,
poolId: 678,
pythPriceFeedId: 'eaa020c61cc479712813461ce153894a96a6c00b21ed0cfc2798d1f9a9e9c94a',
},
@ -152,4 +155,4 @@ export const ASSETS: Asset[] = [
hasOraclePrice: true,
forceFetchPrice: true,
},
]
]

View File

@ -20,6 +20,7 @@ import { formatAmountWithSymbol } from 'utils/formatters'
import getTokenOutFromSwapResponse from 'utils/getTokenOutFromSwapResponse'
import { BN } from 'utils/helpers'
function generateExecutionMessage(
sender: string | undefined = '',
contract: string,
@ -209,7 +210,7 @@ export default function createBroadcastSlice(
return { estimateFee, execute }
},
deposit: async (options: { accountId: string; coins: BNCoin[] }) => {
deposit: async (options: { accountId: string; coins: BNCoin[]; lend: boolean }) => {
const msg: CreditManagerExecuteMsg = {
update_credit_account: {
account_id: options.accountId,
@ -219,11 +220,11 @@ export default function createBroadcastSlice(
},
}
if (checkAutoLendEnabled(options.accountId)) {
if (options.lend) {
msg.update_credit_account.actions.push(
...options.coins
.filter((coin) => getAssetByDenom(coin.denom)?.isAutoLendEnabled)
.map((coin) => ({ lend: coin.toActionCoin(true) })),
.map((coin) => ({ lend: coin.toActionCoin(options.lend) })),
)
}
@ -238,7 +239,9 @@ export default function createBroadcastSlice(
.join(' and ')
handleResponseMessages(
response,
`Deposited ${depositString} to Credit Credit Account ${options.accountId}`,
`Deposited ${options.lend ? 'and lent ' : ''}${depositString} to Credit Credit Account ${
options.accountId
}`,
)
return !!response.result
},
@ -524,4 +527,4 @@ export default function createBroadcastSlice(
}
},
}
}
}

View File

@ -20,7 +20,7 @@ interface BroadcastSlice {
claimRewards: (options: { accountId: string }) => ExecutableTx
createAccount: () => Promise<string | null>
deleteAccount: (options: { accountId: string; lends: BNCoin[] }) => Promise<boolean>
deposit: (options: { accountId: string; coins: BNCoin[] }) => Promise<boolean>
deposit: (options: { accountId: string; coins: BNCoin[]; lend: boolean }) => Promise<boolean>
depositIntoVault: (options: { accountId: string; actions: Action[] }) => Promise<boolean>
executeMsg: (options: { messages: MsgExecuteContract[] }) => Promise<BroadcastResult>
lend: (options: { accountId: string; coin: BNCoin; isMax?: boolean }) => Promise<boolean>
@ -52,4 +52,4 @@ interface BroadcastSlice {
borrow: BNCoin[]
reclaims: ActionCoin[]
}) => Promise<boolean>
}
}

View File

@ -16,12 +16,7 @@ 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: BNCoin, prices: BNCoin[]): BigNumber {
const price = prices.find((price) => price.denom === coin.denom)?.amount || '0'
return BN(price).multipliedBy(coin.amount).decimalPlaces(0)
}
export function getTokenPrice(denom: string, prices: BNCoin[]): BigNumber {
const price = prices.find((price) => price.denom === denom)?.amount || '0'
return BN(price)
}
}

View File

@ -5,9 +5,9 @@ import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults'
import { BNCoin } from 'types/classes/BNCoin'
import { Action } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
import { getAssetByDenom } from 'utils/assets'
import { getTokenPrice, getTokenValue } from 'utils/tokens'
import { getValueFromBNCoins, mergeBNCoinArrays } from './helpers'
import { getCoinValue } from 'utils/formatters'
import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers'
import { getTokenPrice } from 'utils/tokens'
export function getVaultsMetaData() {
return IS_TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA
@ -88,7 +88,7 @@ export function getVaultSwapActions(
)
primaryCoins.forEach((bnCoin) => {
let value = getTokenValue(bnCoin, prices)
let value = getCoinValue(bnCoin, prices)
if (value.isLessThanOrEqualTo(primaryLeftoverValue)) {
primaryLeftoverValue = primaryLeftoverValue.minus(value)
} else {
@ -99,7 +99,7 @@ export function getVaultSwapActions(
})
secondaryCoins.forEach((bnCoin) => {
let value = getTokenValue(bnCoin, prices)
let value = getCoinValue(bnCoin, prices)
if (value.isLessThanOrEqualTo(secondaryLeftoverValue)) {
secondaryLeftoverValue = secondaryLeftoverValue.minus(value)
} else {
@ -110,7 +110,7 @@ export function getVaultSwapActions(
})
otherCoins.forEach((bnCoin) => {
let value = getTokenValue(bnCoin, prices)
let value = getCoinValue(bnCoin, prices)
let amount = bnCoin.amount
if (primaryLeftoverValue.isGreaterThan(0)) {
@ -177,4 +177,4 @@ function getSwapAction(denomIn: string, denomOut: string, amount: BigNumber, sli
slippage: slippage.toString(),
},
}
}
}