feat: lend and withdraw modals (#257)

* feat: lend/withdraw functionality

* feat: addressed to pr discussions

* rename: data hook members
This commit is contained in:
Yusuf Seyrek 2023-06-23 13:30:08 +03:00 committed by GitHub
parent f780de9d76
commit 2c399a2f16
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 1257 additions and 143 deletions

View File

@ -7,7 +7,7 @@ import { BN } from 'utils/helpers'
const data: LendingMarketTableData = {
asset: ASSETS[0],
marketDepositAmount: BN('890546916'),
accountDepositValue: BN('0.5498406009348686811'),
accountLentValue: BN('0.5498406009348686811'),
marketLiquidityAmount: BN('629396551'),
marketDepositCap: BN('2500000000000'),
marketLiquidityRate: 0.017,

View File

@ -3,9 +3,9 @@ import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { ENV } from 'constants/env'
import { MarsAccountNftQueryClient } from 'types/generated/mars-account-nft/MarsAccountNft.client'
import { MarsCreditManagerQueryClient } from 'types/generated/mars-credit-manager/MarsCreditManager.client'
import { MarsIncentivesQueryClient } from 'types/generated/mars-incentives/MarsIncentives.client'
import { MarsMockOracleQueryClient } from 'types/generated/mars-mock-oracle/MarsMockOracle.client'
import { MarsMockRedBankQueryClient } from 'types/generated/mars-mock-red-bank/MarsMockRedBank.client'
import { MarsMockRedBankReactQuery } from 'types/generated/mars-mock-red-bank/MarsMockRedBank.react-query'
import { MarsMockVaultQueryClient } from 'types/generated/mars-mock-vault/MarsMockVault.client'
import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.client'
@ -15,6 +15,7 @@ let _creditManagerQueryClient: MarsCreditManagerQueryClient
let _oracleQueryClient: MarsMockOracleQueryClient
let _redBankQueryClient: MarsMockRedBankQueryClient
let _paramsQueryClient: MarsParamsQueryClient
let _incentivesQueryClient: MarsIncentivesQueryClient
const getClient = async () => {
try {
@ -105,6 +106,19 @@ const getVaultQueryClient = async (address: string) => {
}
}
const getIncentivesQueryClient = async () => {
try {
if (!_incentivesQueryClient) {
const client = await getClient()
_incentivesQueryClient = new MarsIncentivesQueryClient(client, ENV.ADDRESS_INCENTIVES)
}
return _incentivesQueryClient
} catch (error) {
throw error
}
}
export {
getClient,
getAccountNftQueryClient,
@ -113,4 +127,5 @@ export {
getOracleQueryClient,
getRedBankQueryClient,
getVaultQueryClient,
getIncentivesQueryClient,
}

View File

@ -0,0 +1,46 @@
import getMarket from 'api/markets/getMarket'
import getAssetIncentive from 'api/incentives/getAssetIncentive'
import getUnderlyingLiquidityAmount from 'api/markets/getMarketUnderlyingLiquidityAmount'
import { BN } from 'utils/helpers'
import { SECONDS_IN_A_YEAR } from 'utils/constants'
import getPrice from 'api/prices/getPrice'
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> {
try {
const [assetIncentive, market] = await Promise.all([getAssetIncentive(denom), getMarket(denom)])
if (!assetIncentive) return null
const [marketLiquidityAmount, assetPriceResponse, marsPrice] = await Promise.all([
getUnderlyingLiquidityAmount(market),
getPrice(denom),
getMarsPrice(),
])
const assetDecimals = (ASSETS.find(byDenom(denom)) as Asset).decimals
const marsDecimals = 6,
priceFeedDecimals = 6
const assetPrice = BN(assetPriceResponse.price).shiftedBy(assetDecimals - priceFeedDecimals)
const marketLiquidityValue = BN(marketLiquidityAmount)
.shiftedBy(-assetDecimals)
.multipliedBy(assetPrice)
const marketReturns = BN(market.liquidityRate).multipliedBy(marketLiquidityValue)
const annualEmission = BN(assetIncentive.emission_per_second)
.multipliedBy(SECONDS_IN_A_YEAR)
.shiftedBy(-marsDecimals)
.multipliedBy(marsPrice)
const totalAnnualReturnsValue = annualEmission.plus(marketReturns)
const incentivesApy = totalAnnualReturnsValue.dividedBy(marketLiquidityValue).multipliedBy(100)
return incentivesApy.toNumber()
} catch (ex) {
console.error(ex)
return null
}
}

View File

@ -0,0 +1,27 @@
import moment from 'moment'
import { getIncentivesQueryClient } from 'api/cosmwasm-client'
import { AssetIncentiveResponse } from 'types/generated/mars-incentives/MarsIncentives.types'
export default async function getAssetIncentive(
denom: string,
): Promise<AssetIncentiveResponse | null> {
try {
const client = await getIncentivesQueryClient()
const assetIncentive = await client.assetIncentive({
denom,
})
const { duration, start_time } = assetIncentive
const isValid = moment(start_time + duration).isBefore(moment.now())
if (!isValid) {
throw 'Asset incentive duration is end.'
}
return assetIncentive
} catch (ex) {
console.error(ex)
return null
}
}

View File

@ -0,0 +1,14 @@
import { ENV } from 'constants/env'
import { resolveMarketResponse } from 'utils/resolvers'
import { getClient, getRedBankQueryClient } from 'api/cosmwasm-client'
export default async function getMarket(denom: string): Promise<Market> {
try {
const client = await getRedBankQueryClient()
const market = await client.market({ denom })
return resolveMarketResponse(market)
} catch (ex) {
throw ex
}
}

View File

@ -1,17 +1,10 @@
import getMarkets from 'api/markets/getMarkets'
import { getRedBankQueryClient } from 'api/cosmwasm-client'
import getUnderlyingLiquidityAmount from 'api/markets/getMarketUnderlyingLiquidityAmount'
export default async function getMarketDeposits(): Promise<Coin[]> {
try {
const markets: Market[] = await getMarkets()
const redBankQueryClient = await getRedBankQueryClient()
const depositQueries = markets.map((asset) =>
redBankQueryClient.underlyingLiquidityAmount({
denom: asset.denom,
amountScaled: asset.collateralTotalScaled,
}),
)
const depositQueries = markets.map(getUnderlyingLiquidityAmount)
const depositsResults = await Promise.all(depositQueries)
return depositsResults.map<Coin>((deposit, index) => ({

View File

@ -0,0 +1,15 @@
import { getRedBankQueryClient } from 'api/cosmwasm-client'
export default async function getUnderlyingLiquidityAmount(market: Market): Promise<string> {
try {
const client = await getRedBankQueryClient()
const marketLiquidityAmount: string = await client.underlyingLiquidityAmount({
denom: market.denom,
amountScaled: market.collateralTotalScaled,
})
return marketLiquidityAmount
} catch (ex) {
throw ex
}
}

View File

@ -1,18 +1,12 @@
import { getEnabledMarketAssets } from 'utils/assets'
import { resolveMarketResponses } from 'utils/resolvers'
import { getRedBankQueryClient } from 'api/cosmwasm-client'
import getMarket from 'api/markets/getMarket'
export default async function getMarkets(): Promise<Market[]> {
try {
const enabledAssets = getEnabledMarketAssets()
const redBankQueryClient = await getRedBankQueryClient()
const marketQueries = enabledAssets.map((asset) => getMarket(asset.denom))
const marketQueries = enabledAssets.map((asset) =>
redBankQueryClient.market({ denom: asset.denom }),
)
const marketResults = await Promise.all(marketQueries)
return resolveMarketResponses(marketResults)
return await Promise.all(marketQueries)
} catch (ex) {
throw ex
}

View File

@ -0,0 +1,45 @@
import { BN } from 'utils/helpers'
import getPrice from 'api/prices/getPrice'
const MARS_MAINNET_DENOM = 'ibc/573FCD90FACEE750F55A8864EF7D38265F07E5A9273FA0E8DAFD39951332B580'
const MARS_OSMO_POOL_URL = 'https://lcd-osmosis.blockapsis.com/osmosis/gamm/v1beta1/pools/907'
interface PoolToken {
denom: string
amount: string
}
interface PoolAsset {
token: PoolToken
weight: string
}
const findPoolAssetByTokenDenom = (assets: PoolAsset[], denom: string) =>
assets.find((a) => a.token.denom === denom)
async function getMarsPrice() {
const marsOsmoRate = await getMarsOsmoRate()
const osmoPrice = await getPrice('uosmo')
return marsOsmoRate.multipliedBy(osmoPrice.price)
}
const getMarsOsmoRate = async () => {
const resp = await fetch(MARS_OSMO_POOL_URL).then((res) => res.json())
const spotPrice = calculateSpotPrice(resp.pool.pool_assets)
return BN(1).dividedBy(spotPrice)
}
const calculateSpotPrice = (poolAssets: PoolAsset[]) => {
const assetIn = findPoolAssetByTokenDenom(poolAssets, MARS_MAINNET_DENOM) as PoolAsset
const assetOut = findPoolAssetByTokenDenom(poolAssets, 'uosmo') as PoolAsset
const numerator = BN(assetIn.token.amount).div(assetIn.weight)
const denominator = BN(assetOut.token.amount).div(assetOut.weight)
return numerator.dividedBy(denominator)
}
export default getMarsPrice

View File

@ -12,7 +12,7 @@ import Text from 'components/Text'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { calculateAccountDeposits } from 'utils/accounts'
import { hardcodedFee } from 'utils/contants'
import { hardcodedFee } from 'utils/constants'
import { BN } from 'utils/helpers'
import { getPage, getRoute } from 'utils/route'
import usePrices from 'hooks/usePrices'

View File

@ -12,7 +12,7 @@ import Overlay from 'components/Overlay'
import Text from 'components/Text'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { hardcodedFee } from 'utils/contants'
import { hardcodedFee } from 'utils/constants'
import { isNumber } from 'utils/parsers'
const menuClasses = 'absolute isolate flex w-full flex-wrap scrollbar-hide'

View File

@ -0,0 +1,10 @@
import useCurrentAccount from 'hooks/useCurrentAccount'
import AccountSummary from 'components/Account/AccountSummary'
function CurrentAccountSummary({ change }: { change?: AccountChange }) {
const account = useCurrentAccount()
return <AccountSummary account={account} change={change} />
}
export default CurrentAccountSummary

View File

@ -11,7 +11,7 @@ import { ASSETS } from 'constants/assets'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { getAmount } from 'utils/accounts'
import { hardcodedFee } from 'utils/contants'
import { hardcodedFee } from 'utils/constants'
import { BN } from 'utils/helpers'
interface Props {

View File

@ -4,6 +4,7 @@ import Text from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import ConditionalWrapper from 'hocs/ConditionalWrapper'
import useCurrentAccountDeposits from 'hooks/useCurrentAccountDeposits'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import { byDenom } from 'utils/array'
interface Props {
@ -14,18 +15,19 @@ const buttonClassnames = 'm-0 flex w-40 text-lg'
const iconClassnames = 'ml-0 mr-1 w-4 h-4'
function LendingActionButtons(props: Props) {
const { asset, accountDepositValue } = props.data
const { asset, accountLentValue: accountLendValue } = props.data
const accountDeposits = useCurrentAccountDeposits()
const { openLend, openReclaim } = useLendAndReclaimModal()
const assetDepositAmount = accountDeposits.find(byDenom(asset.denom))?.amount
return (
<div className='flex flex-row space-x-2'>
{accountDepositValue && (
{accountLendValue && (
<Button
leftIcon={<ArrowDownLine />}
iconClassName={iconClassnames}
color='secondary'
onClick={() => alert('hello!')}
onClick={() => openReclaim(props.data)}
className={buttonClassnames}
>
Withdraw
@ -50,7 +52,7 @@ function LendingActionButtons(props: Props) {
iconClassName={iconClassnames}
disabled={!assetDepositAmount}
color='secondary'
onClick={() => alert('hello!')}
onClick={() => openLend(props.data)}
className={buttonClassnames}
>
Lend

View File

@ -22,7 +22,7 @@ interface Props {
function LendingMarketsTable(props: Props) {
const { title, data } = props
const { symbol: displayCurrencySymbol } = useDisplayCurrencyPrice()
const shouldShowAccountDeposit = !!data[0]?.accountDepositValue
const shouldShowAccountDeposit = !!data[0]?.accountLentValue
const rowRenderer = (row: Row<LendingMarketTableData>, table: Table<LendingMarketTableData>) => {
return (
@ -64,9 +64,7 @@ function LendingMarketsTable(props: Props) {
accessorKey: 'accountDepositValue',
header: 'Deposited',
cell: ({ row }) => {
const accountDepositValue = (
row.original.accountDepositValue as BigNumber
).toNumber()
const accountDepositValue = (row.original.accountLentValue as BigNumber).toNumber()
return (
<FormattedNumber

View File

@ -38,8 +38,10 @@ export default function Modal(props: Props) {
// close dialog on unmount
useEffect(() => {
const dialog = ref.current
dialog?.removeAttribute('open')
return () => dialog.close()
return () => {
dialog.removeAttribute('open')
dialog.close()
}
}, [])
return (

View File

@ -0,0 +1,97 @@
import Image from 'next/image'
import { useState } from 'react'
import Button from 'components/Button'
import Card from 'components/Card'
import Divider from 'components/Divider'
import { ArrowRight } from 'components/Icons'
import Modal from 'components/Modal'
import Text from 'components/Text'
import TokenInputWithSlider from 'components/TokenInputWithSlider'
import { BN } from 'utils/helpers'
import { byDenom } from 'utils/array'
import CurrentAccountSummary from 'components/Account/CurrentAccountSummary'
import AssetImage from 'components/AssetImage'
interface Props {
asset: Asset
title: string
isOpen: boolean
coinBalances: Coin[]
contentHeader?: JSX.Element
actionButtonText: string
showLoaderInButton: boolean
accountSummaryChange?: AccountChange
onClose: () => void
onChange: (value: BigNumber) => void
onAction: (value: BigNumber) => void
}
export default function AssetAmountSelectActionModal(props: Props) {
const {
asset,
title,
isOpen,
coinBalances,
contentHeader = null,
actionButtonText,
showLoaderInButton,
accountSummaryChange,
onClose,
onChange,
onAction,
} = props
const [amount, setAmount] = useState(BN(0))
const maxAmount = BN(coinBalances.find(byDenom(asset.denom))?.amount ?? 0)
const handleAmountChange = (value: BigNumber) => {
setAmount(value)
onChange(value)
}
const handleActionClick = () => {
onAction(amount)
}
return (
<Modal
open={isOpen}
onClose={onClose}
header={
<span className='flex items-center gap-4 px-4'>
<AssetImage size={24} asset={asset} />
<Text>{title}</Text>
</span>
}
headerClassName='gradient-header pl-2 pr-2.5 py-2.5 border-b-white/5 border-b'
contentClassName='flex flex-col min-h-[400px]'
>
{contentHeader}
<div className='flex flex-grow items-start gap-6 p-6'>
<Card
className='flex flex-grow bg-white/5 p-4'
contentClassName='gap-6 flex flex-col justify-between h-full'
>
<TokenInputWithSlider
asset={asset}
onChange={handleAmountChange}
amount={amount}
max={maxAmount}
hasSelect
maxText='Max'
/>
<Divider />
<Button
onClick={handleActionClick}
showProgressIndicator={showLoaderInButton}
disabled={!amount.toNumber()}
className='w-full'
text={actionButtonText}
rightIcon={<ArrowRight />}
/>
</Card>
<CurrentAccountSummary change={accountSummaryChange} />
</div>
</Modal>
)
}

View File

@ -14,7 +14,7 @@ import { ASSETS } from 'constants/assets'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { hardcodedFee } from 'utils/contants'
import { hardcodedFee } from 'utils/constants'
import { formatPercent, formatValue } from 'utils/formatters'
import { BN } from 'utils/helpers'
import AssetImage from 'components/AssetImage'

View File

@ -10,7 +10,7 @@ import TokenInputWithSlider from 'components/TokenInputWithSlider'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { getAmount } from 'utils/accounts'
import { hardcodedFee } from 'utils/contants'
import { hardcodedFee } from 'utils/constants'
import { BN } from 'utils/helpers'
interface Props {

View File

@ -0,0 +1,61 @@
import DisplayCurrency from 'components/DisplayCurrency'
import TitleAndSubCell from 'components/TitleAndSubCell'
import { FormattedNumber } from 'components/FormattedNumber'
import useAssetIncentivesApy from 'hooks/useAssetIncentiveApy'
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
interface Props {
data: LendingMarketTableData
}
function DetailsHeader({ data }: Props) {
const { asset, marketDepositCap, accountLentAmount: accountLendAmount } = data
const { data: assetApy } = useAssetIncentivesApy(asset.denom)
const balanceInWallet = useCurrentWalletBalance(asset.denom)
return (
<div className='flex gap-6 border-b border-b-white/5 px-6 py-4 gradient-header'>
{assetApy && (
<>
<TitleAndSubCell
title={
<>
<FormattedNumber amount={assetApy} options={{ suffix: '%' }} />
<FormattedNumber
className='ml-2 text-xs'
amount={assetApy / 365}
options={{ suffix: '%/day' }}
/>
</>
}
sub={'APY'}
/>
<div className='h-100 w-[1px] bg-white/10'></div>
</>
)}
{accountLendAmount && (
<>
<TitleAndSubCell
title={<DisplayCurrency coin={{ denom: asset.denom, amount: accountLendAmount }} />}
sub={'Deposited'}
/>
<div className='h-100 w-[1px] bg-white/10'></div>
</>
)}
{balanceInWallet && (
<>
<TitleAndSubCell title={<DisplayCurrency coin={balanceInWallet} />} sub={'In Wallet'} />
<div className='h-100 w-[1px] bg-white/10'></div>
</>
)}
<TitleAndSubCell
title={
<DisplayCurrency coin={{ denom: asset.denom, amount: marketDepositCap.toString() }} />
}
sub={'Deposit Cap'}
/>
</div>
)
}
export default DetailsHeader

View File

@ -0,0 +1,80 @@
import { useState } from 'react'
import useStore from 'store'
import useToggle from 'hooks/useToggle'
import { hardcodedFee } from 'utils/constants'
import useCurrentAccount from 'hooks/useCurrentAccount'
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
const getAccountChange = (isLend: boolean, value: BigNumber, denom: string): AccountChange => {
const makeCoin = (denom: string, shouldNegate: boolean) => [
{
amount: (shouldNegate ? value.negated() : value).toString(),
denom,
},
]
return {
deposits: makeCoin(denom, isLend),
lends: makeCoin(denom, !isLend),
}
}
function LendAndReclaimModal() {
const lend = useStore((s) => s.lend)
const reclaim = useStore((s) => s.reclaim)
const currentAccount = useCurrentAccount()
const { config, close } = useLendAndReclaimModal()
const [isConfirming, setIsConfirming] = useToggle()
const [accountChange, setAccountChange] = useState<AccountChange | undefined>()
if (!config || !currentAccount) return null
const { data, action } = config
const { asset } = data
const isLendAction = action === 'lend'
const actionText = isLendAction ? 'Lend' : 'Withdraw'
const coinBalances = currentAccount[isLendAction ? 'deposits' : 'lends'] ?? []
const handleAmountChange = (value: BigNumber) => {
setAccountChange(getAccountChange(isLendAction, value, asset.denom))
}
const handleAction = async (value: BigNumber) => {
setIsConfirming(true)
const options = {
fee: hardcodedFee,
accountId: currentAccount.id,
coin: {
denom: asset.denom,
amount: value.toString(),
},
}
await (isLendAction ? lend : reclaim)(options)
setIsConfirming(false)
close()
}
return (
<AssetAmountSelectActionModal
asset={asset}
isOpen={true}
contentHeader={<DetailsHeader data={data} />}
coinBalances={coinBalances}
actionButtonText={actionText}
showLoaderInButton={isConfirming}
accountSummaryChange={accountChange}
title={`${actionText} ${asset.symbol}`}
onClose={close}
onAction={handleAction}
onChange={handleAmountChange}
/>
)
}
export default LendAndReclaimModal

View File

@ -3,15 +3,18 @@ import BorrowModal from 'components/Modals/Borrow/BorrowModal'
import FundAndWithdrawModal from 'components/Modals/FundWithdraw/FundAndWithdrawModal'
import AddVaultBorrowAssetsModal from 'components/Modals/AddVaultAssets/AddVaultBorrowAssetsModal'
import UnlockModal from 'components/Modals/Unlock/UnlockModal'
import LendAndReclaimModal from 'components/Modals/LendAndReclaim'
export default function ModalsContainer() {
return (
<>
<VaultModal />
<BorrowModal />
<FundAndWithdrawModal />
<VaultModal />
<AddVaultBorrowAssetsModal />
<UnlockModal />
<LendAndReclaimModal />
</>
)
}

View File

@ -1,8 +1,8 @@
import { hardcodedFee } from 'utils/constants'
import Button from 'components/Button'
import { Enter } from 'components/Icons'
import Text from 'components/Text'
import useStore from 'store'
import { hardcodedFee } from 'utils/contants'
interface Props {
depositedVault: DepositedVault

View File

@ -24,7 +24,7 @@ export default function TokenInputWithSlider(props: Props) {
const [percentage, setPercentage] = useState(0)
function onChangeSlider(percentage: number) {
const newAmount = BN(percentage).div(100).times(props.max)
const newAmount = BN(percentage).div(100).times(props.max).integerValue()
setPercentage(percentage)
setAmount(newAmount)
props.onChange(newAmount)

View File

@ -0,0 +1,7 @@
import useSWR from 'swr'
import calculateAssetIncentivesApy from 'api/incentives/calculateAssetIncentivesApy'
export default function useAssetIncentivesApy(denom: string) {
return useSWR(`assetIncentiveApy-${denom}`, () => calculateAssetIncentivesApy(denom))
}

View File

@ -0,0 +1,6 @@
import useCurrentAccount from 'hooks/useCurrentAccount'
export default function useCurrentAccountLends() {
const account = useCurrentAccount()
return account?.lends ?? []
}

View File

@ -0,0 +1,12 @@
import useStore from 'store'
import useWalletBalances from 'hooks/useWalletBalances'
import { byDenom } from 'utils/array'
function useCurrentWalletBalance(denom: string) {
const address = useStore((s) => s.address)
const { data: walletBalances } = useWalletBalances(address)
return walletBalances.find(byDenom(denom))
}
export default useCurrentWalletBalance

View File

@ -0,0 +1,27 @@
import { useCallback } from 'react'
import useStore from 'store'
function useLendAndReclaimModal() {
const config = useStore((s) => s.lendAndReclaimModal)
const open = useCallback((action: LendAndReclaimModalAction, data: LendingMarketTableData) => {
const _config: LendAndReclaimModalConfig = {
action,
data,
}
useStore.setState({ lendAndReclaimModal: _config })
}, [])
const close = useCallback(() => {
useStore.setState({ lendAndReclaimModal: null })
}, [])
const openLend = useCallback((data: LendingMarketTableData) => open('lend', data), [open])
const openReclaim = useCallback((data: LendingMarketTableData) => open('reclaim', data), [open])
return { config, openLend, openReclaim, close }
}
export default useLendAndReclaimModal

View File

@ -7,36 +7,36 @@ import useMarketDeposits from 'hooks/useMarketDeposits'
import useMarketLiquidities from 'hooks/useMarketLiquidities'
import useDisplayCurrencyPrice from 'hooks/useDisplayCurrencyPrice'
import useDepositEnabledMarkets from 'hooks/useDepositEnabledMarkets'
import useCurrentAccountDeposits from 'hooks/useCurrentAccountDeposits'
import useCurrentAccountLends from 'hooks/useCurrentAccountLends'
function useLendingMarketAssetsTableData(): {
lentAssets: LendingMarketTableData[]
accountLentAssets: LendingMarketTableData[]
availableAssets: LendingMarketTableData[]
} {
const markets = useDepositEnabledMarkets()
const accountDeposits = useCurrentAccountDeposits()
// TODO: replace market deposits with account.lends when credit manager contract has lend feature
const accountLentAmounts = useCurrentAccountLends()
const { data: marketDeposits } = useMarketDeposits()
const { data: marketLiquidities } = useMarketLiquidities()
const { convertAmount } = useDisplayCurrencyPrice()
return useMemo(() => {
const lentAssets: LendingMarketTableData[] = [],
const accountLentAssets: LendingMarketTableData[] = [],
availableAssets: LendingMarketTableData[] = []
markets.forEach(({ denom, depositCap, liquidityRate, liquidationThreshold, maxLtv }) => {
const asset = getAssetByDenom(denom) as Asset
const marketDepositAmount = BN(marketDeposits.find(byDenom(denom))?.amount ?? 0)
const marketLiquidityAmount = BN(marketLiquidities.find(byDenom(denom))?.amount ?? 0)
const accountDepositAmount = accountDeposits.find(byDenom(denom))?.amount
const accountDepositValue = accountDepositAmount
? convertAmount(asset, accountDepositAmount)
const accountLentAmount = accountLentAmounts.find(byDenom(denom))?.amount
const accountLentValue = accountLentAmount
? convertAmount(asset, accountLentAmount)
: undefined
const lendingMarketAsset: LendingMarketTableData = {
asset,
marketDepositAmount,
accountDepositValue,
accountLentValue,
accountLentAmount,
marketLiquidityAmount,
marketDepositCap: BN(depositCap),
marketLiquidityRate: liquidityRate,
@ -44,13 +44,13 @@ function useLendingMarketAssetsTableData(): {
marketMaxLtv: maxLtv,
}
;(lendingMarketAsset.accountDepositValue ? lentAssets : availableAssets).push(
;(lendingMarketAsset.accountLentValue ? accountLentAssets : availableAssets).push(
lendingMarketAsset,
)
})
return { lentAssets, availableAssets }
}, [markets, marketDeposits, marketLiquidities, accountDeposits, convertAmount])
return { accountLentAssets, availableAssets }
}, [markets, marketDeposits, marketLiquidities, accountLentAmounts, convertAmount])
}
export default useLendingMarketAssetsTableData

View File

@ -5,5 +5,6 @@ import getWalletBalances from 'api/wallets/getWalletBalances'
export default function useWalletBalances(address?: string) {
return useSWR(`walletBalances${address}`, () => getWalletBalances(address || ''), {
isPaused: () => !address,
fallbackData: [],
})
}

View File

@ -3,12 +3,12 @@ import LendingMarketsTable from 'components/Earn/Lend/LendingMarketsTable'
import useLendingMarketAssetsTableData from 'hooks/useLendingMarketAssetsTableData'
export default function LendPage() {
const { lentAssets, availableAssets } = useLendingMarketAssetsTableData()
const { accountLentAssets, availableAssets } = useLendingMarketAssetsTableData()
return (
<>
<Tab />
<LendingMarketsTable data={lentAssets} title='Lent Assets' />
<LendingMarketsTable data={accountLentAssets} title='Lent Assets' />
<LendingMarketsTable data={availableAssets} title='Available Markets' />
</>
)

View File

@ -6,11 +6,34 @@ import { ENV } from 'constants/env'
import { Store } from 'store'
import { getSingleValueFromBroadcastResult } from 'utils/broadcast'
import { formatAmountWithSymbol } from 'utils/formatters'
import { BN } from 'utils/helpers'
export default function createBroadcastSlice(
set: SetState<Store>,
get: GetState<Store>,
): BroadcastSlice {
const handleResponseMessages = (
response: BroadcastResult,
successMessage: string,
errorMessage?: string,
) => {
if (response.result?.response.code === 0) {
set({
toast: {
message: successMessage,
},
})
} else {
const error = response.error ? response.error : response.result?.rawLogs
set({
toast: {
message: errorMessage ?? `Transaction failed: ${error}`,
isError: true,
},
})
}
}
return {
toast: null,
borrow: async (options: { fee: StdFee; accountId: string; coin: Coin }) => {
@ -23,24 +46,10 @@ export default function createBroadcastSlice(
const response = await get().executeMsg({ msg, fee: options.fee })
if (response.result?.response.code === 0) {
set({
toast: {
message: `Borrowed ${formatAmountWithSymbol(options.coin)} to Account ${
options.accountId
}`,
},
})
} else {
const error = response.error ? response.error : response.result?.rawLogs
set({
toast: {
message: `Transaction failed: ${error}`,
isError: true,
},
})
}
handleResponseMessages(
response,
`Borrowed ${formatAmountWithSymbol(options.coin)} to Account ${options.accountId}`,
)
return !!response.result
},
createAccount: async (options: { fee: StdFee }) => {
@ -76,16 +85,9 @@ export default function createBroadcastSlice(
const response = await get().executeMsg({ msg, fee: options.fee })
set({ deleteAccountModal: false })
if (response.result) {
set({ toast: { message: `Account ${options.accountId} deleted` } })
} else {
set({
toast: {
message: response.error ?? `Transaction failed: ${response.error}`,
isError: true,
},
})
}
handleResponseMessages(response, `Account ${options.accountId} deleted`)
return !!response.result
},
deposit: async (options: { fee: StdFee; accountId: string; coin: Coin }) => {
@ -101,22 +103,11 @@ export default function createBroadcastSlice(
}
const response = await get().executeMsg({ msg, fee: options.fee, funds: [options.coin] })
if (response.result) {
set({
toast: {
message: `Deposited ${formatAmountWithSymbol(options.coin)} to Account ${
options.accountId
}`,
},
})
} else {
set({
toast: {
message: response.error ?? `Transaction failed: ${response.error}`,
isError: true,
},
})
}
handleResponseMessages(
response,
`Deposited ${formatAmountWithSymbol(options.coin)} to Account ${options.accountId}`,
)
return !!response.result
},
unlock: async (options: { fee: StdFee; vault: Vault; amount: string }) => {
@ -133,20 +124,7 @@ export default function createBroadcastSlice(
funds: [],
})
if (response.result) {
set({
toast: {
message: `Requested unlock for ${options.vault.name}`,
},
})
} else {
set({
toast: {
message: response.error ?? `Request unlocked failed: ${response.error}`,
isError: true,
},
})
}
handleResponseMessages(response, `Requested unlock for ${options.vault.name}`)
return !!response.result
},
withdraw: async (options: { fee: StdFee; accountId: string; coin: Coin }) => {
@ -162,22 +140,11 @@ export default function createBroadcastSlice(
}
const response = await get().executeMsg({ msg, fee: options.fee })
if (response.result) {
set({
toast: {
message: `Withdrew ${formatAmountWithSymbol(options.coin)} from Account ${
options.accountId
}`,
},
})
} else {
set({
toast: {
message: response.error ?? `Transaction failed: ${response.error}`,
isError: true,
},
})
}
handleResponseMessages(
response,
`Withdrew ${formatAmountWithSymbol(options.coin)} from Account ${options.accountId}`,
)
return !!response.result
},
executeMsg: async (options: {
@ -239,22 +206,52 @@ export default function createBroadcastSlice(
}
const response = await get().executeMsg({ msg, fee: options.fee, funds: [] })
if (response.result?.response.code === 0) {
set({
toast: {
message: `Repayed ${formatAmountWithSymbol(options.coin)} to Account ${
options.accountId
}`,
},
})
} else {
set({
toast: {
message: response.error ?? `Transaction failed: ${response.error}`,
isError: true,
},
})
handleResponseMessages(
response,
`Repayed ${formatAmountWithSymbol(options.coin)} to Account ${options.accountId}`,
)
return !!response.result
},
lend: async (options: { fee: StdFee; accountId: string; coin: Coin }) => {
const msg = {
update_credit_account: {
account_id: options.accountId,
actions: [
{
lend: options.coin,
},
],
},
}
const response = await get().executeMsg({ msg, fee: options.fee })
handleResponseMessages(
response,
`Successfully deposited ${formatAmountWithSymbol(options.coin)}`,
)
return !!response.result
},
reclaim: async (options: { fee: StdFee; accountId: string; coin: Coin }) => {
const reclaim = { denom: options.coin.denom, amount: { exact: BN(options.coin.amount) } }
const msg = {
update_credit_account: {
account_id: options.accountId,
actions: [
{
reclaim,
},
],
},
}
const response = await get().executeMsg({ msg, fee: options.fee })
handleResponseMessages(
response,
`Successfully deposited ${formatAmountWithSymbol(options.coin)}`,
)
return !!response.result
},
}

View File

@ -9,6 +9,7 @@ export default function createModalSlice(set: SetState<ModalSlice>, get: GetStat
fundAccountModal: false,
fundAndWithdrawModal: null,
unlockModal: null,
lendAndReclaimModal: null,
vaultModal: null,
}
}

View File

@ -0,0 +1,284 @@
// @ts-nocheck
/**
* This file was automatically generated by @cosmwasm/ts-codegen@0.30.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/
import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { Coin, StdFee } from '@cosmjs/amino'
import {
InstantiateMsg,
ExecuteMsg,
Uint128,
Addr,
OwnerUpdate,
QueryMsg,
Decimal,
AssetIncentiveResponse,
ArrayOfAssetIncentiveResponse,
ConfigResponse,
} from './MarsIncentives.types'
export interface MarsIncentivesReadOnlyInterface {
contractAddress: string
config: () => Promise<ConfigResponse>
assetIncentive: ({ denom }: { denom: string }) => Promise<AssetIncentiveResponse>
assetIncentives: ({
limit,
startAfter,
}: {
limit?: number
startAfter?: string
}) => Promise<ArrayOfAssetIncentiveResponse>
userUnclaimedRewards: ({ user }: { user: string }) => Promise<Uint128>
}
export class MarsIncentivesQueryClient implements MarsIncentivesReadOnlyInterface {
client: CosmWasmClient
contractAddress: string
constructor(client: CosmWasmClient, contractAddress: string) {
this.client = client
this.contractAddress = contractAddress
this.config = this.config.bind(this)
this.assetIncentive = this.assetIncentive.bind(this)
this.assetIncentives = this.assetIncentives.bind(this)
this.userUnclaimedRewards = this.userUnclaimedRewards.bind(this)
}
config = async (): Promise<ConfigResponse> => {
return this.client.queryContractSmart(this.contractAddress, {
config: {},
})
}
assetIncentive = async ({ denom }: { denom: string }): Promise<AssetIncentiveResponse> => {
return this.client.queryContractSmart(this.contractAddress, {
asset_incentive: {
denom,
},
})
}
assetIncentives = async ({
limit,
startAfter,
}: {
limit?: number
startAfter?: string
}): Promise<ArrayOfAssetIncentiveResponse> => {
return this.client.queryContractSmart(this.contractAddress, {
asset_incentives: {
limit,
start_after: startAfter,
},
})
}
userUnclaimedRewards = async ({ user }: { user: string }): Promise<Uint128> => {
return this.client.queryContractSmart(this.contractAddress, {
user_unclaimed_rewards: {
user,
},
})
}
}
export interface MarsIncentivesInterface extends MarsIncentivesReadOnlyInterface {
contractAddress: string
sender: string
setAssetIncentive: (
{
denom,
duration,
emissionPerSecond,
startTime,
}: {
denom: string
duration?: number
emissionPerSecond?: Uint128
startTime?: number
},
fee?: number | StdFee | 'auto',
memo?: string,
_funds?: Coin[],
) => Promise<ExecuteResult>
balanceChange: (
{
denom,
totalAmountScaledBefore,
userAddr,
userAmountScaledBefore,
}: {
denom: string
totalAmountScaledBefore: Uint128
userAddr: Addr
userAmountScaledBefore: Uint128
},
fee?: number | StdFee | 'auto',
memo?: string,
_funds?: Coin[],
) => Promise<ExecuteResult>
claimRewards: (
fee?: number | StdFee | 'auto',
memo?: string,
_funds?: Coin[],
) => Promise<ExecuteResult>
updateConfig: (
{
addressProvider,
marsDenom,
}: {
addressProvider?: string
marsDenom?: string
},
fee?: number | StdFee | 'auto',
memo?: string,
_funds?: Coin[],
) => Promise<ExecuteResult>
updateOwner: (
ownerUpdate: OwnerUpdate,
fee?: number | StdFee | 'auto',
memo?: string,
_funds?: Coin[],
) => Promise<ExecuteResult>
}
export class MarsIncentivesClient
extends MarsIncentivesQueryClient
implements MarsIncentivesInterface
{
client: SigningCosmWasmClient
sender: string
contractAddress: string
constructor(client: SigningCosmWasmClient, sender: string, contractAddress: string) {
super(client, contractAddress)
this.client = client
this.sender = sender
this.contractAddress = contractAddress
this.setAssetIncentive = this.setAssetIncentive.bind(this)
this.balanceChange = this.balanceChange.bind(this)
this.claimRewards = this.claimRewards.bind(this)
this.updateConfig = this.updateConfig.bind(this)
this.updateOwner = this.updateOwner.bind(this)
}
setAssetIncentive = async (
{
denom,
duration,
emissionPerSecond,
startTime,
}: {
denom: string
duration?: number
emissionPerSecond?: Uint128
startTime?: number
},
fee: number | StdFee | 'auto' = 'auto',
memo?: string,
_funds?: Coin[],
): Promise<ExecuteResult> => {
return await this.client.execute(
this.sender,
this.contractAddress,
{
set_asset_incentive: {
denom,
duration,
emission_per_second: emissionPerSecond,
start_time: startTime,
},
},
fee,
memo,
_funds,
)
}
balanceChange = async (
{
denom,
totalAmountScaledBefore,
userAddr,
userAmountScaledBefore,
}: {
denom: string
totalAmountScaledBefore: Uint128
userAddr: Addr
userAmountScaledBefore: Uint128
},
fee: number | StdFee | 'auto' = 'auto',
memo?: string,
_funds?: Coin[],
): Promise<ExecuteResult> => {
return await this.client.execute(
this.sender,
this.contractAddress,
{
balance_change: {
denom,
total_amount_scaled_before: totalAmountScaledBefore,
user_addr: userAddr,
user_amount_scaled_before: userAmountScaledBefore,
},
},
fee,
memo,
_funds,
)
}
claimRewards = async (
fee: number | StdFee | 'auto' = 'auto',
memo?: string,
_funds?: Coin[],
): Promise<ExecuteResult> => {
return await this.client.execute(
this.sender,
this.contractAddress,
{
claim_rewards: {},
},
fee,
memo,
_funds,
)
}
updateConfig = async (
{
addressProvider,
marsDenom,
}: {
addressProvider?: string
marsDenom?: string
},
fee: number | StdFee | 'auto' = 'auto',
memo?: string,
_funds?: Coin[],
): Promise<ExecuteResult> => {
return await this.client.execute(
this.sender,
this.contractAddress,
{
update_config: {
address_provider: addressProvider,
mars_denom: marsDenom,
},
},
fee,
memo,
_funds,
)
}
updateOwner = async (
ownerUpdate: OwnerUpdate,
fee: number | StdFee | 'auto' = 'auto',
memo?: string,
_funds?: Coin[],
): Promise<ExecuteResult> => {
return await this.client.execute(
this.sender,
this.contractAddress,
{
update_owner: ownerUpdate,
},
fee,
memo,
_funds,
)
}
}

View File

@ -0,0 +1,254 @@
// @ts-nocheck
/**
* This file was automatically generated by @cosmwasm/ts-codegen@0.30.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/
import { UseQueryOptions, useQuery, useMutation, UseMutationOptions } from '@tanstack/react-query'
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
import { StdFee, Coin } from '@cosmjs/amino'
import {
InstantiateMsg,
ExecuteMsg,
Uint128,
Addr,
OwnerUpdate,
QueryMsg,
Decimal,
AssetIncentiveResponse,
ArrayOfAssetIncentiveResponse,
ConfigResponse,
} from './MarsIncentives.types'
import { MarsIncentivesQueryClient, MarsIncentivesClient } from './MarsIncentives.client'
export const marsIncentivesQueryKeys = {
contract: [
{
contract: 'marsIncentives',
},
] as const,
address: (contractAddress: string | undefined) =>
[{ ...marsIncentivesQueryKeys.contract[0], address: contractAddress }] as const,
config: (contractAddress: string | undefined, args?: Record<string, unknown>) =>
[{ ...marsIncentivesQueryKeys.address(contractAddress)[0], method: 'config', args }] as const,
assetIncentive: (contractAddress: string | undefined, args?: Record<string, unknown>) =>
[
{ ...marsIncentivesQueryKeys.address(contractAddress)[0], method: 'asset_incentive', args },
] as const,
assetIncentives: (contractAddress: string | undefined, args?: Record<string, unknown>) =>
[
{ ...marsIncentivesQueryKeys.address(contractAddress)[0], method: 'asset_incentives', args },
] as const,
userUnclaimedRewards: (contractAddress: string | undefined, args?: Record<string, unknown>) =>
[
{
...marsIncentivesQueryKeys.address(contractAddress)[0],
method: 'user_unclaimed_rewards',
args,
},
] as const,
}
export interface MarsIncentivesReactQuery<TResponse, TData = TResponse> {
client: MarsIncentivesQueryClient | undefined
options?: Omit<
UseQueryOptions<TResponse, Error, TData>,
"'queryKey' | 'queryFn' | 'initialData'"
> & {
initialData?: undefined
}
}
export interface MarsIncentivesUserUnclaimedRewardsQuery<TData>
extends MarsIncentivesReactQuery<Uint128, TData> {
args: {
user: string
}
}
export function useMarsIncentivesUserUnclaimedRewardsQuery<TData = Uint128>({
client,
args,
options,
}: MarsIncentivesUserUnclaimedRewardsQuery<TData>) {
return useQuery<Uint128, Error, TData>(
marsIncentivesQueryKeys.userUnclaimedRewards(client?.contractAddress, args),
() =>
client
? client.userUnclaimedRewards({
user: args.user,
})
: Promise.reject(new Error('Invalid client')),
{ ...options, enabled: !!client && (options?.enabled != undefined ? options.enabled : true) },
)
}
export interface MarsIncentivesAssetIncentivesQuery<TData>
extends MarsIncentivesReactQuery<ArrayOfAssetIncentiveResponse, TData> {
args: {
limit?: number
startAfter?: string
}
}
export function useMarsIncentivesAssetIncentivesQuery<TData = ArrayOfAssetIncentiveResponse>({
client,
args,
options,
}: MarsIncentivesAssetIncentivesQuery<TData>) {
return useQuery<ArrayOfAssetIncentiveResponse, Error, TData>(
marsIncentivesQueryKeys.assetIncentives(client?.contractAddress, args),
() =>
client
? client.assetIncentives({
limit: args.limit,
startAfter: args.startAfter,
})
: Promise.reject(new Error('Invalid client')),
{ ...options, enabled: !!client && (options?.enabled != undefined ? options.enabled : true) },
)
}
export interface MarsIncentivesAssetIncentiveQuery<TData>
extends MarsIncentivesReactQuery<AssetIncentiveResponse, TData> {
args: {
denom: string
}
}
export function useMarsIncentivesAssetIncentiveQuery<TData = AssetIncentiveResponse>({
client,
args,
options,
}: MarsIncentivesAssetIncentiveQuery<TData>) {
return useQuery<AssetIncentiveResponse, Error, TData>(
marsIncentivesQueryKeys.assetIncentive(client?.contractAddress, args),
() =>
client
? client.assetIncentive({
denom: args.denom,
})
: Promise.reject(new Error('Invalid client')),
{ ...options, enabled: !!client && (options?.enabled != undefined ? options.enabled : true) },
)
}
export interface MarsIncentivesConfigQuery<TData>
extends MarsIncentivesReactQuery<ConfigResponse, TData> {}
export function useMarsIncentivesConfigQuery<TData = ConfigResponse>({
client,
options,
}: MarsIncentivesConfigQuery<TData>) {
return useQuery<ConfigResponse, Error, TData>(
marsIncentivesQueryKeys.config(client?.contractAddress),
() => (client ? client.config() : Promise.reject(new Error('Invalid client'))),
{ ...options, enabled: !!client && (options?.enabled != undefined ? options.enabled : true) },
)
}
export interface MarsIncentivesUpdateOwnerMutation {
client: MarsIncentivesClient
msg: OwnerUpdate
args?: {
fee?: number | StdFee | 'auto'
memo?: string
funds?: Coin[]
}
}
export function useMarsIncentivesUpdateOwnerMutation(
options?: Omit<
UseMutationOptions<ExecuteResult, Error, MarsIncentivesUpdateOwnerMutation>,
'mutationFn'
>,
) {
return useMutation<ExecuteResult, Error, MarsIncentivesUpdateOwnerMutation>(
({ client, msg, args: { fee, memo, funds } = {} }) => client.updateOwner(msg, fee, memo, funds),
options,
)
}
export interface MarsIncentivesUpdateConfigMutation {
client: MarsIncentivesClient
msg: {
addressProvider?: string
marsDenom?: string
}
args?: {
fee?: number | StdFee | 'auto'
memo?: string
funds?: Coin[]
}
}
export function useMarsIncentivesUpdateConfigMutation(
options?: Omit<
UseMutationOptions<ExecuteResult, Error, MarsIncentivesUpdateConfigMutation>,
'mutationFn'
>,
) {
return useMutation<ExecuteResult, Error, MarsIncentivesUpdateConfigMutation>(
({ client, msg, args: { fee, memo, funds } = {} }) =>
client.updateConfig(msg, fee, memo, funds),
options,
)
}
export interface MarsIncentivesClaimRewardsMutation {
client: MarsIncentivesClient
args?: {
fee?: number | StdFee | 'auto'
memo?: string
funds?: Coin[]
}
}
export function useMarsIncentivesClaimRewardsMutation(
options?: Omit<
UseMutationOptions<ExecuteResult, Error, MarsIncentivesClaimRewardsMutation>,
'mutationFn'
>,
) {
return useMutation<ExecuteResult, Error, MarsIncentivesClaimRewardsMutation>(
({ client, args: { fee, memo, funds } = {} }) => client.claimRewards(fee, memo, funds),
options,
)
}
export interface MarsIncentivesBalanceChangeMutation {
client: MarsIncentivesClient
msg: {
denom: string
totalAmountScaledBefore: Uint128
userAddr: Addr
userAmountScaledBefore: Uint128
}
args?: {
fee?: number | StdFee | 'auto'
memo?: string
funds?: Coin[]
}
}
export function useMarsIncentivesBalanceChangeMutation(
options?: Omit<
UseMutationOptions<ExecuteResult, Error, MarsIncentivesBalanceChangeMutation>,
'mutationFn'
>,
) {
return useMutation<ExecuteResult, Error, MarsIncentivesBalanceChangeMutation>(
({ client, msg, args: { fee, memo, funds } = {} }) =>
client.balanceChange(msg, fee, memo, funds),
options,
)
}
export interface MarsIncentivesSetAssetIncentiveMutation {
client: MarsIncentivesClient
msg: {
denom: string
duration?: number
emissionPerSecond?: Uint128
startTime?: number
}
args?: {
fee?: number | StdFee | 'auto'
memo?: string
funds?: Coin[]
}
}
export function useMarsIncentivesSetAssetIncentiveMutation(
options?: Omit<
UseMutationOptions<ExecuteResult, Error, MarsIncentivesSetAssetIncentiveMutation>,
'mutationFn'
>,
) {
return useMutation<ExecuteResult, Error, MarsIncentivesSetAssetIncentiveMutation>(
({ client, msg, args: { fee, memo, funds } = {} }) =>
client.setAssetIncentive(msg, fee, memo, funds),
options,
)
}

View File

@ -0,0 +1,94 @@
// @ts-nocheck
/**
* This file was automatically generated by @cosmwasm/ts-codegen@0.30.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/
export interface InstantiateMsg {
address_provider: string
mars_denom: string
owner: string
}
export type ExecuteMsg =
| {
set_asset_incentive: {
denom: string
duration?: number | null
emission_per_second?: Uint128 | null
start_time?: number | null
}
}
| {
balance_change: {
denom: string
total_amount_scaled_before: Uint128
user_addr: Addr
user_amount_scaled_before: Uint128
}
}
| {
claim_rewards: {}
}
| {
update_config: {
address_provider?: string | null
mars_denom?: string | null
}
}
| {
update_owner: OwnerUpdate
}
export type Uint128 = string
export type Addr = string
export type OwnerUpdate =
| {
propose_new_owner: {
proposed: string
}
}
| 'clear_proposed'
| 'accept_proposed'
| 'abolish_owner_role'
| {
set_emergency_owner: {
emergency_owner: string
}
}
| 'clear_emergency_owner'
export type QueryMsg =
| {
config: {}
}
| {
asset_incentive: {
denom: string
}
}
| {
asset_incentives: {
limit?: number | null
start_after?: string | null
}
}
| {
user_unclaimed_rewards: {
user: string
}
}
export type Decimal = string
export interface AssetIncentiveResponse {
denom: string
duration: number
emission_per_second: Uint128
index: Decimal
last_updated: number
start_time: number
}
export type ArrayOfAssetIncentiveResponse = AssetIncentiveResponse[]
export interface ConfigResponse {
address_provider: Addr
mars_denom: string
owner?: string | null
proposed_new_owner?: string | null
}

View File

@ -0,0 +1,13 @@
// @ts-nocheck
/**
* This file was automatically generated by @cosmwasm/ts-codegen@0.30.0.
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
*/
import * as _3 from './MarsIncentives.types'
import * as _4 from './MarsIncentives.client'
import * as _5 from './MarsIncentives.react-query'
export namespace contracts {
export const MarsIncentives = { ..._3, ..._4, ..._5 }
}

View File

@ -42,8 +42,9 @@ interface LendingMarketTableData {
marketMaxLtv: number
marketLiquidityRate: number
marketDepositCap: BigNumber
accountLentAmount?: string
marketDepositAmount: BigNumber
accountDepositValue?: BigNumber
accountLentValue?: BigNumber
marketLiquidityAmount: BigNumber
marketLiquidationThreshold: number
}

View File

@ -16,6 +16,8 @@ interface BroadcastSlice {
deposit: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
unlock: (options: { fee: StdFee; vault: Vault; amount: string }) => Promise<boolean>
withdraw: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
lend: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
reclaim: (options: { fee: StdFee; accountId: string; coin: Coin }) => Promise<boolean>
repay: (options: {
fee: StdFee
accountId: string

View File

@ -7,6 +7,13 @@ interface ModalSlice {
fundAndWithdrawModal: 'fund' | 'withdraw' | null
vaultModal: VaultModal | null
unlockModal: UnlockModal | null
lendAndReclaimModal: LendAndReclaimModalConfig | null
}
type LendAndReclaimModalAction = 'lend' | 'reclaim'
interface LendAndReclaimModalConfig {
data: LendingMarketTableData
action: LendAndReclaimModalAction
}
interface BorrowModal {

View File

@ -9,3 +9,5 @@ export const hardcodedFee = {
],
gas: '5000000',
}
export const SECONDS_IN_A_YEAR = 31540000

View File

@ -16,7 +16,11 @@ export function resolvePositionResponse(response: CreditManagerPosition): Accoun
}
export function resolveMarketResponses(responses: RedBankMarket[]): Market[] {
return responses.map((response) => ({
return responses.map(resolveMarketResponse)
}
export function resolveMarketResponse(response: RedBankMarket): Market {
return {
denom: response.denom,
borrowRate: Number(response.borrow_rate),
debtTotalScaled: response.debt_total_scaled,
@ -27,5 +31,5 @@ export function resolveMarketResponses(responses: RedBankMarket[]): Market[] {
maxLtv: Number(response.max_loan_to_value),
liquidityRate: Number(response.liquidity_rate),
liquidationThreshold: Number(response.liquidation_threshold),
}))
}
}