v2.2.0 (#746)
This commit is contained in:
@@ -3,21 +3,25 @@ import { getCreditManagerQueryClient } from 'api/cosmwasm-client'
|
||||
import getDepositedVaults from 'api/vaults/getDepositedVaults'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { Positions } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||
import { resolvePerpsPositions } from 'utils/resolvers'
|
||||
|
||||
export default async function getAccount(accountId?: string): Promise<Account> {
|
||||
export default async function getAccount(
|
||||
chainConfig: ChainConfig,
|
||||
accountId?: string,
|
||||
): Promise<Account> {
|
||||
if (!accountId) return new Promise((_, reject) => reject('No account ID found'))
|
||||
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient()
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient(chainConfig)
|
||||
|
||||
const accountPosition: Positions = await cacheFn(
|
||||
() => creditManagerQueryClient.positions({ accountId: accountId }),
|
||||
positionsCache,
|
||||
`account/${accountId}`,
|
||||
`${chainConfig.id}/account/${accountId}`,
|
||||
)
|
||||
|
||||
const accountKind = await creditManagerQueryClient.accountKind({ accountId: accountId })
|
||||
|
||||
const depositedVaults = await getDepositedVaults(accountId, accountPosition)
|
||||
const depositedVaults = await getDepositedVaults(accountId, chainConfig, accountPosition)
|
||||
|
||||
if (accountPosition) {
|
||||
return {
|
||||
@@ -26,6 +30,7 @@ export default async function getAccount(accountId?: string): Promise<Account> {
|
||||
lends: accountPosition.lends.map((lend) => new BNCoin(lend)),
|
||||
deposits: accountPosition.deposits.map((deposit) => new BNCoin(deposit)),
|
||||
vaults: depositedVaults,
|
||||
perps: resolvePerpsPositions(accountPosition.perps),
|
||||
kind: accountKind,
|
||||
}
|
||||
}
|
||||
|
||||
+104
-59
@@ -1,6 +1,5 @@
|
||||
import { CosmWasmClient } from '@cosmjs/cosmwasm-stargate'
|
||||
|
||||
import { ENV } from 'constants/env'
|
||||
import { ICNSQueryClient } from 'types/classes/ICNSClient.client'
|
||||
import { MarsAccountNftQueryClient } from 'types/generated/mars-account-nft/MarsAccountNft.client'
|
||||
import { MarsCreditManagerQueryClient } from 'types/generated/mars-credit-manager/MarsCreditManager.client'
|
||||
@@ -8,142 +7,187 @@ import { MarsIncentivesQueryClient } from 'types/generated/mars-incentives/MarsI
|
||||
import { MarsMockVaultQueryClient } from 'types/generated/mars-mock-vault/MarsMockVault.client'
|
||||
import { MarsOracleOsmosisQueryClient } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.client'
|
||||
import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.client'
|
||||
import { MarsPerpsQueryClient } from 'types/generated/mars-perps/MarsPerps.client'
|
||||
import { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client'
|
||||
import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client'
|
||||
|
||||
let _cosmWasmClient: CosmWasmClient
|
||||
let _accountNftQueryClient: MarsAccountNftQueryClient
|
||||
let _creditManagerQueryClient: MarsCreditManagerQueryClient
|
||||
let _oracleQueryClient: MarsOracleOsmosisQueryClient
|
||||
let _redBankQueryClient: MarsRedBankQueryClient
|
||||
let _paramsQueryClient: MarsParamsQueryClient
|
||||
let _incentivesQueryClient: MarsIncentivesQueryClient
|
||||
let _swapperOsmosisClient: MarsSwapperOsmosisQueryClient
|
||||
let _ICNSQueryClient: ICNSQueryClient
|
||||
let _cosmWasmClient: Map<string, CosmWasmClient> = new Map()
|
||||
let _accountNftQueryClient: Map<string, MarsAccountNftQueryClient> = new Map()
|
||||
let _creditManagerQueryClient: Map<string, MarsCreditManagerQueryClient> = new Map()
|
||||
let _oracleQueryClient: Map<string, MarsOracleOsmosisQueryClient> = new Map()
|
||||
let _redBankQueryClient: Map<string, MarsRedBankQueryClient> = new Map()
|
||||
let _paramsQueryClient: Map<string, MarsParamsQueryClient> = new Map()
|
||||
let _incentivesQueryClient: Map<string, MarsIncentivesQueryClient> = new Map()
|
||||
let _swapperOsmosisClient: Map<string, MarsSwapperOsmosisQueryClient> = new Map()
|
||||
let _perpsClient: Map<string, MarsPerpsQueryClient> = new Map()
|
||||
let _ICNSQueryClient: Map<string, ICNSQueryClient> = new Map()
|
||||
|
||||
const getClient = async () => {
|
||||
const getClient = async (rpc: string) => {
|
||||
try {
|
||||
if (!_cosmWasmClient) {
|
||||
_cosmWasmClient = await CosmWasmClient.connect(ENV.URL_RPC)
|
||||
if (!_cosmWasmClient.get(rpc)) {
|
||||
const client = await CosmWasmClient.connect(rpc)
|
||||
_cosmWasmClient.set(rpc, client)
|
||||
}
|
||||
|
||||
return _cosmWasmClient
|
||||
return _cosmWasmClient.get(rpc)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getAccountNftQueryClient = async () => {
|
||||
const getAccountNftQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
if (!_accountNftQueryClient) {
|
||||
const client = await getClient()
|
||||
_accountNftQueryClient = new MarsAccountNftQueryClient(client, ENV.ADDRESS_ACCOUNT_NFT)
|
||||
const contract = chainConfig.contracts.accountNft
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
|
||||
if (!_accountNftQueryClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_accountNftQueryClient.set(key, new MarsAccountNftQueryClient(client, contract))
|
||||
}
|
||||
|
||||
return _accountNftQueryClient
|
||||
return _accountNftQueryClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getCreditManagerQueryClient = async () => {
|
||||
const getCreditManagerQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
if (!_creditManagerQueryClient) {
|
||||
const client = await getClient()
|
||||
_creditManagerQueryClient = new MarsCreditManagerQueryClient(
|
||||
client,
|
||||
ENV.ADDRESS_CREDIT_MANAGER,
|
||||
)
|
||||
const contract = chainConfig.contracts.creditManager
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
|
||||
if (!_creditManagerQueryClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_creditManagerQueryClient.set(key, new MarsCreditManagerQueryClient(client, contract))
|
||||
}
|
||||
|
||||
return _creditManagerQueryClient
|
||||
return _creditManagerQueryClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getParamsQueryClient = async () => {
|
||||
const getParamsQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
if (!_paramsQueryClient) {
|
||||
const client = await getClient()
|
||||
_paramsQueryClient = new MarsParamsQueryClient(client, ENV.ADDRESS_PARAMS)
|
||||
const contract = chainConfig.contracts.params
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
|
||||
if (!_paramsQueryClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_paramsQueryClient.set(key, new MarsParamsQueryClient(client, contract))
|
||||
}
|
||||
|
||||
return _paramsQueryClient
|
||||
return _paramsQueryClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getOracleQueryClient = async () => {
|
||||
const getOracleQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
if (!_oracleQueryClient) {
|
||||
const client = await getClient()
|
||||
_oracleQueryClient = new MarsOracleOsmosisQueryClient(client, ENV.ADDRESS_ORACLE)
|
||||
const contract = chainConfig.contracts.oracle
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
|
||||
if (!_oracleQueryClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_oracleQueryClient.set(key, new MarsOracleOsmosisQueryClient(client, contract))
|
||||
}
|
||||
|
||||
return _oracleQueryClient
|
||||
return _oracleQueryClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getRedBankQueryClient = async () => {
|
||||
const getRedBankQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
if (!_redBankQueryClient) {
|
||||
const client = await getClient()
|
||||
_redBankQueryClient = new MarsRedBankQueryClient(client, ENV.ADDRESS_RED_BANK)
|
||||
const contract = chainConfig.contracts.redBank
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
|
||||
if (!_redBankQueryClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_redBankQueryClient.set(key, new MarsRedBankQueryClient(client, contract))
|
||||
}
|
||||
|
||||
return _redBankQueryClient
|
||||
return _redBankQueryClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getVaultQueryClient = async (address: string) => {
|
||||
const getVaultQueryClient = async (chainConfig: ChainConfig, address: string) => {
|
||||
try {
|
||||
const client = await getClient()
|
||||
const client = await getClient(chainConfig.endpoints.rpc)
|
||||
return new MarsMockVaultQueryClient(client, address)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getIncentivesQueryClient = async () => {
|
||||
const getIncentivesQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
if (!_incentivesQueryClient) {
|
||||
const client = await getClient()
|
||||
_incentivesQueryClient = new MarsIncentivesQueryClient(client, ENV.ADDRESS_INCENTIVES)
|
||||
const contract = chainConfig.contracts.incentives
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
if (!_incentivesQueryClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_incentivesQueryClient.set(key, new MarsIncentivesQueryClient(client, contract))
|
||||
}
|
||||
|
||||
return _incentivesQueryClient
|
||||
return _incentivesQueryClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getSwapperQueryClient = async () => {
|
||||
const getSwapperQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
if (!_swapperOsmosisClient) {
|
||||
const client = await getClient()
|
||||
_swapperOsmosisClient = new MarsSwapperOsmosisQueryClient(client, ENV.ADDRESS_SWAPPER)
|
||||
const contract = chainConfig.contracts.swapper
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
if (!_swapperOsmosisClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_swapperOsmosisClient.set(key, new MarsSwapperOsmosisQueryClient(client, contract))
|
||||
}
|
||||
|
||||
return _swapperOsmosisClient
|
||||
return _swapperOsmosisClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getICNSQueryClient = async () => {
|
||||
const getPerpsQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
if (!_ICNSQueryClient) {
|
||||
const client = await getClient()
|
||||
_ICNSQueryClient = new ICNSQueryClient(client)
|
||||
const contract = chainConfig.contracts.perps
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
if (!_perpsClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_perpsClient.set(key, new MarsPerpsQueryClient(client, contract))
|
||||
}
|
||||
|
||||
return _ICNSQueryClient
|
||||
return _perpsClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
const getICNSQueryClient = async (chainConfig: ChainConfig) => {
|
||||
try {
|
||||
const contract = chainConfig.contracts.params
|
||||
const rpc = chainConfig.endpoints.rpc
|
||||
const key = rpc + contract
|
||||
if (!_ICNSQueryClient.get(key)) {
|
||||
const client = await getClient(rpc)
|
||||
_ICNSQueryClient.set(key, new ICNSQueryClient(client))
|
||||
}
|
||||
|
||||
return _ICNSQueryClient.get(key)!
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
@@ -160,4 +204,5 @@ export {
|
||||
getRedBankQueryClient,
|
||||
getSwapperQueryClient,
|
||||
getVaultQueryClient,
|
||||
getPerpsQueryClient,
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { cacheFn, stakingAprCache } from 'api/cache'
|
||||
import { ENV } from 'constants/env'
|
||||
|
||||
export default async function getStakingAprs() {
|
||||
export default async function getStakingAprs(url: string) {
|
||||
try {
|
||||
return cacheFn(() => fetchAprs(), stakingAprCache, `stakingAprs`)
|
||||
return cacheFn(() => fetchAprs(url), stakingAprCache, `stakingAprs`)
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
@@ -13,9 +12,7 @@ interface StakingAprResponse {
|
||||
stats: StakingApr[]
|
||||
}
|
||||
|
||||
async function fetchAprs(): Promise<StakingApr[]> {
|
||||
const url = ENV.STRIDE_APRS
|
||||
|
||||
async function fetchAprs(url: string): Promise<StakingApr[]> {
|
||||
const response = await fetch(url)
|
||||
const body = (await response.json()) as StakingAprResponse
|
||||
return body.stats
|
||||
|
||||
@@ -4,24 +4,29 @@ import getAccounts from 'api/wallets/getAccounts'
|
||||
import { calculateAccountLeverage, getAccountPositionValues, isAccountEmpty } from 'utils/accounts'
|
||||
|
||||
export default async function getHLSStakingAccounts(
|
||||
chainConfig: ChainConfig,
|
||||
address?: string,
|
||||
): Promise<HLSAccountWithStrategy[]> {
|
||||
const accounts = await getAccounts('high_levered_strategy', address)
|
||||
const accounts = await getAccounts('high_levered_strategy', chainConfig, address)
|
||||
const activeAccounts = accounts.filter((account) => !isAccountEmpty(account))
|
||||
const hlsStrategies = await getHLSStakingAssets()
|
||||
const prices = await getPrices()
|
||||
const hlsStrategies = await getHLSStakingAssets(chainConfig)
|
||||
const prices = await getPrices(chainConfig)
|
||||
const hlsAccountsWithStrategy: HLSAccountWithStrategy[] = []
|
||||
|
||||
activeAccounts.forEach((account) => {
|
||||
if (account.deposits.length === 0) return
|
||||
|
||||
const strategy = hlsStrategies.find(
|
||||
(strategy) => strategy.denoms.deposit === account.deposits.at(0).denom,
|
||||
(strategy) => strategy.denoms.deposit === account.deposits[0].denom,
|
||||
)
|
||||
|
||||
if (!strategy) return
|
||||
|
||||
const [deposits, lends, debts, vaults] = getAccountPositionValues(account, prices)
|
||||
const [deposits, lends, debts, vaults] = getAccountPositionValues(
|
||||
account,
|
||||
prices,
|
||||
chainConfig.assets,
|
||||
)
|
||||
|
||||
hlsAccountsWithStrategy.push({
|
||||
...account,
|
||||
@@ -31,7 +36,7 @@ export default async function getHLSStakingAccounts(
|
||||
debt: debts,
|
||||
total: deposits,
|
||||
},
|
||||
leverage: calculateAccountLeverage(account, prices).toNumber(),
|
||||
leverage: calculateAccountLeverage(account, prices, chainConfig.assets).toNumber(),
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
import { getParamsQueryClient } from 'api/cosmwasm-client'
|
||||
import getStakingAprs from 'api/hls/getAprs'
|
||||
import getAssetParams from 'api/params/getAssetParams'
|
||||
import { getAssetByDenom, getStakingAssets } from 'utils/assets'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { BN } from 'utils/helpers'
|
||||
import { resolveHLSStrategies } from 'utils/resolvers'
|
||||
|
||||
export default async function getHLSStakingAssets() {
|
||||
const stakingAssetDenoms = getStakingAssets().map((asset) => asset.denom)
|
||||
const assetParams = await getAssetParams()
|
||||
export default async function getHLSStakingAssets(chainConfig: ChainConfig) {
|
||||
const stakingAssetDenoms = chainConfig.assets
|
||||
.filter((asset) => asset.isStaking)
|
||||
.map((asset) => asset.denom)
|
||||
const assetParams = await getAssetParams(chainConfig)
|
||||
const HLSAssets = assetParams
|
||||
.filter((asset) => stakingAssetDenoms.includes(asset.denom))
|
||||
.filter((asset) => asset.credit_manager.hls)
|
||||
const strategies = resolveHLSStrategies('coin', HLSAssets)
|
||||
|
||||
const client = await getParamsQueryClient()
|
||||
const client = await getParamsQueryClient(chainConfig)
|
||||
const depositCaps$ = strategies.map((strategy) =>
|
||||
client.totalDeposit({ denom: strategy.denoms.deposit }),
|
||||
)
|
||||
|
||||
const aprs = await getStakingAprs()
|
||||
const aprs = await getStakingAprs(chainConfig.endpoints.aprs.stride)
|
||||
|
||||
return Promise.all(depositCaps$).then((depositCaps) => {
|
||||
return depositCaps.map((depositCap, index) => {
|
||||
const borrowSymbol = getAssetByDenom(strategies[index].denoms.borrow)?.symbol
|
||||
const borrowSymbol = chainConfig.assets.find(byDenom(strategies[index].denoms.borrow))?.symbol
|
||||
return {
|
||||
...strategies[index],
|
||||
depositCap: {
|
||||
|
||||
@@ -4,10 +4,10 @@ import { getVaultConfigs } from 'api/vaults/getVaultConfigs'
|
||||
import { BN } from 'utils/helpers'
|
||||
import { resolveHLSStrategies } from 'utils/resolvers'
|
||||
|
||||
export default async function getHLSVaults() {
|
||||
const assetParams = await getAssetParams()
|
||||
const client = await getCreditManagerQueryClient()
|
||||
const vaultConfigs = await getVaultConfigs()
|
||||
export default async function getHLSVaults(chainConfig: ChainConfig) {
|
||||
const assetParams = await getAssetParams(chainConfig)
|
||||
const client = await getCreditManagerQueryClient(chainConfig)
|
||||
const vaultConfigs = await getVaultConfigs(chainConfig)
|
||||
const HLSAssets = assetParams.filter((asset) => asset.credit_manager.hls)
|
||||
const strategies = resolveHLSStrategies('vault', HLSAssets)
|
||||
|
||||
|
||||
@@ -2,28 +2,28 @@ import getTotalActiveEmissionValue from 'api/incentives/getTotalActiveEmissionVa
|
||||
import getMarket from 'api/markets/getMarket'
|
||||
import getUnderlyingLiquidityAmount from 'api/markets/getMarketUnderlyingLiquidityAmount'
|
||||
import getPrice from 'api/prices/getPrice'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { SECONDS_IN_A_YEAR } from 'utils/constants'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default async function calculateAssetIncentivesApy(
|
||||
chainConfig: ChainConfig,
|
||||
denom: string,
|
||||
): Promise<BigNumber | null> {
|
||||
try {
|
||||
const [totalActiveEmissionValue, market] = await Promise.all([
|
||||
getTotalActiveEmissionValue(denom),
|
||||
getMarket(denom),
|
||||
getTotalActiveEmissionValue(chainConfig, denom),
|
||||
getMarket(chainConfig, denom),
|
||||
])
|
||||
|
||||
if (!totalActiveEmissionValue) return null
|
||||
|
||||
const [marketLiquidityAmount, assetPrice] = await Promise.all([
|
||||
getUnderlyingLiquidityAmount(market),
|
||||
getPrice(denom),
|
||||
getUnderlyingLiquidityAmount(chainConfig, market),
|
||||
getPrice(chainConfig, denom),
|
||||
])
|
||||
|
||||
const assetDecimals = (ASSETS.find(byDenom(denom)) as Asset).decimals
|
||||
const assetDecimals = (chainConfig.assets.find(byDenom(denom)) as Asset).decimals
|
||||
|
||||
const marketLiquidityValue = BN(marketLiquidityAmount)
|
||||
.shiftedBy(-assetDecimals)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { cacheFn, emissionsCache } from 'api/cache'
|
||||
import { getIncentivesQueryClient } from 'api/cosmwasm-client'
|
||||
import getPrice from 'api/prices/getPrice'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default async function getTotalActiveEmissionValue(
|
||||
chainConfig: ChainConfig,
|
||||
denom: string,
|
||||
): Promise<BigNumber | null> {
|
||||
try {
|
||||
const client = await getIncentivesQueryClient()
|
||||
const client = await getIncentivesQueryClient(chainConfig)
|
||||
const activeEmissions = await cacheFn(
|
||||
() =>
|
||||
client.activeEmissions({
|
||||
@@ -26,12 +26,12 @@ export default async function getTotalActiveEmissionValue(
|
||||
}
|
||||
|
||||
const prices = await Promise.all(
|
||||
activeEmissions.map((activeEmission) => getPrice(activeEmission.denom)),
|
||||
activeEmissions.map((activeEmission) => getPrice(chainConfig, activeEmission.denom)),
|
||||
)
|
||||
|
||||
return activeEmissions.reduce((accumulation, current, index) => {
|
||||
const price = prices[index]
|
||||
const decimals = ASSETS.find(byDenom(current.denom))?.decimals as number
|
||||
const decimals = chainConfig.assets.find(byDenom(current.denom))?.decimals as number
|
||||
const emissionValue = BN(current.emission_rate).shiftedBy(-decimals).multipliedBy(price)
|
||||
|
||||
return accumulation.plus(emissionValue)
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import { cacheFn, unclaimedRewardsCache } from 'api/cache'
|
||||
import { getIncentivesQueryClient } from 'api/cosmwasm-client'
|
||||
import { ENV } from 'constants/env'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import iterateContractQuery from 'utils/iterateContractQuery'
|
||||
|
||||
export default async function getUnclaimedRewards(accountId: string): Promise<BNCoin[]> {
|
||||
export default async function getUnclaimedRewards(
|
||||
chainConfig: ChainConfig,
|
||||
accountId: string,
|
||||
): Promise<BNCoin[]> {
|
||||
try {
|
||||
const client = await getIncentivesQueryClient()
|
||||
const client = await getIncentivesQueryClient(chainConfig)
|
||||
const unclaimedRewards = await cacheFn(
|
||||
() =>
|
||||
iterateContractQuery(() =>
|
||||
client.userUnclaimedRewards({
|
||||
user: ENV.ADDRESS_CREDIT_MANAGER,
|
||||
user: chainConfig.contracts.creditManager,
|
||||
accountId,
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -2,14 +2,14 @@ import { cacheFn, marketCache } from 'api/cache'
|
||||
import { getParamsQueryClient, getRedBankQueryClient } from 'api/cosmwasm-client'
|
||||
import { resolveMarketResponse } from 'utils/resolvers'
|
||||
|
||||
export default async function getMarket(denom: string): Promise<Market> {
|
||||
return cacheFn(() => fetchMarket(denom), marketCache, denom, 60)
|
||||
export default async function getMarket(chainConfig: ChainConfig, denom: string): Promise<Market> {
|
||||
return cacheFn(() => fetchMarket(chainConfig, denom), marketCache, denom, 60)
|
||||
}
|
||||
|
||||
async function fetchMarket(denom: string) {
|
||||
async function fetchMarket(chainConfig: ChainConfig, denom: string) {
|
||||
try {
|
||||
const redBankClient = await getRedBankQueryClient()
|
||||
const paramsClient = await getParamsQueryClient()
|
||||
const redBankClient = await getRedBankQueryClient(chainConfig)
|
||||
const paramsClient = await getParamsQueryClient(chainConfig)
|
||||
|
||||
const [market, assetParams, assetCap] = await Promise.all([
|
||||
redBankClient.market({ denom }),
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import getMarketLiquidities from 'api/markets/getMarketLiquidities'
|
||||
import getMarkets from 'api/markets/getMarkets'
|
||||
import getPrices from 'api/prices/getPrices'
|
||||
import { getEnabledMarketAssets } from 'utils/assets'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default async function getMarketBorrowings(): Promise<BorrowAsset[]> {
|
||||
const liquidities = await getMarketLiquidities()
|
||||
const enabledAssets = getEnabledMarketAssets()
|
||||
const borrowEnabledMarkets = (await getMarkets()).filter((market: Market) => market.borrowEnabled)
|
||||
const prices = await getPrices()
|
||||
export default async function getMarketBorrowings(
|
||||
chainConfig: ChainConfig,
|
||||
): Promise<BorrowAsset[]> {
|
||||
const liquidities = await getMarketLiquidities(chainConfig)
|
||||
const borrowEnabledMarkets = (await getMarkets(chainConfig)).filter(
|
||||
(market: Market) => market.borrowEnabled,
|
||||
)
|
||||
const prices = await getPrices(chainConfig)
|
||||
|
||||
const borrow: BorrowAsset[] = borrowEnabledMarkets.map((market) => {
|
||||
const price = prices.find((coin) => coin.denom === market.denom)?.amount ?? '1'
|
||||
const amount = liquidities.find((coin) => coin.denom === market.denom)?.amount ?? '0'
|
||||
const asset = enabledAssets.find((asset) => asset.denom === market.denom)!
|
||||
const asset = chainConfig.assets.find((asset) => asset.denom === market.denom)!
|
||||
|
||||
return {
|
||||
...asset,
|
||||
|
||||
@@ -3,10 +3,10 @@ import { getRedBankQueryClient } from 'api/cosmwasm-client'
|
||||
import getMarkets from 'api/markets/getMarkets'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
||||
export default async function getMarketDebts(): Promise<BNCoin[]> {
|
||||
export default async function getMarketDebts(chainConfig: ChainConfig): Promise<BNCoin[]> {
|
||||
try {
|
||||
const markets: Market[] = await getMarkets()
|
||||
const redBankQueryClient = await getRedBankQueryClient()
|
||||
const markets: Market[] = await getMarkets(chainConfig)
|
||||
const redBankQueryClient = await getRedBankQueryClient(chainConfig)
|
||||
|
||||
const debtQueries = markets.map((asset) =>
|
||||
cacheFn(
|
||||
|
||||
@@ -2,10 +2,12 @@ import getMarkets from 'api/markets/getMarkets'
|
||||
import getUnderlyingLiquidityAmount from 'api/markets/getMarketUnderlyingLiquidityAmount'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
||||
export default async function getMarketDeposits(): Promise<BNCoin[]> {
|
||||
export default async function getMarketDeposits(chainConfig: ChainConfig): Promise<BNCoin[]> {
|
||||
try {
|
||||
const markets: Market[] = await getMarkets()
|
||||
const depositQueries = markets.map(getUnderlyingLiquidityAmount)
|
||||
const markets: Market[] = await getMarkets(chainConfig)
|
||||
const depositQueries = markets.map((market) =>
|
||||
getUnderlyingLiquidityAmount(chainConfig, market),
|
||||
)
|
||||
const depositsResults = await Promise.all(depositQueries)
|
||||
|
||||
return depositsResults.map<BNCoin>(
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
import { BN } from 'utils/helpers'
|
||||
import getMarketDeposits from 'api/markets/getMarketDeposits'
|
||||
import getMarketDebts from 'api/markets/getMarketDebts'
|
||||
import getMarketDeposits from 'api/markets/getMarketDeposits'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
||||
export default async function getMarketLiquidities(): Promise<BNCoin[]> {
|
||||
const deposits = await getMarketDeposits()
|
||||
const debts = await getMarketDebts()
|
||||
export default async function getMarketLiquidities(chainConfig: ChainConfig): Promise<BNCoin[]> {
|
||||
const deposits = await getMarketDeposits(chainConfig)
|
||||
const debts = await getMarketDebts(chainConfig)
|
||||
|
||||
const liquidity: BNCoin[] = deposits.map((deposit) => {
|
||||
const debt = debts.find((debt) => debt.denom === deposit.denom)
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { cacheFn, underlyingLiquidityAmountCache } from 'api/cache'
|
||||
import { getRedBankQueryClient } from 'api/cosmwasm-client'
|
||||
|
||||
export default async function getUnderlyingLiquidityAmount(market: Market): Promise<string> {
|
||||
export default async function getUnderlyingLiquidityAmount(
|
||||
chainConfig: ChainConfig,
|
||||
market: Market,
|
||||
): Promise<string> {
|
||||
return cacheFn(
|
||||
() => fetchUnderlyingLiquidityAmount(market),
|
||||
() => fetchUnderlyingLiquidityAmount(chainConfig, market),
|
||||
underlyingLiquidityAmountCache,
|
||||
`underlyingLiquidity/${market.denom}/amount/${market.collateralTotalScaled}`,
|
||||
60,
|
||||
)
|
||||
}
|
||||
|
||||
async function fetchUnderlyingLiquidityAmount(market: Market) {
|
||||
async function fetchUnderlyingLiquidityAmount(chainConfig: ChainConfig, market: Market) {
|
||||
try {
|
||||
const client = await getRedBankQueryClient()
|
||||
const client = await getRedBankQueryClient(chainConfig)
|
||||
return await client.underlyingLiquidityAmount({
|
||||
denom: market.denom,
|
||||
amountScaled: market.collateralTotalScaled,
|
||||
|
||||
@@ -6,36 +6,46 @@ import {
|
||||
} from 'types/generated/mars-params/MarsParams.types'
|
||||
import { Market as RedBankMarket } from 'types/generated/mars-red-bank/MarsRedBank.types'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { getEnabledMarketAssets } from 'utils/assets'
|
||||
import iterateContractQuery from 'utils/iterateContractQuery'
|
||||
import { resolveMarketResponse } from 'utils/resolvers'
|
||||
|
||||
export default async function getMarkets(): Promise<Market[]> {
|
||||
export default async function getMarkets(chainConfig: ChainConfig): Promise<Market[]> {
|
||||
try {
|
||||
const redBankClient = await getRedBankQueryClient()
|
||||
const paramsClient = await getParamsQueryClient()
|
||||
const redBankClient = await getRedBankQueryClient(chainConfig)
|
||||
const paramsClient = await getParamsQueryClient(chainConfig)
|
||||
|
||||
const enabledAssets = getEnabledMarketAssets()
|
||||
const capQueries = enabledAssets.map((asset) =>
|
||||
const marketAssets = chainConfig.assets.filter((asset) => asset.isMarket)
|
||||
|
||||
const capQueries = marketAssets
|
||||
.filter((asset) => asset.isMarket)
|
||||
.map((asset) =>
|
||||
cacheFn(
|
||||
() => paramsClient.totalDeposit({ denom: asset.denom }),
|
||||
totalDepositCache,
|
||||
`chains/${chainConfig.id}/enabledMarkets/${asset.denom}`,
|
||||
60,
|
||||
),
|
||||
)
|
||||
|
||||
const caps = await Promise.all(capQueries)
|
||||
|
||||
const [markets, assetParams, assetCaps] = await Promise.all([
|
||||
cacheFn(
|
||||
() => paramsClient.totalDeposit({ denom: asset.denom }),
|
||||
totalDepositCache,
|
||||
`enabledMarkets/${asset.denom}`,
|
||||
() => iterateContractQuery(redBankClient.markets),
|
||||
marketsCache,
|
||||
`chains/${chainConfig.id}/markets`,
|
||||
60,
|
||||
),
|
||||
)
|
||||
const [markets, assetParams, assetCaps] = await Promise.all([
|
||||
cacheFn(() => iterateContractQuery(redBankClient.markets), marketsCache, 'markets', 60),
|
||||
cacheFn(
|
||||
async () => await iterateContractQuery(paramsClient.allAssetParams),
|
||||
allParamsCache,
|
||||
'params',
|
||||
`chains/${chainConfig.id}/params`,
|
||||
60,
|
||||
),
|
||||
Promise.all(capQueries),
|
||||
])
|
||||
|
||||
return enabledAssets.map((asset) =>
|
||||
return marketAssets.map((asset) =>
|
||||
resolveMarketResponse(
|
||||
markets.find(byDenom(asset.denom)) as RedBankMarket,
|
||||
assetParams.find(byDenom(asset.denom)) as AssetParams,
|
||||
@@ -43,6 +53,7 @@ export default async function getMarkets(): Promise<Market[]> {
|
||||
),
|
||||
)
|
||||
} catch (ex) {
|
||||
console.log(ex)
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,15 +3,17 @@ import { getParamsQueryClient } from 'api/cosmwasm-client'
|
||||
import { AssetParamsBaseForAddr } from 'types/generated/mars-params/MarsParams.types'
|
||||
import iterateContractQuery from 'utils/iterateContractQuery'
|
||||
|
||||
export default async function getAssetParams(): Promise<AssetParamsBaseForAddr[]> {
|
||||
export default async function getAssetParams(
|
||||
chainConfig: ChainConfig,
|
||||
): Promise<AssetParamsBaseForAddr[]> {
|
||||
try {
|
||||
return await cacheFn(
|
||||
async () => {
|
||||
const paramsQueryClient = await getParamsQueryClient()
|
||||
const paramsQueryClient = await getParamsQueryClient(chainConfig)
|
||||
return iterateContractQuery(paramsQueryClient.allAssetParams)
|
||||
},
|
||||
assetParamsCache,
|
||||
'assetParams',
|
||||
`${chainConfig.id}/assetParams`,
|
||||
600,
|
||||
)
|
||||
} catch (ex) {
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
import { getPerpsQueryClient } from 'api/cosmwasm-client'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
||||
export default async function getOpeningFee(
|
||||
chainConfig: ChainConfig,
|
||||
denom: string,
|
||||
amount: string,
|
||||
) {
|
||||
const perpsClient = await getPerpsQueryClient(chainConfig)
|
||||
|
||||
return perpsClient
|
||||
.openingFee({ denom, size: amount as any })
|
||||
.then((resp) => BNCoin.fromCoin(resp.fee))
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
import getPoolPrice from 'api/prices/getPoolPrice'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { bySymbol } from 'utils/array'
|
||||
|
||||
async function getMarsPrice() {
|
||||
const marsAsset = ASSETS.find(bySymbol('MARS'))
|
||||
async function getMarsPrice(chainConfig: ChainConfig) {
|
||||
const marsAsset = chainConfig.assets.find(bySymbol('MARS'))
|
||||
if (!marsAsset) return 0
|
||||
return await getPoolPrice(marsAsset)
|
||||
return await getPoolPrice(chainConfig, marsAsset)
|
||||
}
|
||||
|
||||
export default getMarsPrice
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { cacheFn, oraclePriceCache } from 'api/cache'
|
||||
import { getOracleQueryClient } from 'api/cosmwasm-client'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { PRICE_ORACLE_DECIMALS } from 'constants/query'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { PriceResponse } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.types'
|
||||
@@ -7,15 +8,18 @@ import { byDenom } from 'utils/array'
|
||||
import { BN } from 'utils/helpers'
|
||||
import iterateContractQuery from 'utils/iterateContractQuery'
|
||||
|
||||
export default async function getOraclePrices(...assets: Asset[]): Promise<BNCoin[]> {
|
||||
export default async function getOraclePrices(
|
||||
chainConfig: ChainConfig,
|
||||
assets: Asset[],
|
||||
): Promise<BNCoin[]> {
|
||||
try {
|
||||
if (!assets.length) return []
|
||||
|
||||
const oracleQueryClient = await getOracleQueryClient()
|
||||
const oracleQueryClient = await getOracleQueryClient(chainConfig)
|
||||
const priceResults = await cacheFn(
|
||||
() => iterateContractQuery(oracleQueryClient.prices),
|
||||
oraclePriceCache,
|
||||
'oraclePrices',
|
||||
`${chainConfig.id}/oraclePrices`,
|
||||
60,
|
||||
)
|
||||
|
||||
@@ -24,7 +28,7 @@ export default async function getOraclePrices(...assets: Asset[]): Promise<BNCoi
|
||||
const decimalDiff = asset.decimals - PRICE_ORACLE_DECIMALS
|
||||
return BNCoin.fromDenomAndBigNumber(
|
||||
asset.denom,
|
||||
BN(priceResponse.price).shiftedBy(decimalDiff),
|
||||
BN(priceResponse?.price ?? BN_ZERO).shiftedBy(decimalDiff),
|
||||
)
|
||||
})
|
||||
} catch (ex) {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { cacheFn, poolPriceCache } from 'api/cache'
|
||||
import getPrice from 'api/prices/getPrice'
|
||||
import { ENV } from 'constants/env'
|
||||
import { BN_ONE } from 'constants/math'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { byDenom, byTokenDenom, partition } from 'utils/array'
|
||||
@@ -17,28 +16,29 @@ interface PoolAsset {
|
||||
}
|
||||
|
||||
export default async function getPoolPrice(
|
||||
chainConfig: ChainConfig,
|
||||
asset: Asset,
|
||||
lookupPricesForBaseAsset?: BNCoin[],
|
||||
): Promise<BigNumber> {
|
||||
if (!asset.poolId) throw 'given asset should have a poolId to fetch the price'
|
||||
|
||||
const [assetRate, baseAsset] = await getAssetRate(asset)
|
||||
const [assetRate, baseAsset] = await getAssetRate(chainConfig, asset)
|
||||
const baseAssetPrice =
|
||||
(lookupPricesForBaseAsset &&
|
||||
lookupPricesForBaseAsset.find(byDenom(baseAsset.token.denom))?.amount) ||
|
||||
(await getPrice(baseAsset.token.denom))
|
||||
(await getPrice(chainConfig, baseAsset.token.denom))
|
||||
|
||||
if (!baseAssetPrice) throw 'base asset price must be available on Pyth or in Oracle contract'
|
||||
|
||||
return assetRate.multipliedBy(baseAssetPrice)
|
||||
}
|
||||
|
||||
const getAssetRate = async (asset: Asset) => {
|
||||
const url = `${ENV.URL_REST}osmosis/gamm/v1beta1/pools/${asset.poolId}`
|
||||
const getAssetRate = async (chainConfig: ChainConfig, asset: Asset) => {
|
||||
const url = chainConfig.endpoints.pools.replace('POOL_ID', asset.poolId!.toString())
|
||||
const response = await cacheFn(
|
||||
() => fetch(url).then((res) => res.json()),
|
||||
poolPriceCache,
|
||||
`poolPrices/${(asset.poolId || 0).toString()}`,
|
||||
`${chainConfig.id}/poolPrices/${(asset.poolId || 0).toString()}`,
|
||||
60,
|
||||
)
|
||||
const pool = response.pool
|
||||
|
||||
+10
-27
@@ -1,37 +1,20 @@
|
||||
import { cacheFn, priceCache } from 'api/cache'
|
||||
import { getOracleQueryClient } from 'api/cosmwasm-client'
|
||||
import getPoolPrice from 'api/prices/getPoolPrice'
|
||||
import getPythPrice from 'api/prices/getPythPrices'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import { PRICE_ORACLE_DECIMALS } from 'constants/query'
|
||||
import getPrices from 'api/prices/getPrices'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default async function getPrice(denom: string): Promise<BigNumber> {
|
||||
return cacheFn(() => fetchPrice(denom), priceCache, `price/${denom}`, 60)
|
||||
export default async function getPrice(
|
||||
chainConfig: ChainConfig,
|
||||
denom: string,
|
||||
): Promise<BigNumber> {
|
||||
return cacheFn(() => fetchPrice(chainConfig, denom), priceCache, `price/${denom}`, 60)
|
||||
}
|
||||
|
||||
async function fetchPrice(denom: string) {
|
||||
async function fetchPrice(chainConfig: ChainConfig, denom: string) {
|
||||
try {
|
||||
const asset = ASSETS.find(byDenom(denom)) as Asset
|
||||
const prices = await getPrices(chainConfig)
|
||||
|
||||
if (asset.pythPriceFeedId) {
|
||||
return (await getPythPrice(asset.pythPriceFeedId))[0]
|
||||
}
|
||||
|
||||
if (asset.hasOraclePrice) {
|
||||
const oracleQueryClient = await getOracleQueryClient()
|
||||
const priceResponse = await oracleQueryClient.price({ denom: asset.denom })
|
||||
const decimalDiff = asset.decimals - PRICE_ORACLE_DECIMALS
|
||||
|
||||
return BN(priceResponse.price).shiftedBy(decimalDiff)
|
||||
}
|
||||
|
||||
if (asset.poolId) {
|
||||
return await getPoolPrice(asset)
|
||||
}
|
||||
|
||||
throw `could not fetch the price info for the given denom: ${denom}`
|
||||
return prices.find(byDenom(denom))?.amount ?? BN_ZERO
|
||||
} catch (ex) {
|
||||
throw ex
|
||||
}
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import fetchPythPriceData from 'api/prices/getPythPriceData'
|
||||
import { getPythAssets } from 'utils/assets'
|
||||
|
||||
export default async function getPricesData(): Promise<string[]> {
|
||||
try {
|
||||
const assetsWithPythPriceFeedId = getPythAssets()
|
||||
const pythAndOraclePriceData = await requestPythPriceData(assetsWithPythPriceFeedId)
|
||||
return pythAndOraclePriceData
|
||||
} catch (ex) {
|
||||
console.error(ex)
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
|
||||
async function requestPythPriceData(assets: Asset[]): Promise<string[]> {
|
||||
if (!assets.length) return []
|
||||
|
||||
const priceFeedIds = assets.map((a) => a.pythPriceFeedId) as string[]
|
||||
return await fetchPythPriceData(...priceFeedIds)
|
||||
}
|
||||
+36
-28
@@ -1,29 +1,35 @@
|
||||
import getOraclePrices from 'api/prices/getOraclePrices'
|
||||
import getPoolPrice from 'api/prices/getPoolPrice'
|
||||
import fetchPythPrices from 'api/prices/getPythPrices'
|
||||
import chains from 'configs/chains'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { partition } from 'utils/array'
|
||||
import { getAssetsMustHavePriceInfo } from 'utils/assets'
|
||||
import { getAllAssetsWithPythId } from 'utils/assets'
|
||||
|
||||
export default async function getPrices(): Promise<BNCoin[]> {
|
||||
export default async function getPrices(chainConfig: ChainConfig): Promise<BNCoin[]> {
|
||||
const usdPrice = new BNCoin({ denom: 'usd', amount: '1' })
|
||||
|
||||
const pythAndOraclePrices = []
|
||||
const assetsToFetchPrices = useStore
|
||||
.getState()
|
||||
.chainConfig.assets.filter(
|
||||
(asset) => (asset.isEnabled && asset.isMarket) || asset.forceFetchPrice,
|
||||
)
|
||||
|
||||
const assetsWithPythPriceFeedId = getAllAssetsWithPythId(chains)
|
||||
const pythPrices = await requestPythPrices(assetsWithPythPriceFeedId)
|
||||
pythAndOraclePrices.push(...pythPrices)
|
||||
|
||||
try {
|
||||
const assetsToFetchPrices = getAssetsMustHavePriceInfo()
|
||||
const [assetsWithPythPriceFeedId, assetsWithOraclePrices, assetsWithPoolIds] =
|
||||
const [assetsWithOraclePrices, assetsWithPoolIds] =
|
||||
separateAssetsByPriceSources(assetsToFetchPrices)
|
||||
const oraclePrices = await getOraclePrices(chainConfig, assetsWithOraclePrices)
|
||||
const poolPrices = await requestPoolPrices(chainConfig, assetsWithPoolIds, pythAndOraclePrices)
|
||||
|
||||
const pythAndOraclePrices = (
|
||||
await Promise.all([
|
||||
requestPythPrices(assetsWithPythPriceFeedId),
|
||||
getOraclePrices(...assetsWithOraclePrices),
|
||||
])
|
||||
).flat()
|
||||
const poolPrices = await requestPoolPrices(assetsWithPoolIds, pythAndOraclePrices)
|
||||
if (oraclePrices) useStore.setState({ isOracleStale: false })
|
||||
|
||||
useStore.setState({ isOracleStale: false })
|
||||
|
||||
return [...pythAndOraclePrices, ...poolPrices, usdPrice]
|
||||
return [...pythAndOraclePrices, ...oraclePrices, ...poolPrices, usdPrice]
|
||||
} catch (ex) {
|
||||
console.error(ex)
|
||||
let message = 'Unknown Error'
|
||||
@@ -31,41 +37,43 @@ export default async function getPrices(): Promise<BNCoin[]> {
|
||||
if (message.includes('price publish time is too old'))
|
||||
useStore.setState({ isOracleStale: true })
|
||||
|
||||
throw ex
|
||||
return [...pythAndOraclePrices, usdPrice]
|
||||
}
|
||||
}
|
||||
|
||||
async function requestPythPrices(assets: Asset[]): Promise<BNCoin[]> {
|
||||
if (!assets.length) return []
|
||||
|
||||
const priceFeedIds = assets.map((a) => a.pythPriceFeedId) as string[]
|
||||
return await fetchPythPrices(...priceFeedIds).then(mapResponseToBnCoin(assets))
|
||||
const priceFeedIds = assets
|
||||
.map((a) => a.pythPriceFeedId)
|
||||
.filter((priceFeedId, index, array) => array.indexOf(priceFeedId) === index) as string[]
|
||||
return await fetchPythPrices(priceFeedIds, assets)
|
||||
}
|
||||
|
||||
async function requestPoolPrices(assets: Asset[], lookupPrices: BNCoin[]): Promise<BNCoin[]> {
|
||||
const requests = assets.map((asset) => getPoolPrice(asset, lookupPrices))
|
||||
async function requestPoolPrices(
|
||||
chainConfig: ChainConfig,
|
||||
assets: Asset[],
|
||||
lookupPrices: BNCoin[],
|
||||
): Promise<BNCoin[]> {
|
||||
const requests = assets.map((asset) => getPoolPrice(chainConfig, asset, lookupPrices))
|
||||
|
||||
return await Promise.all(requests).then(mapResponseToBnCoin(assets))
|
||||
}
|
||||
|
||||
const mapResponseToBnCoin = (assets: Asset[]) => (prices: BigNumber[]) =>
|
||||
prices.map((price: BigNumber, index: number) =>
|
||||
const mapResponseToBnCoin = (assets: Asset[]) => (prices: BigNumber[]) => {
|
||||
return prices.map((price: BigNumber, index: number) =>
|
||||
BNCoin.fromDenomAndBigNumber(assets[index].denom, price),
|
||||
)
|
||||
}
|
||||
|
||||
function separateAssetsByPriceSources(assets: Asset[]) {
|
||||
// Only fetch Pyth prices for mainnet
|
||||
const [assetsWithPythPriceFeedId, assetsWithoutPythPriceFeedId] = partition(
|
||||
assets,
|
||||
(asset) => !!asset.pythPriceFeedId,
|
||||
)
|
||||
const assetsWithoutPythPriceFeedId = assets.filter((asset) => !asset.pythPriceFeedId)
|
||||
|
||||
// Don't get oracle price if it's not mainnet and there is a poolId
|
||||
const [assetsWithOraclePrice, assetsWithoutOraclePrice] = partition(
|
||||
assetsWithoutPythPriceFeedId,
|
||||
(asset) => asset.hasOraclePrice || !asset.poolId,
|
||||
)
|
||||
const assetsWithPoolId = assetsWithoutOraclePrice.filter((asset) => !!asset.poolId)
|
||||
|
||||
return [assetsWithPythPriceFeedId, assetsWithOraclePrice, assetsWithPoolId]
|
||||
return [assetsWithOraclePrice, assetsWithPoolId]
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { cacheFn, pythPriceCache } from 'api/cache'
|
||||
import { ENV } from 'constants/env'
|
||||
import { pythEndpoints } from 'constants/pyth'
|
||||
|
||||
export default async function fetchPythPriceData(...priceFeedIds: string[]) {
|
||||
export default async function getPythPriceData(priceFeedIds: string[]) {
|
||||
try {
|
||||
const pricesUrl = new URL(`${ENV.PYTH_ENDPOINT}/latest_vaas`)
|
||||
const pricesUrl = new URL(`${pythEndpoints.api}/latest_vaas`)
|
||||
priceFeedIds.forEach((id) => pricesUrl.searchParams.append('ids[]', id))
|
||||
|
||||
const pythDataResponse: string[] = await cacheFn(
|
||||
@@ -14,6 +14,7 @@ export default async function fetchPythPriceData(...priceFeedIds: string[]) {
|
||||
)
|
||||
return pythDataResponse
|
||||
} catch (ex) {
|
||||
throw ex
|
||||
console.log(ex)
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { cacheFn, pythPriceCache } from 'api/cache'
|
||||
import { ENV } from 'constants/env'
|
||||
import { pythEndpoints } from 'constants/pyth'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default async function fetchPythPrices(...priceFeedIds: string[]) {
|
||||
export default async function fetchPythPrices(priceFeedIds: string[], assets: Asset[]) {
|
||||
try {
|
||||
const pricesUrl = new URL(`${ENV.PYTH_ENDPOINT}/latest_price_feeds`)
|
||||
const pricesUrl = new URL(`${pythEndpoints.api}/latest_price_feeds`)
|
||||
priceFeedIds.forEach((id) => pricesUrl.searchParams.append('ids[]', id))
|
||||
|
||||
const pythResponse: PythPriceData[] = await cacheFn(
|
||||
@@ -14,7 +15,18 @@ export default async function fetchPythPrices(...priceFeedIds: string[]) {
|
||||
30,
|
||||
)
|
||||
|
||||
return pythResponse.map(({ price }) => BN(price.price).shiftedBy(price.expo))
|
||||
const mappedPriceData = [] as BNCoin[]
|
||||
|
||||
assets.forEach((asset) => {
|
||||
const price = pythResponse.find((pythPrice) => asset.pythPriceFeedId === pythPrice.id)?.price
|
||||
if (price)
|
||||
mappedPriceData.push(
|
||||
BNCoin.fromDenomAndBigNumber(asset.denom, BN(price.price).shiftedBy(price.expo)),
|
||||
)
|
||||
return
|
||||
})
|
||||
|
||||
return mappedPriceData
|
||||
} catch (ex) {
|
||||
throw ex
|
||||
}
|
||||
|
||||
@@ -2,9 +2,13 @@ import { getSwapperQueryClient } from 'api/cosmwasm-client'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export default async function estimateExactIn(coinIn: Coin, denomOut: string) {
|
||||
export default async function estimateExactIn(
|
||||
chainConfig: ChainConfig,
|
||||
coinIn: Coin,
|
||||
denomOut: string,
|
||||
) {
|
||||
try {
|
||||
const swapperClient = await getSwapperQueryClient()
|
||||
const swapperClient = await getSwapperQueryClient(chainConfig)
|
||||
const estimatedAmount = (await swapperClient.estimateExactInSwap({ coinIn, denomOut })).amount
|
||||
|
||||
return BN(estimatedAmount)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { STANDARD_SWAP_FEE } from 'utils/constants'
|
||||
|
||||
export default async function getOsmosisSwapFee(
|
||||
chainConfig: ChainConfig,
|
||||
poolIds: string[],
|
||||
): Promise<number> {
|
||||
const promises = poolIds.map((poolId) =>
|
||||
fetch(chainConfig.endpoints.pools.replace('POOL_ID', poolId)),
|
||||
)
|
||||
|
||||
const responses = await Promise.all(promises)
|
||||
|
||||
const pools = await Promise.all(responses.map(async (pool) => (await pool.json()).pool as Pool))
|
||||
|
||||
if (!pools?.length) return STANDARD_SWAP_FEE
|
||||
|
||||
return pools
|
||||
.reduce((acc, pool) => acc.plus(pool?.pool_params?.swap_fee || STANDARD_SWAP_FEE), BN_ZERO)
|
||||
.toNumber()
|
||||
}
|
||||
|
||||
interface Pool {
|
||||
'@type': string
|
||||
address: string
|
||||
future_pool_governor: string
|
||||
id: string
|
||||
pool_assets?: PoolAsset[]
|
||||
pool_liquidity?: PoolLiquidity[]
|
||||
pool_params: PoolParams
|
||||
total_shares: TotalShares
|
||||
total_weight: string
|
||||
}
|
||||
|
||||
interface PoolAsset {
|
||||
token: TotalShares
|
||||
weight: string
|
||||
}
|
||||
|
||||
interface PoolLiquidity {
|
||||
amount: string
|
||||
denom: string
|
||||
}
|
||||
interface TotalShares {
|
||||
amount: string
|
||||
denom: string
|
||||
}
|
||||
|
||||
interface PoolParams {
|
||||
exit_fee: string
|
||||
smooth_weight_change_params: null
|
||||
swap_fee: string
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { ENV } from 'constants/env'
|
||||
|
||||
const url = `${ENV.URL_REST}osmosis/gamm/v1beta1/pools/`
|
||||
export default async function getPools(poolIds: string[]): Promise<Pool[]> {
|
||||
const promises = poolIds.map((poolId) => fetch(url + poolId))
|
||||
|
||||
const responses = await Promise.all(promises)
|
||||
|
||||
return await Promise.all(responses.map(async (pool) => (await pool.json()).pool as Pool))
|
||||
}
|
||||
|
||||
interface Pool {
|
||||
'@type': string
|
||||
address: string
|
||||
future_pool_governor: string
|
||||
id: string
|
||||
pool_assets?: PoolAsset[]
|
||||
pool_liquidity?: PoolLiquidity[]
|
||||
pool_params: PoolParams
|
||||
total_shares: TotalShares
|
||||
total_weight: string
|
||||
}
|
||||
|
||||
interface PoolAsset {
|
||||
token: TotalShares
|
||||
weight: string
|
||||
}
|
||||
|
||||
interface PoolLiquidity {
|
||||
amount: string
|
||||
denom: string
|
||||
}
|
||||
interface TotalShares {
|
||||
amount: string
|
||||
denom: string
|
||||
}
|
||||
|
||||
interface PoolParams {
|
||||
exit_fee: string
|
||||
smooth_weight_change_params: null
|
||||
swap_fee: string
|
||||
}
|
||||
@@ -1,8 +1,12 @@
|
||||
import { getSwapperQueryClient } from 'api/cosmwasm-client'
|
||||
|
||||
export default async function getSwapRoute(denomIn: string, denomOut: string): Promise<Route[]> {
|
||||
export default async function getSwapRoute(
|
||||
chainConfig: ChainConfig,
|
||||
denomIn: string,
|
||||
denomOut: string,
|
||||
): Promise<Route[]> {
|
||||
try {
|
||||
const swapperClient = await getSwapperQueryClient()
|
||||
const swapperClient = await getSwapperQueryClient(chainConfig)
|
||||
const routes = await swapperClient.route({
|
||||
denomIn,
|
||||
denomOut,
|
||||
|
||||
@@ -21,9 +21,13 @@ import {
|
||||
import { getCoinValue } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
async function getUnlocksAtTimestamp(unlockingId: number, vaultAddress: string) {
|
||||
async function getUnlocksAtTimestamp(
|
||||
chainConfig: ChainConfig,
|
||||
unlockingId: number,
|
||||
vaultAddress: string,
|
||||
) {
|
||||
try {
|
||||
const client = await getClient()
|
||||
const client = await getClient(chainConfig.endpoints.rpc)
|
||||
|
||||
const vaultExtension = (await cacheFn(
|
||||
() =>
|
||||
@@ -42,6 +46,7 @@ async function getUnlocksAtTimestamp(unlockingId: number, vaultAddress: string)
|
||||
}
|
||||
|
||||
async function getVaultPositionStatusAndUnlockIdAndUnlockTime(
|
||||
chainConfig: ChainConfig,
|
||||
vaultPosition: VaultPosition,
|
||||
): Promise<[VaultStatus, number | undefined, number | undefined]> {
|
||||
const amount = vaultPosition.amount
|
||||
@@ -50,7 +55,11 @@ async function getVaultPositionStatusAndUnlockIdAndUnlockTime(
|
||||
|
||||
if (amount.locking.unlocking.length) {
|
||||
const unlockId = amount.locking.unlocking[0].id
|
||||
const unlocksAtTimestamp = await getUnlocksAtTimestamp(unlockId, vaultPosition.vault.address)
|
||||
const unlocksAtTimestamp = await getUnlocksAtTimestamp(
|
||||
chainConfig,
|
||||
unlockId,
|
||||
vaultPosition.vault.address,
|
||||
)
|
||||
|
||||
if (moment(unlocksAtTimestamp).isBefore(new Date())) {
|
||||
return [VaultStatus.UNLOCKED, unlockId, unlocksAtTimestamp]
|
||||
@@ -83,12 +92,13 @@ export function flatVaultPositionAmount(
|
||||
}
|
||||
|
||||
export async function getLpTokensForVaultPosition(
|
||||
chainConfig: ChainConfig,
|
||||
vault: Vault,
|
||||
vaultPosition: VaultPosition,
|
||||
): Promise<Coin[]> {
|
||||
try {
|
||||
const vaultQueryClient = await getVaultQueryClient(vault.address)
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient()
|
||||
const vaultQueryClient = await getVaultQueryClient(chainConfig, vault.address)
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient(chainConfig)
|
||||
const amounts = flatVaultPositionAmount(vaultPosition.amount)
|
||||
const totalAmount = amounts.locked.plus(amounts.unlocked).plus(amounts.unlocking).toString()
|
||||
|
||||
@@ -133,15 +143,16 @@ export async function getLpTokensForVaultPosition(
|
||||
async function getVaultValuesAndAmounts(
|
||||
vault: Vault,
|
||||
vaultPosition: VaultPosition,
|
||||
chainConfig: ChainConfig,
|
||||
): Promise<VaultValuesAndAmounts> {
|
||||
try {
|
||||
const pricesQueries = Promise.all([
|
||||
getPrice(vault.denoms.primary),
|
||||
getPrice(vault.denoms.secondary),
|
||||
getPrice(vault.denoms.lp),
|
||||
getPrice(chainConfig, vault.denoms.primary),
|
||||
getPrice(chainConfig, vault.denoms.secondary),
|
||||
getPrice(chainConfig, vault.denoms.lp),
|
||||
])
|
||||
|
||||
const lpTokensQuery = getLpTokensForVaultPosition(vault, vaultPosition)
|
||||
const lpTokensQuery = getLpTokensForVaultPosition(chainConfig, vault, vaultPosition)
|
||||
const amounts = flatVaultPositionAmount(vaultPosition.amount)
|
||||
|
||||
const [[primaryLpToken, secondaryLpToken], [primaryPrice, secondaryPrice, lpPrice]] =
|
||||
@@ -154,18 +165,26 @@ async function getVaultValuesAndAmounts(
|
||||
secondary: BN(secondaryLpToken.amount),
|
||||
},
|
||||
values: {
|
||||
primary: getCoinValue(new BNCoin(primaryLpToken), [
|
||||
BNCoin.fromDenomAndBigNumber(primaryLpToken.denom, primaryPrice),
|
||||
]),
|
||||
secondary: getCoinValue(new BNCoin(secondaryLpToken), [
|
||||
BNCoin.fromDenomAndBigNumber(secondaryLpToken.denom, secondaryPrice),
|
||||
]),
|
||||
unlocking: getCoinValue(BNCoin.fromDenomAndBigNumber(vault.denoms.lp, amounts.unlocking), [
|
||||
BNCoin.fromDenomAndBigNumber(vault.denoms.lp, lpPrice),
|
||||
]),
|
||||
unlocked: getCoinValue(BNCoin.fromDenomAndBigNumber(vault.denoms.lp, amounts.unlocked), [
|
||||
BNCoin.fromDenomAndBigNumber(vault.denoms.lp, lpPrice),
|
||||
]),
|
||||
primary: getCoinValue(
|
||||
new BNCoin(primaryLpToken),
|
||||
[BNCoin.fromDenomAndBigNumber(primaryLpToken.denom, primaryPrice)],
|
||||
chainConfig.assets,
|
||||
),
|
||||
secondary: getCoinValue(
|
||||
new BNCoin(secondaryLpToken),
|
||||
[BNCoin.fromDenomAndBigNumber(secondaryLpToken.denom, secondaryPrice)],
|
||||
chainConfig.assets,
|
||||
),
|
||||
unlocking: getCoinValue(
|
||||
BNCoin.fromDenomAndBigNumber(vault.denoms.lp, amounts.unlocking),
|
||||
[BNCoin.fromDenomAndBigNumber(vault.denoms.lp, lpPrice)],
|
||||
chainConfig.assets,
|
||||
),
|
||||
unlocked: getCoinValue(
|
||||
BNCoin.fromDenomAndBigNumber(vault.denoms.lp, amounts.unlocked),
|
||||
[BNCoin.fromDenomAndBigNumber(vault.denoms.lp, lpPrice)],
|
||||
chainConfig.assets,
|
||||
),
|
||||
},
|
||||
}
|
||||
} catch (ex) {
|
||||
@@ -175,10 +194,11 @@ async function getVaultValuesAndAmounts(
|
||||
|
||||
async function getDepositedVaults(
|
||||
accountId: string,
|
||||
chainConfig: ChainConfig,
|
||||
positions?: Positions,
|
||||
): Promise<DepositedVault[]> {
|
||||
try {
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient()
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient(chainConfig)
|
||||
|
||||
if (!positions)
|
||||
positions = await cacheFn(
|
||||
@@ -189,7 +209,7 @@ async function getDepositedVaults(
|
||||
|
||||
if (!positions.vaults.length) return []
|
||||
|
||||
const [allVaults] = await Promise.all([getVaults()])
|
||||
const [allVaults] = await Promise.all([getVaults(chainConfig)])
|
||||
|
||||
const depositedVaults = positions.vaults.map(async (vaultPosition) => {
|
||||
const vault = allVaults.find((v) => v.address === vaultPosition.vault.address)
|
||||
@@ -199,8 +219,8 @@ async function getDepositedVaults(
|
||||
}
|
||||
|
||||
const [[status, unlockId, unlocksAt], valuesAndAmounts] = await Promise.all([
|
||||
getVaultPositionStatusAndUnlockIdAndUnlockTime(vaultPosition),
|
||||
getVaultValuesAndAmounts(vault, vaultPosition),
|
||||
getVaultPositionStatusAndUnlockIdAndUnlockTime(chainConfig, vaultPosition),
|
||||
getVaultValuesAndAmounts(vault, vaultPosition, chainConfig),
|
||||
])
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,17 +1,22 @@
|
||||
import { aprsCache, aprsCacheResponse, cacheFn } from 'api/cache'
|
||||
import { ENV } from 'constants/env'
|
||||
|
||||
export default async function getAprs() {
|
||||
export default async function getAprs(chainConfig: ChainConfig) {
|
||||
if (!chainConfig.farm) return []
|
||||
try {
|
||||
const response = await cacheFn(
|
||||
() => fetch(ENV.URL_VAULT_APR),
|
||||
() => fetch(chainConfig.endpoints.aprs.vaults),
|
||||
aprsCacheResponse,
|
||||
'aprsResponse',
|
||||
`${chainConfig.id}/aprsResponse`,
|
||||
60,
|
||||
)
|
||||
|
||||
if (response.ok) {
|
||||
const data: AprResponse = await cacheFn(() => response.json(), aprsCache, 'aprs', 60)
|
||||
const data: AprResponse = await cacheFn(
|
||||
() => response.json(),
|
||||
aprsCache,
|
||||
`${chainConfig.id}/aprs`,
|
||||
60,
|
||||
)
|
||||
|
||||
return data.vaults.map((aprData) => {
|
||||
const finalApr = aprData.apr.projected_apr * 100
|
||||
|
||||
@@ -3,13 +3,15 @@ import { getParamsQueryClient } from 'api/cosmwasm-client'
|
||||
import { VaultConfigBaseForAddr } from 'types/generated/mars-params/MarsParams.types'
|
||||
import iterateContractQuery from 'utils/iterateContractQuery'
|
||||
|
||||
export const getVaultConfigs = async (): Promise<VaultConfigBaseForAddr[]> => {
|
||||
export const getVaultConfigs = async (
|
||||
chainConfig: ChainConfig,
|
||||
): Promise<VaultConfigBaseForAddr[]> => {
|
||||
try {
|
||||
const paramsQueryClient = await getParamsQueryClient()
|
||||
const paramsQueryClient = await getParamsQueryClient(chainConfig)
|
||||
return await cacheFn(
|
||||
() => iterateContractQuery(paramsQueryClient.allVaultConfigs, 'addr'),
|
||||
vaultConfigsCache,
|
||||
'vaultConfigs',
|
||||
`${chainConfig.id}/vaultConfigs`,
|
||||
600,
|
||||
)
|
||||
} catch (ex) {
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { cacheFn, previewDepositCache } from 'api/cache'
|
||||
import { getVaultQueryClient } from 'api/cosmwasm-client'
|
||||
|
||||
export async function getVaultTokenFromLp(
|
||||
vaultAddress: string,
|
||||
lpAmount: string,
|
||||
): Promise<{ vaultAddress: string; amount: string }> {
|
||||
try {
|
||||
const client = await getVaultQueryClient(vaultAddress)
|
||||
|
||||
return cacheFn(
|
||||
() =>
|
||||
client.previewDeposit({ amount: lpAmount }).then((amount) => ({ vaultAddress, amount })),
|
||||
previewDepositCache,
|
||||
`vaults/${vaultAddress}/amounts/${lpAmount}`,
|
||||
30,
|
||||
)
|
||||
} catch (ex) {
|
||||
throw ex
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,13 @@
|
||||
import { cacheFn, vaultUtilizationCache } from 'api/cache'
|
||||
import { getCreditManagerQueryClient } from 'api/cosmwasm-client'
|
||||
import { ENV } from 'constants/env'
|
||||
import { VaultUtilizationResponse } from 'types/generated/mars-credit-manager/MarsCreditManager.types'
|
||||
import { VaultConfigBaseForString } from 'types/generated/mars-params/MarsParams.types'
|
||||
|
||||
export const getVaultUtilizations = async (
|
||||
chainConfig: ChainConfig,
|
||||
vaultConfigs: VaultConfigBaseForString[],
|
||||
): Promise<VaultUtilizationResponse[]> => {
|
||||
if (!ENV.ADDRESS_PARAMS) return []
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient()
|
||||
const creditManagerQueryClient = await getCreditManagerQueryClient(chainConfig)
|
||||
try {
|
||||
const vaultUtilizations$ = vaultConfigs.map((vaultConfig) => {
|
||||
return cacheFn(
|
||||
|
||||
@@ -2,20 +2,16 @@ import getAssetParams from 'api/params/getAssetParams'
|
||||
import getAprs from 'api/vaults/getVaultAprs'
|
||||
import { getVaultConfigs } from 'api/vaults/getVaultConfigs'
|
||||
import { getVaultUtilizations } from 'api/vaults/getVaultUtilizations'
|
||||
import { ENV } from 'constants/env'
|
||||
import { TESTNET_VAULTS_META_DATA, VAULTS_META_DATA } from 'constants/vaults'
|
||||
import { NETWORK } from 'types/enums/network'
|
||||
import { BN } from 'utils/helpers'
|
||||
import { convertAprToApy } from 'utils/parsers'
|
||||
import { resolveHLSStrategies } from 'utils/resolvers'
|
||||
|
||||
export default async function getVaults(): Promise<Vault[]> {
|
||||
const assetParams = await getAssetParams()
|
||||
const vaultConfigs = await getVaultConfigs()
|
||||
const $vaultUtilizations = getVaultUtilizations(vaultConfigs)
|
||||
const $aprs = getAprs()
|
||||
const vaultMetaDatas =
|
||||
ENV.NETWORK === NETWORK.TESTNET ? TESTNET_VAULTS_META_DATA : VAULTS_META_DATA
|
||||
export default async function getVaults(chainConfig: ChainConfig): Promise<Vault[]> {
|
||||
const assetParams = await getAssetParams(chainConfig)
|
||||
const vaultConfigs = await getVaultConfigs(chainConfig)
|
||||
const $vaultUtilizations = getVaultUtilizations(chainConfig, vaultConfigs)
|
||||
const $aprs = getAprs(chainConfig)
|
||||
const vaultMetaDatas = chainConfig.vaults
|
||||
const HLSAssets = assetParams.filter((asset) => asset.credit_manager.hls)
|
||||
const hlsStrategies = resolveHLSStrategies('vault', HLSAssets)
|
||||
|
||||
|
||||
@@ -2,12 +2,13 @@ import { getCreditManagerQueryClient } from 'api/cosmwasm-client'
|
||||
import { ITEM_LIMIT_PER_QUERY } from 'constants/query'
|
||||
|
||||
export default async function getAccountIds(
|
||||
chainConfig: ChainConfig,
|
||||
address?: string,
|
||||
previousResults?: AccountIdAndKind[],
|
||||
): Promise<AccountIdAndKind[]> {
|
||||
if (!address) return []
|
||||
try {
|
||||
const client = await getCreditManagerQueryClient()
|
||||
const client = await getCreditManagerQueryClient(chainConfig)
|
||||
|
||||
const lastItem = previousResults && previousResults.at(-1)
|
||||
const accounts = (
|
||||
@@ -24,7 +25,7 @@ export default async function getAccountIds(
|
||||
return accumulated.sort((a, b) => parseInt(a.id) - parseInt(b.id))
|
||||
}
|
||||
|
||||
return await getAccountIds(address, accumulated)
|
||||
return await getAccountIds(chainConfig, address, accumulated)
|
||||
} catch {
|
||||
return new Promise((_, reject) => reject('No data'))
|
||||
}
|
||||
|
||||
@@ -2,16 +2,19 @@ import getAccount from 'api/accounts/getAccount'
|
||||
import getWalletAccountIds from 'api/wallets/getAccountIds'
|
||||
import { AccountKind } from 'types/generated/mars-rover-health-computer/MarsRoverHealthComputer.types'
|
||||
|
||||
export default async function getAccounts(kind: AccountKind, address?: string): Promise<Account[]> {
|
||||
export default async function getAccounts(
|
||||
kind: AccountKind,
|
||||
chainConfig: ChainConfig,
|
||||
address?: string,
|
||||
): Promise<Account[]> {
|
||||
if (!address) return new Promise((_, reject) => reject('No address'))
|
||||
const accountIdsAndKinds = await getWalletAccountIds(address)
|
||||
const accountIdsAndKinds = await getWalletAccountIds(chainConfig, address)
|
||||
|
||||
const $accounts = accountIdsAndKinds
|
||||
.filter((a) => a.kind === kind)
|
||||
.map((account) => getAccount(account.id))
|
||||
.map((account) => getAccount(chainConfig, account.id))
|
||||
|
||||
const accounts = await Promise.all($accounts).then((accounts) => accounts)
|
||||
|
||||
if (accounts) {
|
||||
return accounts.sort((a, b) => Number(a.id) - Number(b.id))
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { getICNSQueryClient } from 'api/cosmwasm-client'
|
||||
import { ENV } from 'constants/env'
|
||||
import { ChainInfoID } from 'types/enums/wallet'
|
||||
|
||||
export default async function getICNS(address?: string): Promise<ICNSResult | undefined> {
|
||||
if (!address || ENV.CHAIN_ID !== ChainInfoID.Osmosis1) return
|
||||
export default async function getICNS(
|
||||
chainConfig: ChainConfig,
|
||||
address?: string,
|
||||
): Promise<ICNSResult | undefined> {
|
||||
if (!address || chainConfig.id !== ChainInfoID.Osmosis1) return
|
||||
try {
|
||||
const icnsQueryClient = await getICNSQueryClient()
|
||||
const icnsQueryClient = await getICNSQueryClient(chainConfig)
|
||||
return icnsQueryClient.primaryName({ address })
|
||||
} catch (ex) {
|
||||
throw ex
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { ENV } from 'constants/env'
|
||||
|
||||
export default async function getWalletBalances(address: string): Promise<Coin[]> {
|
||||
export default async function getWalletBalances(
|
||||
chainConfig: ChainConfig,
|
||||
address: string,
|
||||
): Promise<Coin[]> {
|
||||
const uri = '/cosmos/bank/v1beta1/balances/'
|
||||
|
||||
const response = await fetch(`${ENV.URL_REST}${uri}${address}`)
|
||||
const response = await fetch(`${chainConfig.endpoints.rest}${uri}${address}`)
|
||||
|
||||
if (response.ok) {
|
||||
const data = await response.json()
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
import Text from 'components/Text'
|
||||
export const ASSET_META = { accessorKey: 'symbol', header: 'Asset', id: 'symbol' }
|
||||
|
||||
interface Props {
|
||||
symbol: string
|
||||
type: 'deposits' | 'borrowing' | 'lending' | 'vault'
|
||||
}
|
||||
|
||||
export const borderColor = (type: Props['type']): string =>
|
||||
type === 'borrowing' ? 'border-loss' : 'border-profit'
|
||||
|
||||
export default function Asset(props: Props) {
|
||||
const { symbol, type } = props
|
||||
return (
|
||||
<Text size='xs'>
|
||||
{symbol}
|
||||
{type === 'borrowing' && <span className='ml-1 text-loss'>(debt)</span>}
|
||||
{type === 'lending' && <span className='ml-1 text-profit'>(lent)</span>}
|
||||
{type === 'vault' && <span className='ml-1 text-profit'>(farm)</span>}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import classNames from 'classnames'
|
||||
import { HTMLAttributes } from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { InfoCircle } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
|
||||
interface Props extends HTMLAttributes<HTMLDivElement> {
|
||||
action: 'buy' | 'deposit' | 'fund'
|
||||
coins: BNCoin[]
|
||||
showIcon?: boolean
|
||||
}
|
||||
|
||||
export default function DepositCapMessage(props: Props) {
|
||||
if (!props.coins.length) return null
|
||||
|
||||
return (
|
||||
<div className={classNames('flex items-start', props.className)}>
|
||||
{props.showIcon && <InfoCircle width={26} className='mr-5' />}
|
||||
<div className='flex flex-col gap-2'>
|
||||
<Text size='sm'>Deposit Cap Reached!</Text>
|
||||
<Text size='xs' className='text-white/40'>{`Unfortunately you're not able to ${
|
||||
props.action
|
||||
} more than the following amount${props.coins.length > 1 ? 's' : ''}:`}</Text>
|
||||
{props.coins.map((coin) => {
|
||||
const asset = getAssetByDenom(coin.denom)
|
||||
|
||||
if (!asset) return null
|
||||
|
||||
return (
|
||||
<div key={coin.denom} className='flex gap-1'>
|
||||
<Text size='xs'>Cap Left:</Text>
|
||||
<FormattedNumber
|
||||
amount={Math.max(coin.amount.toNumber(), 0)}
|
||||
options={{
|
||||
decimals: asset.decimals,
|
||||
maxDecimals: asset.decimals,
|
||||
suffix: ` ${asset.symbol}`,
|
||||
}}
|
||||
className='text-xs text-white/60'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Text from 'components/Text'
|
||||
|
||||
interface Props {
|
||||
direction: OrderDirection
|
||||
onChangeDirection: (direction: OrderDirection) => void
|
||||
asset?: Asset
|
||||
}
|
||||
|
||||
export function DirectionSelect(props: Props) {
|
||||
const hasAsset = props.asset
|
||||
const directions: OrderDirection[] = hasAsset ? ['buy', 'sell'] : ['long', 'short']
|
||||
return (
|
||||
<div className='flex rounded-sm bg-black/20'>
|
||||
<Direction
|
||||
onClick={() => props.onChangeDirection(directions[0])}
|
||||
direction={directions[0]}
|
||||
isActive={props.direction === directions[0]}
|
||||
asset={props.asset}
|
||||
/>
|
||||
<Direction
|
||||
onClick={() => props.onChangeDirection(directions[1])}
|
||||
direction={directions[1]}
|
||||
isActive={props.direction === directions[1]}
|
||||
asset={props.asset}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
interface DirectionProps {
|
||||
direction: 'long' | 'short' | 'buy' | 'sell'
|
||||
isActive: boolean
|
||||
onClick: () => void
|
||||
asset?: Asset
|
||||
}
|
||||
function Direction(props: DirectionProps) {
|
||||
const classString = props.direction === 'long' || props.direction === 'buy' ? 'success' : 'error'
|
||||
return (
|
||||
<button
|
||||
className={classNames(
|
||||
'px-4 py-3 rounded-sm flex-1',
|
||||
props.isActive && 'border bg-white/10',
|
||||
`border-${classString}`,
|
||||
)}
|
||||
onClick={props.onClick}
|
||||
>
|
||||
<Text
|
||||
className={classNames(
|
||||
'text-center first-letter:uppercase',
|
||||
props.isActive ? `text-${classString}` : 'text-white/20',
|
||||
)}
|
||||
>
|
||||
{props.asset ? `${props.direction} ${props.asset.symbol}` : props.direction}
|
||||
</Text>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
import classNames from 'classnames'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import { ORACLE_DENOM } from 'constants/oracle'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { getDisplayCurrencies } from 'utils/assets'
|
||||
import { getCoinValue } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
coin: BNCoin
|
||||
className?: string
|
||||
isApproximation?: boolean
|
||||
parentheses?: boolean
|
||||
showZero?: boolean
|
||||
options?: FormatOptions
|
||||
}
|
||||
|
||||
export default function DisplayCurrency(props: Props) {
|
||||
const displayCurrencies = getDisplayCurrencies()
|
||||
const [displayCurrency] = useLocalStorage<string>(
|
||||
LocalStorageKeys.DISPLAY_CURRENCY,
|
||||
DEFAULT_SETTINGS.displayCurrency,
|
||||
)
|
||||
const { data: prices } = usePrices()
|
||||
|
||||
const displayCurrencyAsset = useMemo(
|
||||
() =>
|
||||
displayCurrencies.find((asset) => asset.denom === displayCurrency) ?? displayCurrencies[0],
|
||||
[displayCurrency, displayCurrencies],
|
||||
)
|
||||
|
||||
const isUSD = displayCurrencyAsset.id === 'USD'
|
||||
|
||||
const amount = useMemo(() => {
|
||||
const coinValue = getCoinValue(props.coin, prices)
|
||||
|
||||
if (displayCurrency === ORACLE_DENOM) return coinValue.toNumber()
|
||||
|
||||
const displayDecimals = displayCurrencyAsset.decimals
|
||||
const displayPrice = getCoinValue(
|
||||
BNCoin.fromDenomAndBigNumber(displayCurrency, BN(1).shiftedBy(displayDecimals)),
|
||||
prices,
|
||||
)
|
||||
|
||||
return coinValue.div(displayPrice).toNumber()
|
||||
}, [displayCurrency, displayCurrencyAsset.decimals, prices, props.coin])
|
||||
|
||||
const isLessThanACent = (isUSD && amount < 0.01 && amount > 0) || (amount === 0 && props.showZero)
|
||||
const smallerThanPrefix = isLessThanACent ? '< ' : ''
|
||||
|
||||
const prefix = isUSD
|
||||
? `${props.isApproximation ? '~ ' : smallerThanPrefix}$`
|
||||
: `${props.isApproximation ? '~ ' : ''}`
|
||||
const suffix = isUSD
|
||||
? ''
|
||||
: ` ${displayCurrencyAsset.symbol ? ` ${displayCurrencyAsset.symbol}` : ''}`
|
||||
|
||||
return (
|
||||
<FormattedNumber
|
||||
className={classNames(
|
||||
props.className,
|
||||
props.parentheses && 'before:content-["("] after:content-[")"]',
|
||||
)}
|
||||
amount={isLessThanACent ? 0.01 : amount}
|
||||
options={{
|
||||
minDecimals: isUSD ? 2 : 0,
|
||||
maxDecimals: 2,
|
||||
abbreviated: true,
|
||||
prefix,
|
||||
suffix,
|
||||
...props.options,
|
||||
}}
|
||||
animate
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Suspense } from 'react'
|
||||
|
||||
import Card from 'components/Card'
|
||||
import VaultCard from 'components/Earn/Farm/VaultCard'
|
||||
import useVaults from 'hooks/useVaults'
|
||||
|
||||
function Content() {
|
||||
const { data: vaults } = useVaults()
|
||||
if (!vaults) return null
|
||||
const featuredVaults = vaults.filter((vault) => vault.isFeatured)
|
||||
|
||||
if (!featuredVaults.length) return null
|
||||
|
||||
return (
|
||||
<Card
|
||||
title='Featured vaults'
|
||||
className='w-full mb-4 h-fit bg-white/5'
|
||||
contentClassName='grid grid-cols-3'
|
||||
>
|
||||
{featuredVaults.map((vault) => (
|
||||
<VaultCard
|
||||
key={vault.address}
|
||||
vault={vault}
|
||||
title={vault.name}
|
||||
subtitle='Hot off the presses'
|
||||
provider={vault.provider}
|
||||
/>
|
||||
))}
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
export default function FeaturedVaults() {
|
||||
return (
|
||||
<Suspense fallback={null}>
|
||||
<Content />
|
||||
</Suspense>
|
||||
)
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import ActionButton from 'components/Button/ActionButton'
|
||||
import DoubleLogo from 'components/DoubleLogo'
|
||||
import Text from 'components/Text'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||
import useStore from 'store'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
import { formatPercent, formatValue } from 'utils/formatters'
|
||||
|
||||
interface Props {
|
||||
vault: Vault
|
||||
title: string
|
||||
subtitle: string
|
||||
provider?: string
|
||||
unbondingPeriod?: number
|
||||
}
|
||||
|
||||
export default function VaultCard(props: Props) {
|
||||
const currentAccount = useCurrentAccount()
|
||||
|
||||
function openVaultModal() {
|
||||
useStore.setState({
|
||||
vaultModal: {
|
||||
vault: props.vault,
|
||||
selectedBorrowDenoms: [props.vault.denoms.secondary],
|
||||
isCreate: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='border-r-[1px] border-r-white/10 p-4'>
|
||||
<div className='flex justify-between mb-8 align-center'>
|
||||
<div>
|
||||
<Text size='xs' className='mb-2 text-white/60'>
|
||||
{props.subtitle}
|
||||
</Text>
|
||||
<span className='flex'>
|
||||
<Text className='mr-2 font-bold'>{props.title}</Text>
|
||||
{props.provider && (
|
||||
<Text size='sm' className='text-white/60'>
|
||||
via {props.provider}
|
||||
</Text>
|
||||
)}
|
||||
</span>
|
||||
</div>
|
||||
<DoubleLogo
|
||||
primaryDenom={props.vault.denoms.primary}
|
||||
secondaryDenom={props.vault.denoms.secondary}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex justify-between mb-6'>
|
||||
<TitleAndSubCell
|
||||
className='text-xs'
|
||||
title={props.vault.apy ? formatPercent(props.vault.apy, 2) : '-'}
|
||||
sub={'APY'}
|
||||
/>
|
||||
<TitleAndSubCell
|
||||
className='text-xs'
|
||||
title={`${props.vault.lockup.duration} ${props.vault.lockup.timeframe}`}
|
||||
sub={'Lockup'}
|
||||
/>
|
||||
<TitleAndSubCell
|
||||
className='text-xs'
|
||||
title={formatValue(props.vault.cap.used.integerValue().toNumber() || '0', {
|
||||
abbreviated: true,
|
||||
decimals: getAssetByDenom(props.vault.cap.denom)?.decimals,
|
||||
})}
|
||||
sub={'TVL'}
|
||||
/>
|
||||
<TitleAndSubCell
|
||||
className='text-xs'
|
||||
title={formatValue(props.vault.cap.max.integerValue().toNumber() || '0', {
|
||||
abbreviated: true,
|
||||
decimals: getAssetByDenom(props.vault.cap.denom)?.decimals,
|
||||
})}
|
||||
sub={'Deposit Cap'}
|
||||
/>
|
||||
</div>
|
||||
<ActionButton onClick={openVaultModal} color='secondary' text='Deposit' className='w-full' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<svg viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M1.8335 6.00065H11.1668M11.1668 6.00065L6.50016 1.33398M11.1668 6.00065L6.50016 10.6673"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 291 B |
@@ -1,68 +0,0 @@
|
||||
// @index(['./*.svg'], f => `export { default as ${f.name} } from 'components/Icons/${f.name}.svg'`)
|
||||
export { default as Account } from 'components/Icons/Account.svg'
|
||||
export { default as AccountArrowDown } from 'components/Icons/AccountArrowDown.svg'
|
||||
export { default as ArrowChartLineUp } from 'components/Icons/ArrowChartLineUp.svg'
|
||||
export { default as ArrowCircle } from 'components/Icons/ArrowCircle.svg'
|
||||
export { default as ArrowCircledTopRight } from 'components/Icons/ArrowCircledTopRight.svg'
|
||||
export { default as ArrowDownLine } from 'components/Icons/ArrowDownLine.svg'
|
||||
export { default as ArrowRight } from 'components/Icons/ArrowRight.svg'
|
||||
export { default as ArrowUpLine } from 'components/Icons/ArrowUpLine.svg'
|
||||
export { default as Chain } from 'components/Icons/Chain.svg'
|
||||
export { default as Check } from 'components/Icons/Check.svg'
|
||||
export { default as CheckCircled } from 'components/Icons/CheckCircled.svg'
|
||||
export { default as ChevronDown } from 'components/Icons/ChevronDown.svg'
|
||||
export { default as ChevronLeft } from 'components/Icons/ChevronLeft.svg'
|
||||
export { default as ChevronRight } from 'components/Icons/ChevronRight.svg'
|
||||
export { default as ChevronUp } from 'components/Icons/ChevronUp.svg'
|
||||
export { default as Circle } from 'components/Icons/Circle.svg'
|
||||
export { default as Coins } from 'components/Icons/Coins.svg'
|
||||
export { default as CoinsSwap } from 'components/Icons/CoinsSwap.svg'
|
||||
export { default as Compass } from 'components/Icons/Compass.svg'
|
||||
export { default as Copy } from 'components/Icons/Copy.svg'
|
||||
export { default as Cross } from 'components/Icons/Cross.svg'
|
||||
export { default as CrossCircled } from 'components/Icons/CrossCircled.svg'
|
||||
export { default as Enter } from 'components/Icons/Enter.svg'
|
||||
export { default as ExclamationMarkCircled } from 'components/Icons/ExclamationMarkCircled.svg'
|
||||
export { default as ExclamationMarkTriangle } from 'components/Icons/ExclamationMarkTriangle.svg'
|
||||
export { default as ExternalLink } from 'components/Icons/ExternalLink.svg'
|
||||
export { default as Flag } from 'components/Icons/Flag.svg'
|
||||
export { default as Gear } from 'components/Icons/Gear.svg'
|
||||
export { default as GridGlobe } from 'components/Icons/GridGlobe.svg'
|
||||
export { default as GridHole } from 'components/Icons/GridHole.svg'
|
||||
export { default as GridLandscape } from 'components/Icons/GridLandscape.svg'
|
||||
export { default as GridPlanet } from 'components/Icons/GridPlanet.svg'
|
||||
export { default as GridTire } from 'components/Icons/GridTire.svg'
|
||||
export { default as GridWeb } from 'components/Icons/GridWeb.svg'
|
||||
export { default as HandCoins } from 'components/Icons/HandCoins.svg'
|
||||
export { default as Heart } from 'components/Icons/Heart.svg'
|
||||
export { default as InfoCircle } from 'components/Icons/InfoCircle.svg'
|
||||
export { default as LockLocked } from 'components/Icons/LockLocked.svg'
|
||||
export { default as LockUnlocked } from 'components/Icons/LockUnlocked.svg'
|
||||
export { default as Logo } from 'components/Icons/Logo.svg'
|
||||
export { default as Luggage } from 'components/Icons/Luggage.svg'
|
||||
export { default as MarsProtocol } from 'components/Icons/MarsProtocol.svg'
|
||||
export { default as Osmo } from 'components/Icons/Osmo.svg'
|
||||
export { default as OverlayMark } from 'components/Icons/OverlayMark.svg'
|
||||
export { default as Plus } from 'components/Icons/Plus.svg'
|
||||
export { default as PlusCircled } from 'components/Icons/PlusCircled.svg'
|
||||
export { default as PlusSquared } from 'components/Icons/PlusSquared.svg'
|
||||
export { default as PythLogoType } from 'components/Icons/PythLogoType.svg'
|
||||
export { default as Questionmark } from 'components/Icons/Questionmark.svg'
|
||||
export { default as ReceiptCheck } from 'components/Icons/ReceiptCheck.svg'
|
||||
export { default as Scale } from 'components/Icons/Scale.svg'
|
||||
export { default as Search } from 'components/Icons/Search.svg'
|
||||
export { default as Shield } from 'components/Icons/Shield.svg'
|
||||
export { default as SortAsc } from 'components/Icons/SortAsc.svg'
|
||||
export { default as SortDesc } from 'components/Icons/SortDesc.svg'
|
||||
export { default as SortNone } from 'components/Icons/SortNone.svg'
|
||||
export { default as StarFilled } from 'components/Icons/StarFilled.svg'
|
||||
export { default as StarOutlined } from 'components/Icons/StarOutlined.svg'
|
||||
export { default as Subtract } from 'components/Icons/Subtract.svg'
|
||||
export { default as SwapIcon } from 'components/Icons/SwapIcon.svg'
|
||||
export { default as ThreeDots } from 'components/Icons/ThreeDots.svg'
|
||||
export { default as TooltipArrow } from 'components/Icons/TooltipArrow.svg'
|
||||
export { default as TrashBin } from 'components/Icons/TrashBin.svg'
|
||||
export { default as Twitter } from 'components/Icons/Twitter.svg'
|
||||
export { default as VerticalThreeLine } from 'components/Icons/VerticalThreeLine.svg'
|
||||
export { default as Wallet } from 'components/Icons/Wallet.svg'
|
||||
// @endindex
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { Enter, InfoCircle } from 'components/Icons'
|
||||
import { Enter, InfoCircle } from 'components/common/Icons'
|
||||
import useAlertDialog from 'hooks/useAlertDialog'
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import { useCallback, useMemo } from 'react'
|
||||
import { useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom'
|
||||
|
||||
import AssetBalanceRow from 'components/Asset/AssetBalanceRow'
|
||||
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
|
||||
import AssetBalanceRow from 'components/common/assets/AssetBalanceRow'
|
||||
import { ArrowRight, ExclamationMarkCircled } from 'components/common/Icons'
|
||||
import AccountDeleteAlertDialog from 'components/Modals/Account/AccountDeleteAlertDialog'
|
||||
import Text from 'components/Text'
|
||||
import Text from 'components/common/Text'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { combineBNCoins } from 'utils/parsers'
|
||||
import { getPage, getRoute } from 'utils/route'
|
||||
|
||||
@@ -31,7 +32,7 @@ function AccountDeleteModal(props: Props) {
|
||||
const { address } = useParams()
|
||||
const { debts, vaults, id: accountId } = modal || {}
|
||||
const [searchParams] = useSearchParams()
|
||||
|
||||
const assets = useAllAssets()
|
||||
const closeDeleteAccountModal = useCallback(() => {
|
||||
useStore.setState({ accountDeleteModal: null })
|
||||
}, [])
|
||||
@@ -117,7 +118,7 @@ function AccountDeleteModal(props: Props) {
|
||||
<div className='flex flex-col w-full gap-4 py-4 overflow-y-scroll max-h-100 scrollbar-hide'>
|
||||
{depositsAndLends.map((position, index) => {
|
||||
const coin = BNCoin.fromDenomAndBigNumber(position.denom, position.amount)
|
||||
const asset = getAssetByDenom(position.denom)
|
||||
const asset = assets.find(byDenom(position.denom))
|
||||
if (!asset) return null
|
||||
return <AssetBalanceRow key={index} asset={asset} coin={coin} />
|
||||
})}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
|
||||
import AssetSelectTable from 'components/Modals/AssetsSelect/AssetSelectTable'
|
||||
import SearchBar from 'components/SearchBar'
|
||||
import Text from 'components/Text'
|
||||
import useMarketBorrowings from 'hooks/useMarketBorrowings'
|
||||
import AssetsSelect from 'components/Modals/AssetsSelect'
|
||||
import SearchBar from 'components/common/SearchBar'
|
||||
import Text from 'components/common/Text'
|
||||
import useMarketBorrowings from 'hooks/markets/useMarketBorrowings'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
@@ -89,11 +89,11 @@ export default function AddVaultAssetsModalContent(props: Props) {
|
||||
Leverage will be set at 50% for both assets by default
|
||||
</Text>
|
||||
</div>
|
||||
<AssetSelectTable
|
||||
isBorrow={true}
|
||||
<AssetsSelect
|
||||
assets={poolAssets}
|
||||
onChangeSelected={onChangePoolDenoms}
|
||||
selectedDenoms={selectedPoolDenoms}
|
||||
isBorrow
|
||||
/>
|
||||
<div className='p-4'>
|
||||
<Text>Assets not in the liquidity pool</Text>
|
||||
@@ -102,11 +102,11 @@ export default function AddVaultAssetsModalContent(props: Props) {
|
||||
these assets below.
|
||||
</Text>
|
||||
</div>
|
||||
<AssetSelectTable
|
||||
isBorrow={true}
|
||||
<AssetsSelect
|
||||
assets={stableAssets}
|
||||
onChangeSelected={onChangeOtherDenoms}
|
||||
selectedDenoms={selectedOtherDenoms}
|
||||
isBorrow
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import Modal from 'components/Modal'
|
||||
import Button from 'components/common/Button'
|
||||
import { CircularProgress } from 'components/common/CircularProgress'
|
||||
import Modal from 'components/Modals/Modal'
|
||||
import AddVaultAssetsModalContent from 'components/Modals/AddVaultAssets/AddVaultBorrowAssetsModalContent'
|
||||
import Text from 'components/Text'
|
||||
import Text from 'components/common/Text'
|
||||
import useStore from 'store'
|
||||
|
||||
export default function AddVaultBorrowAssetsModal() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Enter } from 'components/Icons'
|
||||
import { Enter } from 'components/common/Icons'
|
||||
|
||||
export function NoIcon() {
|
||||
return (
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import Checkbox from 'components/Checkbox'
|
||||
import Modal from 'components/Modal'
|
||||
import Button from 'components/common/Button'
|
||||
import Checkbox from 'components/common/Checkbox'
|
||||
import Modal from 'components/Modals/Modal'
|
||||
import { NoIcon, YesIcon } from 'components/Modals/AlertDialog/ButtonIcons'
|
||||
import Text from 'components/Text'
|
||||
import Text from 'components/common/Text'
|
||||
import useAlertDialog from 'hooks/useAlertDialog'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
|
||||
import CurrentAccountSummary from 'components/Account/CurrentAccountSummary'
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
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/TokenInput/TokenInputWithSlider'
|
||||
import Modal from 'components/Modals/Modal'
|
||||
import CurrentAccountSummary from 'components/account/CurrentAccountSummary'
|
||||
import Button from 'components/common/Button'
|
||||
import Card from 'components/common/Card'
|
||||
import Divider from 'components/common/Divider'
|
||||
import { ArrowRight } from 'components/common/Icons'
|
||||
import Text from 'components/common/Text'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
import AssetImage from 'components/common/assets/AssetImage'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
asset: Asset
|
||||
title: string
|
||||
coinBalances: Coin[]
|
||||
coinBalances: BNCoin[]
|
||||
actionButtonText: string
|
||||
contentHeader?: JSX.Element
|
||||
onClose: () => void
|
||||
|
||||
@@ -1,154 +0,0 @@
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getSortedRowModel,
|
||||
RowSelectionState,
|
||||
SortingState,
|
||||
useReactTable,
|
||||
} from '@tanstack/react-table'
|
||||
import classNames from 'classnames'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import { SortAsc, SortDesc, SortNone } from 'components/Icons'
|
||||
import useAssetTableColumns from 'components/Modals/AssetsSelect/useAssetTableColumns'
|
||||
import Text from 'components/Text'
|
||||
import useMarketAssets from 'hooks/useMarketAssets'
|
||||
import useStore from 'store'
|
||||
import { byDenom } from 'utils/array'
|
||||
|
||||
interface Props {
|
||||
assets: Asset[] | BorrowAsset[]
|
||||
selectedDenoms: string[]
|
||||
onChangeSelected: (denoms: string[]) => void
|
||||
isBorrow: boolean
|
||||
}
|
||||
|
||||
export default function AssetSelectTable(props: Props) {
|
||||
const { data: markets } = useMarketAssets()
|
||||
const defaultSelected = useMemo(() => {
|
||||
const assets = props.assets as BorrowAsset[]
|
||||
return 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)
|
||||
const balances = useStore((s) => s.balances)
|
||||
const columns = useAssetTableColumns(props.isBorrow)
|
||||
const tableData: AssetTableRow[] = useMemo(() => {
|
||||
return props.assets.map((asset) => {
|
||||
const balancesForAsset = balances.find(byDenom(asset.denom))
|
||||
return {
|
||||
asset,
|
||||
balance: balancesForAsset?.amount ?? '0',
|
||||
market: markets.find((market) => market.denom === asset.denom),
|
||||
}
|
||||
})
|
||||
}, [balances, props.assets, markets])
|
||||
|
||||
const table = useReactTable({
|
||||
data: tableData,
|
||||
columns,
|
||||
state: {
|
||||
sorting,
|
||||
rowSelection: selected,
|
||||
},
|
||||
onRowSelectionChange: setSelected,
|
||||
onSortingChange: setSorting,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const newSelectedDenoms = props.assets
|
||||
.filter((_, index) => selected[index])
|
||||
.map((asset) => asset.denom)
|
||||
|
||||
if (
|
||||
props.selectedDenoms.length === newSelectedDenoms.length &&
|
||||
newSelectedDenoms.every((denom) => props.selectedDenoms.includes(denom))
|
||||
)
|
||||
return
|
||||
props.onChangeSelected(newSelectedDenoms)
|
||||
}, [selected, props])
|
||||
|
||||
return (
|
||||
<table className='w-full'>
|
||||
<thead className='border-b border-white/10'>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
className={classNames(
|
||||
'p-2',
|
||||
header.column.getCanSort() && 'hover:cursor-pointer',
|
||||
header.id === 'symbol' ? 'text-left' : 'text-right',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={classNames(
|
||||
'flex',
|
||||
header.id === 'symbol' ? 'justify-start' : 'justify-end',
|
||||
'align-center',
|
||||
)}
|
||||
>
|
||||
<span className='w-6 h-6 text-white'>
|
||||
{header.column.getCanSort()
|
||||
? {
|
||||
asc: <SortAsc />,
|
||||
desc: <SortDesc />,
|
||||
false: <SortNone />,
|
||||
}[header.column.getIsSorted() as string] ?? null
|
||||
: null}
|
||||
</span>
|
||||
<Text
|
||||
tag='span'
|
||||
size='sm'
|
||||
className='flex items-center font-normal text-white/40'
|
||||
>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</Text>
|
||||
</div>
|
||||
</th>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map((row) => {
|
||||
return (
|
||||
<tr
|
||||
key={row.id}
|
||||
className='hover:cursor-pointer text-white/60'
|
||||
onClick={() => row.toggleSelected()}
|
||||
>
|
||||
{row.getVisibleCells().map((cell) => {
|
||||
return (
|
||||
<td
|
||||
key={cell.id}
|
||||
className={classNames(
|
||||
cell.column.id === 'select' ? `` : 'text-right',
|
||||
'px-4 py-3',
|
||||
)}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</td>
|
||||
)
|
||||
})}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
|
||||
import Checkbox from 'components/common/Checkbox'
|
||||
import Text from 'components/common/Text'
|
||||
import AssetImage from 'components/common/assets/AssetImage'
|
||||
import AssetRate from 'components/common/assets/AssetRate'
|
||||
|
||||
export const ASSET_META = { id: 'name', header: 'Asset', accessorKey: 'asset.symbol' }
|
||||
|
||||
interface Props {
|
||||
row: Row<AssetTableRow>
|
||||
}
|
||||
|
||||
function isBorrowAsset(object?: any): object is BorrowAsset {
|
||||
if (!object) return false
|
||||
return 'borrowRate' in object
|
||||
}
|
||||
|
||||
export default function Asset(props: Props) {
|
||||
const { row } = props
|
||||
const asset = row.original.asset
|
||||
const market = row.original.market
|
||||
const isBorrow = isBorrowAsset(asset)
|
||||
const showRate = !isBorrow && market?.borrowEnabled
|
||||
const apy = isBorrow ? market?.apy.borrow : market?.apy.deposit
|
||||
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
<Checkbox
|
||||
name={`asset-${asset.id.toLowerCase()}`}
|
||||
checked={row.getIsSelected()}
|
||||
onChange={row.getToggleSelectedHandler()}
|
||||
noMouseEvents
|
||||
/>
|
||||
<AssetImage asset={asset} size={24} className='ml-4' />
|
||||
<div className='ml-2 text-left'>
|
||||
<Text size='sm' className='mb-0.5 text-white'>
|
||||
{asset.symbol}
|
||||
</Text>
|
||||
{showRate && market ? (
|
||||
<AssetRate
|
||||
rate={apy ?? 0}
|
||||
isEnabled={market.borrowEnabled}
|
||||
className='text-xs'
|
||||
type='apy'
|
||||
orientation='rtl'
|
||||
suffix
|
||||
/>
|
||||
) : (
|
||||
<Text size='xs'>{asset.name}</Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import { FormattedNumber } from 'components/common/FormattedNumber'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { demagnify } from 'utils/formatters'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
export const BALANCE_META = { id: 'value', header: 'Balance', accessorKey: 'value' }
|
||||
|
||||
interface Props {
|
||||
row: Row<AssetTableRow>
|
||||
}
|
||||
|
||||
export const valueSortingFn = (a: Row<AssetTableRow>, b: Row<AssetTableRow>): number => {
|
||||
const valueA = a.original.value ?? BN_ZERO
|
||||
const valueB = b.original.value ?? BN_ZERO
|
||||
return valueA.minus(valueB).toNumber()
|
||||
}
|
||||
|
||||
export default function Balance(props: Props) {
|
||||
const { row } = props
|
||||
const asset = row.original.asset
|
||||
const balance = BN(row.original.balance ?? '0')
|
||||
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, balance)
|
||||
|
||||
return (
|
||||
<div className='flex flex-wrap items-center'>
|
||||
<DisplayCurrency coin={coin} className='mb-0.5 w-full text-white' />
|
||||
<FormattedNumber
|
||||
className='w-full text-xs'
|
||||
options={{ minDecimals: 2, maxDecimals: asset.decimals }}
|
||||
amount={demagnify(balance, asset)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { Row } from '@tanstack/react-table'
|
||||
|
||||
import Text from 'components/common/Text'
|
||||
import { formatPercent } from 'utils/formatters'
|
||||
|
||||
export const BORROW_RATE_META = {
|
||||
id: 'asset.borrowRate',
|
||||
header: 'BorrowRate',
|
||||
accessorKey: 'asset.borrowRate',
|
||||
}
|
||||
|
||||
interface Props {
|
||||
row: Row<AssetTableRow>
|
||||
}
|
||||
|
||||
export default function BorrowRate(props: Props) {
|
||||
const { row } = props
|
||||
const asset = row.original.asset as BorrowAsset
|
||||
|
||||
return (
|
||||
<Text size='sm' className='mb-0.5 text-white'>
|
||||
{formatPercent(asset.borrowRate ?? 0)}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import Asset, { ASSET_META } from 'components/Modals/AssetsSelect/Columns/Asset'
|
||||
import Balance, {
|
||||
BALANCE_META,
|
||||
valueSortingFn,
|
||||
} from 'components/Modals/AssetsSelect/Columns/Balance'
|
||||
import BorrowRate, { BORROW_RATE_META } from 'components/Modals/AssetsSelect/Columns/BorrowRate'
|
||||
|
||||
export default function useAssetSelectColumns(isBorrow?: boolean) {
|
||||
return useMemo<ColumnDef<AssetTableRow>[]>(() => {
|
||||
return [
|
||||
{
|
||||
...ASSET_META,
|
||||
cell: ({ row }) => <Asset row={row} />,
|
||||
},
|
||||
isBorrow
|
||||
? {
|
||||
...BORROW_RATE_META,
|
||||
cell: ({ row }) => <BorrowRate row={row} />,
|
||||
}
|
||||
: {
|
||||
...BALANCE_META,
|
||||
cell: ({ row }) => <Balance row={row} />,
|
||||
sortingFn: valueSortingFn,
|
||||
},
|
||||
]
|
||||
}, [isBorrow])
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import { RowSelectionState } from '@tanstack/react-table'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import useAssetSelectColumns from 'components/Modals/AssetsSelect/Columns/useAssetSelectColumns'
|
||||
import Table from 'components/common/Table'
|
||||
import useGetCoinValue from 'hooks/assets/useGetCoinValue'
|
||||
import useMarketAssets from 'hooks/markets/useMarketAssets'
|
||||
import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
interface Props {
|
||||
assets: Asset[]
|
||||
onChangeSelected: (selected: string[]) => void
|
||||
selectedDenoms: string[]
|
||||
isBorrow?: boolean
|
||||
}
|
||||
|
||||
export default function AssetsSelect(props: Props) {
|
||||
const { assets, onChangeSelected, selectedDenoms, isBorrow } = props
|
||||
const columns = useAssetSelectColumns(isBorrow)
|
||||
const { data: markets } = useMarketAssets()
|
||||
const getCoinValue = useGetCoinValue()
|
||||
|
||||
const defaultSelected = useMemo(() => {
|
||||
const selectableAssets = assets
|
||||
return selectableAssets.reduce(
|
||||
(acc, asset, index) => {
|
||||
if (selectedDenoms?.includes(asset.denom)) {
|
||||
acc[index] = true
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{} as { [key: number]: boolean },
|
||||
)
|
||||
}, [selectedDenoms, assets])
|
||||
|
||||
const [selected, setSelected] = useState<RowSelectionState>(defaultSelected)
|
||||
|
||||
const balances = useStore((s) => s.balances)
|
||||
const tableData: AssetTableRow[] = useMemo(() => {
|
||||
return assets.map((asset) => {
|
||||
const balancesForAsset = balances.find(byDenom(asset.denom))
|
||||
const coin = BNCoin.fromDenomAndBigNumber(asset.denom, BN(balancesForAsset?.amount ?? '0'))
|
||||
const value = getCoinValue(coin)
|
||||
return {
|
||||
asset,
|
||||
balance: balancesForAsset?.amount ?? '0',
|
||||
value,
|
||||
market: markets.find((market) => market.denom === asset.denom),
|
||||
}
|
||||
})
|
||||
}, [balances, assets, markets, getCoinValue])
|
||||
|
||||
useEffect(() => {
|
||||
const selectedAssets = assets.filter((_, index) => selected[index])
|
||||
|
||||
const newSelectedDenoms = selectedAssets
|
||||
.sort((a, b) => a.symbol.localeCompare(b.symbol))
|
||||
.map((asset) => asset.denom)
|
||||
if (
|
||||
selectedDenoms.length === newSelectedDenoms.length &&
|
||||
newSelectedDenoms.every((denom) => selectedDenoms.includes(denom))
|
||||
)
|
||||
return
|
||||
onChangeSelected(newSelectedDenoms)
|
||||
}, [selected, props, assets, selectedDenoms, onChangeSelected])
|
||||
|
||||
return (
|
||||
<Table
|
||||
title='Assets'
|
||||
hideCard={true}
|
||||
columns={columns}
|
||||
data={tableData}
|
||||
initialSorting={[{ id: isBorrow ? 'asset.borrowRate' : 'value', desc: !isBorrow }]}
|
||||
setRowSelection={setSelected}
|
||||
selectedRows={selected}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
import { ColumnDef } from '@tanstack/react-table'
|
||||
import React from 'react'
|
||||
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import AssetRate from 'components/Asset/AssetRate'
|
||||
import Checkbox from 'components/Checkbox'
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Text from 'components/Text'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
import { demagnify, formatPercent } from 'utils/formatters'
|
||||
|
||||
function showBorrowRate(data: AssetTableRow[]) {
|
||||
const assetData = data.length && (data[0].asset as BorrowAsset)
|
||||
return !!(assetData && assetData?.borrowRate)
|
||||
}
|
||||
|
||||
export default function useAssetTableColumns(isBorrow: boolean) {
|
||||
return React.useMemo<ColumnDef<AssetTableRow>[]>(
|
||||
() => [
|
||||
{
|
||||
header: 'Asset',
|
||||
accessorKey: 'symbol',
|
||||
id: 'symbol',
|
||||
cell: ({ row }) => {
|
||||
const asset = getAssetByDenom(row.original.asset.denom) as Asset
|
||||
const market = row.original.market
|
||||
const borrowAsset = row.original.asset as BorrowAsset
|
||||
const showRate = !borrowAsset?.borrowRate
|
||||
const apy = isBorrow ? market?.apy.borrow : market?.apy.deposit
|
||||
|
||||
return (
|
||||
<div className='flex items-center'>
|
||||
<Checkbox
|
||||
name={`asset-${asset.id.toLowerCase()}`}
|
||||
checked={row.getIsSelected()}
|
||||
onChange={row.getToggleSelectedHandler()}
|
||||
noMouseEvents
|
||||
/>
|
||||
<AssetImage asset={asset} size={24} className='ml-4' />
|
||||
<div className='ml-2 text-left'>
|
||||
<Text size='sm' className='mb-0.5 text-white'>
|
||||
{asset.symbol}
|
||||
</Text>
|
||||
{showRate && market ? (
|
||||
<AssetRate
|
||||
rate={apy ?? 0}
|
||||
isEnabled={market.borrowEnabled}
|
||||
className='text-xs'
|
||||
type='apy'
|
||||
orientation='rtl'
|
||||
suffix
|
||||
/>
|
||||
) : (
|
||||
<Text size='xs'>{asset.name}</Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'details',
|
||||
header: (data) => {
|
||||
const tableData = data.table.options.data as AssetTableRow[]
|
||||
if (showBorrowRate(tableData)) return 'Borrow Rate'
|
||||
return 'Balance'
|
||||
},
|
||||
cell: ({ row }) => {
|
||||
const asset = row.original.asset as BorrowAsset
|
||||
const balance = row.original.balance
|
||||
if (asset?.borrowRate)
|
||||
return (
|
||||
<Text size='sm' className='mb-0.5 text-white'>
|
||||
{formatPercent(asset.borrowRate ?? 0)}
|
||||
</Text>
|
||||
)
|
||||
if (!balance) return null
|
||||
const coin = new BNCoin({ denom: row.original.asset.denom, amount: balance })
|
||||
return (
|
||||
<div className='flex flex-wrap items-center'>
|
||||
<DisplayCurrency coin={coin} className='mb-0.5 w-full text-white' />
|
||||
<FormattedNumber
|
||||
className='w-full text-xs'
|
||||
options={{ minDecimals: 2, maxDecimals: asset.decimals }}
|
||||
amount={demagnify(balance, asset)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
[isBorrow],
|
||||
)
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import AccountSummary from 'components/Account/AccountSummary'
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import Button from 'components/Button'
|
||||
import Card from 'components/Card'
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Divider from 'components/Divider'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { ArrowRight, InfoCircle } from 'components/Icons'
|
||||
import Modal from 'components/Modal'
|
||||
import Switch from 'components/Switch'
|
||||
import Text from 'components/Text'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import AccountSummary from 'components/account/AccountSummary'
|
||||
import AssetImage from 'components/common/assets/AssetImage'
|
||||
import Button from 'components/common/Button'
|
||||
import Card from 'components/common/Card'
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import Divider from 'components/common/Divider'
|
||||
import { FormattedNumber } from 'components/common/FormattedNumber'
|
||||
import { ArrowRight, InfoCircle } from 'components/common/Icons'
|
||||
import Switch from 'components/common/Switch'
|
||||
import Text from 'components/common/Text'
|
||||
import TitleAndSubCell from 'components/common/TitleAndSubCell'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
import Modal from 'components/Modals/Modal'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
|
||||
import useMarketAssets from 'hooks/markets/useMarketAssets'
|
||||
import useAutoLend from 'hooks/useAutoLend'
|
||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import useMarketAssets from 'hooks/useMarketAssets'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import { getDepositAndLendCoinsToSpend } from 'hooks/useUpdatedAccount/functions'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import AccountFundContent from 'components/Account/AccountFund/AccountFundContent'
|
||||
import AccountFundContent from 'components/account/AccountFund/AccountFundContent'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import Divider from 'components/Divider'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import Switch from 'components/Switch'
|
||||
import Text from 'components/Text'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import { ASSETS } from 'constants/assets'
|
||||
import Button from 'components/common/Button'
|
||||
import Divider from 'components/common/Divider'
|
||||
import { ArrowRight } from 'components/common/Icons'
|
||||
import Switch from 'components/common/Switch'
|
||||
import Text from 'components/common/Text'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import useMarketEnabledAssets from 'hooks/assets/useMarketEnabledAssets'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import useToggle from 'hooks/useToggle'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
@@ -16,7 +17,6 @@ import useStore from 'store'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { cloneAccount, getMergedBalancesForAsset, removeDepositsAndLends } from 'utils/accounts'
|
||||
import { byDenom } from 'utils/array'
|
||||
import { getEnabledMarketAssets } from 'utils/assets'
|
||||
|
||||
interface Props {
|
||||
account: Account
|
||||
@@ -24,8 +24,9 @@ interface Props {
|
||||
|
||||
export default function WithdrawFromAccount(props: Props) {
|
||||
const { account } = props
|
||||
const assets = useAllAssets()
|
||||
const defaultAsset =
|
||||
ASSETS.find(byDenom(account.deposits[0]?.denom || account.lends[0]?.denom)) ?? ASSETS[0]
|
||||
assets.find(byDenom(account.deposits[0]?.denom || account.lends[0]?.denom)) ?? assets[0]
|
||||
const withdraw = useStore((s) => s.withdraw)
|
||||
const [withdrawWithBorrowing, setWithdrawWithBorrowing] = useToggle()
|
||||
const [currentAsset, setCurrentAsset] = useState(defaultAsset)
|
||||
@@ -35,7 +36,8 @@ export default function WithdrawFromAccount(props: Props) {
|
||||
const accountClone = cloneAccount(account)
|
||||
const borrowAccount = removeDepositsAndLends(accountClone, currentAsset.denom)
|
||||
const { computeMaxBorrowAmount } = useHealthComputer(borrowAccount)
|
||||
const balances = getMergedBalancesForAsset(account, getEnabledMarketAssets())
|
||||
const marketEnabledAssets = useMarketEnabledAssets()
|
||||
const balances = getMergedBalancesForAsset(account, marketEnabledAssets)
|
||||
const maxWithdrawAmount = computeMaxWithdrawAmount(currentAsset.denom)
|
||||
const maxWithdrawWithBorrowAmount = computeMaxBorrowAmount(currentAsset.denom, 'wallet').plus(
|
||||
maxWithdrawAmount,
|
||||
@@ -46,10 +48,12 @@ export default function WithdrawFromAccount(props: Props) {
|
||||
const max = withdrawWithBorrowing ? maxWithdrawWithBorrowAmount : maxWithdrawAmount
|
||||
|
||||
const accountDeposit = account.deposits.find(byDenom(currentAsset.denom))?.amount ?? BN_ZERO
|
||||
const shouldReclaim =
|
||||
amount.isGreaterThan(accountDeposit) && !withdrawWithBorrowing && currentAsset.isAutoLendEnabled
|
||||
const reclaimAmount = shouldReclaim ? amount.minus(accountDeposit) : BN_ZERO
|
||||
const isReclaimingMaxAmount = maxWithdrawAmount.isEqualTo(amount)
|
||||
const accountLent = account.lends.find(byDenom(currentAsset.denom))?.amount ?? BN_ZERO
|
||||
const shouldReclaim = amount.isGreaterThan(accountDeposit) && !accountLent.isZero()
|
||||
const isReclaimingMaxAmount = accountLent.isLessThanOrEqualTo(amount.minus(accountDeposit))
|
||||
const reclaimAmount = isReclaimingMaxAmount
|
||||
? amount
|
||||
: accountLent.minus(amount).minus(accountDeposit)
|
||||
|
||||
function onChangeAmount(val: BigNumber) {
|
||||
setAmount(val)
|
||||
@@ -65,13 +69,14 @@ export default function WithdrawFromAccount(props: Props) {
|
||||
const borrow = !debtAmount.isZero()
|
||||
? [BNCoin.fromDenomAndBigNumber(currentAsset.denom, debtAmount)]
|
||||
: []
|
||||
const reclaims = !reclaimAmount.isZero()
|
||||
? [
|
||||
BNCoin.fromDenomAndBigNumber(currentAsset.denom, reclaimAmount).toActionCoin(
|
||||
isReclaimingMaxAmount,
|
||||
),
|
||||
]
|
||||
: []
|
||||
const reclaims =
|
||||
shouldReclaim && !reclaimAmount.isZero()
|
||||
? [
|
||||
BNCoin.fromDenomAndBigNumber(currentAsset.denom, reclaimAmount).toActionCoin(
|
||||
isReclaimingMaxAmount,
|
||||
),
|
||||
]
|
||||
: []
|
||||
|
||||
withdraw({
|
||||
accountId: account.id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import FundWithdrawModalContent from 'components/Modals/FundWithdraw/FundAndWithdrawModalContent'
|
||||
import ModalContentWithSummary from 'components/Modals/ModalContentWithSummary'
|
||||
import Text from 'components/Text'
|
||||
import useAccount from 'hooks/useAccount'
|
||||
import Text from 'components/common/Text'
|
||||
import useAccount from 'hooks/accounts/useAccount'
|
||||
import useAccountId from 'hooks/useAccountId'
|
||||
import useStore from 'store'
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useCallback } from 'react'
|
||||
|
||||
import { ChevronRight, Compass, HandCoins, Luggage } from 'components/Icons'
|
||||
import Modal from 'components/Modal'
|
||||
import Text from 'components/Text'
|
||||
import { ChevronRight, Compass, HandCoins, Luggage } from 'components/common/Icons'
|
||||
import Modal from 'components/Modals/Modal'
|
||||
import Text from 'components/common/Text'
|
||||
import useStore from 'store'
|
||||
import { DocURL } from 'types/enums/docURL'
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useState } from 'react'
|
||||
import { mutate } from 'swr'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import DocsLink from 'components/DocsLink'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import Button from 'components/common/Button'
|
||||
import DocsLink from 'components/common/DocsLink'
|
||||
import { ArrowRight } from 'components/common/Icons'
|
||||
import Text from 'components/common/Text'
|
||||
import useStore from 'store'
|
||||
|
||||
export default function CreateAccount() {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import Button from 'components/common/Button'
|
||||
import { ArrowRight } from 'components/common/Icons'
|
||||
import LeverageSummary from 'components/Modals/HLS/Deposit/LeverageSummary'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
import { getLeveragedApy } from 'utils/math'
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import SummaryItems from 'components/SummaryItems'
|
||||
import SummaryItems from 'components/common/SummaryItems'
|
||||
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import Button from 'components/common/Button'
|
||||
import { ArrowRight } from 'components/common/Icons'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
|
||||
interface Props {
|
||||
amount: BigNumber
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import Radio from 'components/Radio'
|
||||
import Button from 'components/common/Button'
|
||||
import { ArrowRight } from 'components/common/Icons'
|
||||
import Radio from 'components/common/Radio'
|
||||
|
||||
interface Props {
|
||||
hlsAccounts: Account[]
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import { ExclamationMarkTriangle } from 'components/Icons'
|
||||
import Text from 'components/Text'
|
||||
import WarningMessages from 'components/WarningMessages'
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import { ExclamationMarkTriangle } from 'components/common/Icons'
|
||||
import Text from 'components/common/Text'
|
||||
import WarningMessages from 'components/common/WarningMessages'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { formatAmountWithSymbol, formatLeverage } from 'utils/formatters'
|
||||
|
||||
@@ -33,6 +34,7 @@ interface CollateralSubTitleProps {
|
||||
}
|
||||
|
||||
export function CollateralSubTitle(props: CollateralSubTitleProps) {
|
||||
const assets = useAllAssets()
|
||||
if (props.isOpen) return null
|
||||
|
||||
if (!props.isOpen && props.amount.isZero()) {
|
||||
@@ -50,10 +52,13 @@ export function CollateralSubTitle(props: CollateralSubTitleProps) {
|
||||
<div className='flex items-center gap-1'>
|
||||
<WarningMessages messages={props.warningMessages} />
|
||||
<SubTitle
|
||||
text={formatAmountWithSymbol({
|
||||
denom: props.denom,
|
||||
amount: props.amount.toString(),
|
||||
})}
|
||||
text={formatAmountWithSymbol(
|
||||
{
|
||||
denom: props.denom,
|
||||
amount: props.amount.toString(),
|
||||
},
|
||||
assets,
|
||||
)}
|
||||
color={props.warningMessages.length > 0 ? 'text-warning' : ''}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import Text from 'components/Text'
|
||||
import { FormattedNumber } from 'components/common/FormattedNumber'
|
||||
import Text from 'components/common/Text'
|
||||
|
||||
interface Props {
|
||||
items: { title: string; amount: number }[]
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React from 'react'
|
||||
|
||||
import AmountAndValue from 'components/AmountAndValue'
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import AmountAndValue from 'components/common/AmountAndValue'
|
||||
import AssetImage from 'components/common/assets/AssetImage'
|
||||
import { FormattedNumber } from 'components/common/FormattedNumber'
|
||||
import Container from 'components/Modals/HLS/Deposit/Summary/Container'
|
||||
import Text from 'components/Text'
|
||||
import Text from 'components/common/Text'
|
||||
|
||||
interface Props {
|
||||
amount: BigNumber
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react'
|
||||
|
||||
import Text from 'components/Text'
|
||||
import Text from 'components/common/Text'
|
||||
|
||||
interface Props {
|
||||
children: React.ReactNode
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React, { useMemo } from 'react'
|
||||
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import { InfoCircle } from 'components/Icons'
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import { FormattedNumber } from 'components/common/FormattedNumber'
|
||||
import { InfoCircle } from 'components/common/Icons'
|
||||
import AprBreakdown from 'components/Modals/HLS/Deposit/Summary/ApyBreakdown'
|
||||
import Container from 'components/Modals/HLS/Deposit/Summary/Container'
|
||||
import Text from 'components/Text'
|
||||
import { Tooltip } from 'components/Tooltip'
|
||||
import Text from 'components/common/Text'
|
||||
import { Tooltip } from 'components/common/Tooltip'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
|
||||
interface Props {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import { ArrowRight } from 'components/Icons'
|
||||
import Button from 'components/common/Button'
|
||||
import { ArrowRight } from 'components/common/Icons'
|
||||
import AssetSummary from 'components/Modals/HLS/Deposit/Summary/AssetSummary'
|
||||
import YourPosition from 'components/Modals/HLS/Deposit/Summary/YourPosition'
|
||||
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import React, { useMemo, useState } from 'react'
|
||||
|
||||
import Accordion from 'components/Accordion'
|
||||
import Accordion from 'components/common/Accordion'
|
||||
import useStakingController from 'components/Modals/HLS/Deposit//useStakingController'
|
||||
import useVaultController from 'components/Modals/HLS/Deposit//useVaultController'
|
||||
import useAccordionItems from 'components/Modals/HLS/Deposit/useAccordionItems'
|
||||
import { EMPTY_ACCOUNT_HLS } from 'constants/accounts'
|
||||
import useAccounts from 'hooks/useAccounts'
|
||||
import useAccounts from 'hooks/accounts/useAccounts'
|
||||
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
||||
import useIsOpenArray from 'hooks/useIsOpenArray'
|
||||
import useVault from 'hooks/useVault'
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from 'components/Modals/HLS/Deposit/SubTitles'
|
||||
import Summary from 'components/Modals/HLS/Deposit/Summary'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { getCoinAmount, getCoinValue } from 'utils/formatters'
|
||||
@@ -47,6 +48,7 @@ interface Props {
|
||||
|
||||
export default function useAccordionItems(props: Props) {
|
||||
const { data: prices } = usePrices()
|
||||
const assets = useAllAssets()
|
||||
|
||||
const depositCapLeft = useMemo(() => {
|
||||
if (!props.strategy) return BN_ZERO
|
||||
@@ -62,9 +64,10 @@ export default function useAccordionItems(props: Props) {
|
||||
const value = getCoinValue(
|
||||
BNCoin.fromDenomAndBigNumber(props.borrowAsset.denom, props.borrowAmount),
|
||||
prices,
|
||||
assets,
|
||||
)
|
||||
return getCoinAmount(props.collateralAsset.denom, value, prices)
|
||||
}, [prices, props.borrowAmount, props.borrowAsset.denom, props.collateralAsset])
|
||||
return getCoinAmount(props.collateralAsset.denom, value, prices, assets)
|
||||
}, [assets, prices, props.borrowAmount, props.borrowAsset.denom, props.collateralAsset.denom])
|
||||
|
||||
const collateralWarningMessages = useMemo(() => {
|
||||
const messages: string[] = []
|
||||
@@ -73,11 +76,14 @@ export default function useAccordionItems(props: Props) {
|
||||
}
|
||||
|
||||
if (props.depositAmount.isGreaterThan(depositCapLeft)) {
|
||||
messages.push(getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'deposit'))
|
||||
messages.push(
|
||||
getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'deposit', assets),
|
||||
)
|
||||
}
|
||||
|
||||
return messages
|
||||
}, [
|
||||
assets,
|
||||
depositCapLeft,
|
||||
props.collateralAsset.denom,
|
||||
props.collateralAsset.symbol,
|
||||
@@ -90,21 +96,22 @@ export default function useAccordionItems(props: Props) {
|
||||
|
||||
if (props.borrowAmount.isGreaterThan(props.maxBorrowAmount)) {
|
||||
messages.push(
|
||||
getHealthFactorMessage(props.borrowAsset.denom, props.maxBorrowAmount, 'borrow'),
|
||||
getHealthFactorMessage(props.borrowAsset.denom, props.maxBorrowAmount, 'borrow', assets),
|
||||
)
|
||||
}
|
||||
|
||||
if (props.borrowAmount.isGreaterThan(borrowLiquidity)) {
|
||||
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
|
||||
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity, assets))
|
||||
}
|
||||
|
||||
if (additionalDepositFromSwap.plus(props.depositAmount).isGreaterThan(props.maxBorrowAmount)) {
|
||||
messages.push(getDepositCapMessage(props.borrowAsset.denom, depositCapLeft, 'borrow'))
|
||||
messages.push(getDepositCapMessage(props.borrowAsset.denom, depositCapLeft, 'borrow', assets))
|
||||
}
|
||||
|
||||
return messages
|
||||
}, [
|
||||
additionalDepositFromSwap,
|
||||
assets,
|
||||
borrowLiquidity,
|
||||
depositCapLeft,
|
||||
props.borrowAmount,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
|
||||
import DoubleLogo from 'components/DoubleLogo'
|
||||
import HLSTag from 'components/HLS/HLSTag'
|
||||
import Text from 'components/Text'
|
||||
import DoubleLogo from 'components/common/DoubleLogo'
|
||||
import HLSTag from 'components/hls/HLSTag'
|
||||
import Text from 'components/common/Text'
|
||||
|
||||
interface Props {
|
||||
primaryAsset: Asset
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import Button from 'components/common/Button'
|
||||
import LeverageSummary from 'components/Modals/HLS/Deposit/LeverageSummary'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import useStore from 'store'
|
||||
@@ -28,6 +29,7 @@ interface Props {
|
||||
|
||||
export default function ChangeLeverage(props: Props) {
|
||||
const { data: prices } = usePrices()
|
||||
const assets = useAllAssets()
|
||||
const [slippage] = useLocalStorage<number>(LocalStorageKeys.SLIPPAGE, DEFAULT_SETTINGS.slippage)
|
||||
const {
|
||||
updatedAccount,
|
||||
@@ -83,10 +85,11 @@ export default function ChangeLeverage(props: Props) {
|
||||
const [deposits, lends, debts, vaults] = getAccountPositionValues(
|
||||
updatedAccount || props.account,
|
||||
prices,
|
||||
assets,
|
||||
)
|
||||
|
||||
return deposits.plus(lends).plus(debts).plus(vaults)
|
||||
}, [prices, props.account, updatedAccount])
|
||||
}, [assets, prices, props.account, updatedAccount])
|
||||
|
||||
const handleOnClick = useCallback(() => {
|
||||
useStore.setState({ hlsManageModal: null })
|
||||
@@ -98,17 +101,19 @@ export default function ChangeLeverage(props: Props) {
|
||||
props.borrowAsset.denom,
|
||||
slippage,
|
||||
prices,
|
||||
assets,
|
||||
)
|
||||
changeHlsStakingLeverage({ accountId: props.account.id, actions })
|
||||
}, [
|
||||
currentDebt,
|
||||
changeHlsStakingLeverage,
|
||||
previousDebt,
|
||||
prices,
|
||||
props.account.id,
|
||||
props.borrowAsset.denom,
|
||||
props.collateralAsset.denom,
|
||||
props.borrowAsset.denom,
|
||||
props.account.id,
|
||||
slippage,
|
||||
prices,
|
||||
assets,
|
||||
changeHlsStakingLeverage,
|
||||
])
|
||||
|
||||
const addedDepositAmount = useMemo(
|
||||
@@ -132,20 +137,25 @@ export default function ChangeLeverage(props: Props) {
|
||||
const borrowLiquidity = props.borrowAsset.liquidity?.amount || BN_ZERO
|
||||
|
||||
if (borrowLiquidity.isLessThan(currentDebt.minus(previousDebt))) {
|
||||
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
|
||||
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity, assets))
|
||||
}
|
||||
|
||||
if (maxBorrowAmount.isLessThan(currentDebt)) {
|
||||
messages.push(getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow'))
|
||||
messages.push(
|
||||
getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow', assets),
|
||||
)
|
||||
}
|
||||
|
||||
if (addedDepositAmount.isGreaterThan(depositCapLeft)) {
|
||||
messages.push(getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'borrow'))
|
||||
messages.push(
|
||||
getDepositCapMessage(props.collateralAsset.denom, depositCapLeft, 'borrow', assets),
|
||||
)
|
||||
}
|
||||
|
||||
return messages
|
||||
}, [
|
||||
addedDepositAmount,
|
||||
assets,
|
||||
currentDebt,
|
||||
depositCapLeft,
|
||||
maxBorrowAmount,
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import Divider from 'components/Divider'
|
||||
import SummaryItems from 'components/SummaryItems'
|
||||
import Switch from 'components/Switch'
|
||||
import Text from 'components/Text'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import Button from 'components/common/Button'
|
||||
import Divider from 'components/common/Divider'
|
||||
import SummaryItems from 'components/common/SummaryItems'
|
||||
import Switch from 'components/common/Switch'
|
||||
import Text from 'components/common/Text'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useDepositActions from 'hooks/HLS/useDepositActions'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import useDepositActions from 'hooks/hls/useDepositActions'
|
||||
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
@@ -40,6 +41,7 @@ export default function Deposit(props: Props) {
|
||||
useUpdatedAccount(props.account)
|
||||
const { computeMaxBorrowAmount } = useHealthComputer(updatedAccount)
|
||||
|
||||
const assets = useAllAssets()
|
||||
const { data: prices } = usePrices()
|
||||
const [keepLeverage, toggleKeepLeverage] = useToggle(true)
|
||||
const collateralAssetAmountInWallet = BN(
|
||||
@@ -49,8 +51,8 @@ export default function Deposit(props: Props) {
|
||||
const borrowRate = useBorrowAsset(props.borrowAsset.denom)?.borrowRate || 0
|
||||
|
||||
const currentLeverage = useMemo(
|
||||
() => calculateAccountLeverage(props.account, prices).toNumber(),
|
||||
[prices, props.account],
|
||||
() => calculateAccountLeverage(props.account, prices, assets).toNumber(),
|
||||
[prices, props.account, assets],
|
||||
)
|
||||
|
||||
const depositCoin = useMemo(
|
||||
@@ -83,7 +85,7 @@ export default function Deposit(props: Props) {
|
||||
)
|
||||
|
||||
if (capLeft.isLessThan(depositCoin.amount.plus(addedDepositFromSwap))) {
|
||||
messages.push(getDepositCapMessage(props.collateralAsset.denom, capLeft, 'deposit'))
|
||||
messages.push(getDepositCapMessage(props.collateralAsset.denom, capLeft, 'deposit', assets))
|
||||
}
|
||||
|
||||
if (collateralAssetAmountInWallet.isZero()) {
|
||||
@@ -99,6 +101,7 @@ export default function Deposit(props: Props) {
|
||||
props.account.strategy.depositCap.used,
|
||||
props.collateralAsset.denom,
|
||||
props.collateralAsset.symbol,
|
||||
assets,
|
||||
])
|
||||
|
||||
const maxBorrowAmount = useMemo(
|
||||
@@ -109,17 +112,20 @@ export default function Deposit(props: Props) {
|
||||
const borrowWarningMessages = useMemo(() => {
|
||||
let messages: string[] = []
|
||||
if (borrowCoin.amount.isGreaterThan(maxBorrowAmount)) {
|
||||
messages.push(getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow'))
|
||||
messages.push(
|
||||
getHealthFactorMessage(props.borrowAsset.denom, maxBorrowAmount, 'borrow', assets),
|
||||
)
|
||||
}
|
||||
|
||||
const borrowLiquidity = props.borrowAsset.liquidity?.amount || BN_ZERO
|
||||
|
||||
if (borrowCoin.amount.isGreaterThan(borrowLiquidity)) {
|
||||
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity))
|
||||
messages.push(getLiquidityMessage(props.borrowAsset.denom, borrowLiquidity, assets))
|
||||
}
|
||||
|
||||
return messages
|
||||
}, [
|
||||
assets,
|
||||
borrowCoin.amount,
|
||||
maxBorrowAmount,
|
||||
props.borrowAsset.denom,
|
||||
@@ -151,9 +157,10 @@ export default function Deposit(props: Props) {
|
||||
const depositValue = getCoinValue(
|
||||
BNCoin.fromDenomAndBigNumber(props.collateralAsset.denom, amount),
|
||||
prices,
|
||||
assets,
|
||||
)
|
||||
const borrowValue = BN(currentLeverage - 1).times(depositValue)
|
||||
additionalDebt = getCoinAmount(props.borrowAsset.denom, borrowValue, prices)
|
||||
additionalDebt = getCoinAmount(props.borrowAsset.denom, borrowValue, prices, assets)
|
||||
}
|
||||
|
||||
simulateHlsStakingDeposit(
|
||||
@@ -168,6 +175,7 @@ export default function Deposit(props: Props) {
|
||||
props.borrowAsset.denom,
|
||||
props.collateralAsset.denom,
|
||||
simulateHlsStakingDeposit,
|
||||
assets,
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import SummaryItems from 'components/SummaryItems'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import Button from 'components/common/Button'
|
||||
import SummaryItems from 'components/common/SummaryItems'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
@@ -28,7 +28,7 @@ export default function Repay(props: Props) {
|
||||
const repay = useStore((s) => s.repay)
|
||||
|
||||
const currentDebt: BigNumber = useMemo(
|
||||
() => props.account.debts.find(byDenom(props.borrowAsset.denom)).amount || BN_ZERO,
|
||||
() => props.account.debts.find(byDenom(props.borrowAsset.denom))?.amount || BN_ZERO,
|
||||
[props.account.debts, props.borrowAsset.denom],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { useCallback, useMemo } from 'react'
|
||||
import { useCallback, useMemo } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import TokenInputWithSlider from 'components/TokenInput/TokenInputWithSlider'
|
||||
import Button from 'components/common/Button'
|
||||
import TokenInputWithSlider from 'components/common/TokenInput/TokenInputWithSlider'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import useStore from 'store'
|
||||
@@ -20,6 +21,7 @@ interface Props {
|
||||
export default function Withdraw(props: Props) {
|
||||
const { removedDeposits, removeDeposits, updatedAccount } = useUpdatedAccount(props.account)
|
||||
const { computeMaxWithdrawAmount } = useHealthComputer(updatedAccount)
|
||||
const assets = useAllAssets()
|
||||
const withdraw = useStore((s) => s.withdraw)
|
||||
const handleChange = useCallback(
|
||||
(amount: BigNumber) =>
|
||||
@@ -40,6 +42,7 @@ export default function Withdraw(props: Props) {
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
useStore.setState({ hlsManageModal: null })
|
||||
if (!removedDeposit) return
|
||||
withdraw({
|
||||
accountId: props.account.id,
|
||||
coins: [{ coin: removedDeposit }],
|
||||
@@ -52,10 +55,12 @@ export default function Withdraw(props: Props) {
|
||||
|
||||
const warningMessages = useMemo(() => {
|
||||
if (maxWithdrawAmount.isLessThan(withdrawAmount) || maxWithdrawAmount.isZero()) {
|
||||
return [getHealthFactorMessage(props.collateralAsset.denom, maxWithdrawAmount, 'withdraw')]
|
||||
return [
|
||||
getHealthFactorMessage(props.collateralAsset.denom, maxWithdrawAmount, 'withdraw', assets),
|
||||
]
|
||||
}
|
||||
return []
|
||||
}, [maxWithdrawAmount, props.collateralAsset.denom, withdrawAmount])
|
||||
}, [assets, maxWithdrawAmount, props.collateralAsset.denom, withdrawAmount])
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -6,15 +6,15 @@ import Deposit from 'components/Modals/HLS/Manage/Deposit'
|
||||
import Repay from 'components/Modals/HLS/Manage/Repay'
|
||||
import Withdraw from 'components/Modals/HLS/Manage/Withdraw'
|
||||
import ModalContentWithSummary from 'components/Modals/ModalContentWithSummary'
|
||||
import useAccount from 'hooks/useAccount'
|
||||
import useAccount from 'hooks/accounts/useAccount'
|
||||
import useAsset from 'hooks/assets/useAsset'
|
||||
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||
import useStore from 'store'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
|
||||
export default function HlsManageModalController() {
|
||||
const modal = useStore((s) => s.hlsManageModal)
|
||||
const { data: account } = useAccount(modal?.accountId)
|
||||
const collateralAsset = getAssetByDenom(modal?.staking.strategy.denoms.deposit || '')
|
||||
const collateralAsset = useAsset(modal?.staking.strategy.denoms.deposit || '')
|
||||
const borrowAsset = useBorrowAsset(modal?.staking.strategy.denoms.borrow || '')
|
||||
|
||||
if (!modal || !collateralAsset || !borrowAsset || !account) return null
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import React from 'react'
|
||||
|
||||
import Modal from 'components/Modal'
|
||||
import Modal from 'components/Modals/Modal'
|
||||
import Content from 'components/Modals/HLS/Deposit'
|
||||
import Header from 'components/Modals/HLS/Header'
|
||||
import useAsset from 'hooks/assets/useAsset'
|
||||
import useBorrowAsset from 'hooks/useBorrowAsset'
|
||||
import useStore from 'store'
|
||||
import { getAssetByDenom } from 'utils/assets'
|
||||
|
||||
export default function HlsModalController() {
|
||||
const modal = useStore((s) => s.hlsModal)
|
||||
|
||||
const primaryAsset = getAssetByDenom(
|
||||
const primaryAsset = useAsset(
|
||||
modal?.vault?.denoms.primary || modal?.strategy?.denoms.deposit || '',
|
||||
)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import { FormattedNumber } from 'components/FormattedNumber'
|
||||
import TitleAndSubCell from 'components/TitleAndSubCell'
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import { FormattedNumber } from 'components/common/FormattedNumber'
|
||||
import TitleAndSubCell from 'components/common/TitleAndSubCell'
|
||||
import useAssetIncentivesApy from 'hooks/useAssetIncentiveApy'
|
||||
import useCurrentWalletBalance from 'hooks/useCurrentWalletBalance'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
@@ -15,7 +15,7 @@ function DetailsHeader({ data }: Props) {
|
||||
const balanceInWallet = useCurrentWalletBalance(asset.denom)
|
||||
|
||||
return (
|
||||
<div className='flex gap-6 border-b border-white/5 px-6 py-4 gradient-header'>
|
||||
<div className='flex gap-6 px-6 py-4 border-b border-white/5 gradient-header'>
|
||||
{assetApy && (
|
||||
<>
|
||||
<TitleAndSubCell
|
||||
@@ -40,7 +40,7 @@ function DetailsHeader({ data }: Props) {
|
||||
<TitleAndSubCell
|
||||
title={
|
||||
<DisplayCurrency
|
||||
coin={new BNCoin({ denom: asset.denom, amount: accountLentAmount })}
|
||||
coin={new BNCoin({ denom: asset.denom, amount: accountLentAmount.toString() })}
|
||||
/>
|
||||
}
|
||||
sub={'Deposited'}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback } from 'react'
|
||||
|
||||
import AssetAmountSelectActionModal from 'components/Modals/AssetAmountSelectActionModal'
|
||||
import DetailsHeader from 'components/Modals/LendAndReclaim/DetailsHeader'
|
||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||
import useCurrentAccount from 'hooks/accounts/useCurrentAccount'
|
||||
import useLendAndReclaimModal from 'hooks/useLendAndReclaimModal'
|
||||
import { useUpdatedAccount } from 'hooks/useUpdatedAccount'
|
||||
import useStore from 'store'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import classNames from 'classnames'
|
||||
import { ReactNode, useEffect, useRef } from 'react'
|
||||
|
||||
import EscButton from 'components/Button/EscButton'
|
||||
import Card from 'components/Card'
|
||||
import EscButton from 'components/common/Button/EscButton'
|
||||
import Card from 'components/common/Card'
|
||||
|
||||
export interface ModalProps {
|
||||
header: string | ReactNode
|
||||
@@ -1,10 +1,10 @@
|
||||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
|
||||
import AccountSummary from 'components/Account/AccountSummary'
|
||||
import Card from 'components/Card'
|
||||
import { CircularProgress } from 'components/CircularProgress'
|
||||
import Modal, { ModalProps } from 'components/Modal'
|
||||
import AccountSummary from 'components/account/AccountSummary'
|
||||
import Card from 'components/common/Card'
|
||||
import { CircularProgress } from 'components/common/CircularProgress'
|
||||
import Modal, { ModalProps } from 'components/Modals/Modal'
|
||||
import useStore from 'store'
|
||||
|
||||
interface Props extends ModalProps {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
import Text from 'components/Text'
|
||||
import Text from 'components/common/Text'
|
||||
|
||||
interface Props {
|
||||
label: string
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import classNames from 'classnames'
|
||||
|
||||
import Switch from 'components/Switch'
|
||||
import Text from 'components/Text'
|
||||
import Switch from 'components/common/Switch'
|
||||
import Text from 'components/common/Text'
|
||||
|
||||
interface Props {
|
||||
onChange: (value: boolean) => void
|
||||
|
||||
@@ -1,24 +1,26 @@
|
||||
import classNames from 'classnames'
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
|
||||
import AssetImage from 'components/Asset/AssetImage'
|
||||
import Button from 'components/Button'
|
||||
import { ArrowCircle, Enter } from 'components/Icons'
|
||||
import Modal from 'components/Modal'
|
||||
import Modal from 'components/Modals/Modal'
|
||||
import SettingsOptions from 'components/Modals/Settings/SettingsOptions'
|
||||
import SettingsSwitch from 'components/Modals/Settings/SettingsSwitch'
|
||||
import NumberInput from 'components/NumberInput'
|
||||
import Select from 'components/Select'
|
||||
import Text from 'components/Text'
|
||||
import { TextLink } from 'components/TextLink'
|
||||
import Button from 'components/common/Button'
|
||||
import { ArrowCircle, Enter } from 'components/common/Icons'
|
||||
import NumberInput from 'components/common/NumberInput'
|
||||
import Select from 'components/common/Select'
|
||||
import Text from 'components/common/Text'
|
||||
import { TextLink } from 'components/common/TextLink'
|
||||
import AssetImage from 'components/common/assets/AssetImage'
|
||||
import { DEFAULT_SETTINGS } from 'constants/defaultSettings'
|
||||
import { LocalStorageKeys } from 'constants/localStorageKeys'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import useDisplayCurrencyAssets from 'hooks/assets/useDisplayCurrencyAssets'
|
||||
import useDisplayCurrency from 'hooks/localStorage/useDisplayCurrency'
|
||||
import useEnableAutoLendGlobal from 'hooks/localStorage/useEnableAutoLendGlobal'
|
||||
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
|
||||
import useAlertDialog from 'hooks/useAlertDialog'
|
||||
import useAutoLend from 'hooks/useAutoLend'
|
||||
import useLocalStorage from 'hooks/useLocalStorage'
|
||||
import useStore from 'store'
|
||||
import { getDisplayCurrencies } from 'utils/assets'
|
||||
import { BN } from 'utils/helpers'
|
||||
|
||||
const slippages = [0.02, 0.03]
|
||||
@@ -26,15 +28,13 @@ const slippages = [0.02, 0.03]
|
||||
export default function SettingsModal() {
|
||||
const modal = useStore((s) => s.settingsModal)
|
||||
const { open: showResetDialog } = useAlertDialog()
|
||||
const displayCurrencies = getDisplayCurrencies()
|
||||
const displayCurrencies = useDisplayCurrencyAssets()
|
||||
const { setAutoLendOnAllAccounts } = useAutoLend()
|
||||
const [customSlippage, setCustomSlippage] = useState<number>(0)
|
||||
const [inputRef, setInputRef] = useState<React.RefObject<HTMLInputElement>>()
|
||||
const [isCustom, setIsCustom] = useState(false)
|
||||
const [displayCurrency, setDisplayCurrency] = useLocalStorage<string>(
|
||||
LocalStorageKeys.DISPLAY_CURRENCY,
|
||||
DEFAULT_SETTINGS.displayCurrency,
|
||||
)
|
||||
|
||||
const [displayCurrency, setDisplayCurrency] = useDisplayCurrency()
|
||||
const [reduceMotion, setReduceMotion] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.REDUCE_MOTION,
|
||||
DEFAULT_SETTINGS.reduceMotion,
|
||||
@@ -43,14 +43,15 @@ export default function SettingsModal() {
|
||||
LocalStorageKeys.TUTORIAL,
|
||||
DEFAULT_SETTINGS.tutorial,
|
||||
)
|
||||
const [lendAssets, setLendAssets] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.LEND_ASSETS,
|
||||
DEFAULT_SETTINGS.lendAssets,
|
||||
)
|
||||
const [enableAutoLendGlobal, setLendAssets] = useEnableAutoLendGlobal()
|
||||
const [slippage, setSlippage] = useLocalStorage<number>(
|
||||
LocalStorageKeys.SLIPPAGE,
|
||||
DEFAULT_SETTINGS.slippage,
|
||||
)
|
||||
const [updateOracle, setUpdateOracle] = useLocalStorage<boolean>(
|
||||
LocalStorageKeys.UPDATE_ORACLE,
|
||||
DEFAULT_SETTINGS.updateOracle,
|
||||
)
|
||||
|
||||
const displayCurrenciesOptions = useMemo(
|
||||
() =>
|
||||
@@ -148,9 +149,16 @@ export default function SettingsModal() {
|
||||
handleDisplayCurrency(DEFAULT_SETTINGS.displayCurrency)
|
||||
handleSlippage(DEFAULT_SETTINGS.slippage)
|
||||
handleReduceMotion(DEFAULT_SETTINGS.reduceMotion)
|
||||
handleLendAssets(DEFAULT_SETTINGS.lendAssets)
|
||||
handleLendAssets(DEFAULT_SETTINGS.enableAutoLendGlobal)
|
||||
}, [handleDisplayCurrency, handleReduceMotion, handleLendAssets, handleSlippage])
|
||||
|
||||
const handleUpdateOracle = useCallback(
|
||||
(value: boolean) => {
|
||||
setUpdateOracle(value)
|
||||
},
|
||||
[setUpdateOracle],
|
||||
)
|
||||
|
||||
const showResetModal = useCallback(() => {
|
||||
showResetDialog({
|
||||
icon: (
|
||||
@@ -193,12 +201,21 @@ export default function SettingsModal() {
|
||||
>
|
||||
<SettingsSwitch
|
||||
onChange={handleLendAssets}
|
||||
name='lendAssets'
|
||||
value={lendAssets}
|
||||
name='enableAutoLendGlobal'
|
||||
value={enableAutoLendGlobal}
|
||||
label='Lend assets in Credit Accounts'
|
||||
description='By turning this on you will automatically lend out all the assets you deposit into your Credit Accounts to earn yield.'
|
||||
withStatus
|
||||
/>
|
||||
|
||||
<SettingsSwitch
|
||||
onChange={handleUpdateOracle}
|
||||
name='updateOracle'
|
||||
value={updateOracle}
|
||||
label='Update Pyth Oracles on transactions'
|
||||
description={`When this setting is on, your Mars transactions will automatically trigger an update of Pyth's price oracles. This increases the accuracy of the prices shown in the UI. In some instances, this could cause your transactions to fail when using a Ledger hardware wallet. If you encounter transaction failures with Ledger, please turn this setting off.`}
|
||||
withStatus
|
||||
/>
|
||||
<SettingsSwitch
|
||||
onChange={handleReduceMotion}
|
||||
name='reduceMotion'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Button from 'components/Button'
|
||||
import Button from 'components/common/Button'
|
||||
import { NoIcon, YesIcon } from 'components/Modals/AlertDialog/ButtonIcons'
|
||||
import Text from 'components/Text'
|
||||
import Text from 'components/common/Text'
|
||||
import useAccountId from 'hooks/useAccountId'
|
||||
import useStore from 'store'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LockUnlocked } from 'components/Icons'
|
||||
import Modal from 'components/Modal'
|
||||
import { LockUnlocked } from 'components/common/Icons'
|
||||
import Modal from 'components/Modals/Modal'
|
||||
import UnlockModalContent from 'components/Modals/Unlock/UnlockModalContent'
|
||||
import useStore from 'store'
|
||||
|
||||
|
||||
@@ -1,24 +1,25 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import React, { useEffect, useMemo, useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import DepositCapMessage from 'components/DepositCapMessage'
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Divider from 'components/Divider'
|
||||
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
|
||||
import Index from 'components/Slider'
|
||||
import Text from 'components/Text'
|
||||
import TokenInput from 'components/TokenInput'
|
||||
import Button from 'components/common/Button'
|
||||
import DepositCapMessage from 'components/common/DepositCapMessage'
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import Divider from 'components/common/Divider'
|
||||
import { ArrowRight, ExclamationMarkCircled } from 'components/common/Icons'
|
||||
import Index from 'components/common/Slider'
|
||||
import Text from 'components/common/Text'
|
||||
import TokenInput from 'components/common/TokenInput'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { ORACLE_DENOM } from 'constants/oracle'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import useMarketAssets from 'hooks/markets/useMarketAssets'
|
||||
import useHealthComputer from 'hooks/useHealthComputer'
|
||||
import useMarketAssets from 'hooks/useMarketAssets'
|
||||
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 { byDenom } from 'utils/array'
|
||||
import { findCoinByDenom, getAssetByDenom } from 'utils/assets'
|
||||
import { findCoinByDenom } from 'utils/assets'
|
||||
import { formatPercent } from 'utils/formatters'
|
||||
import { getValueFromBNCoins, mergeBNCoinArrays } from 'utils/helpers'
|
||||
|
||||
@@ -36,6 +37,7 @@ export interface VaultBorrowingsProps {
|
||||
}
|
||||
|
||||
export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
const assets = useAllAssets()
|
||||
const { borrowings, onChangeBorrowings } = props
|
||||
const { data: marketAssets } = useMarketAssets()
|
||||
const { data: prices } = usePrices()
|
||||
@@ -85,8 +87,8 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
}, [maxBorrowAmountsRaw, props.borrowings])
|
||||
|
||||
const totalValue = useMemo(
|
||||
() => getValueFromBNCoins(mergeBNCoinArrays(props.deposits, props.borrowings), prices),
|
||||
[props.borrowings, props.deposits, prices],
|
||||
() => getValueFromBNCoins(mergeBNCoinArrays(props.deposits, props.borrowings), prices, assets),
|
||||
[props.deposits, props.borrowings, prices, assets],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -176,7 +178,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
return (
|
||||
<div className='flex flex-col flex-1 gap-4 p-4'>
|
||||
{props.borrowings.map((coin) => {
|
||||
const asset = getAssetByDenom(coin.denom)
|
||||
const asset = assets.find(byDenom(coin.denom))
|
||||
const maxAmount = maxBorrowAmounts.find(byDenom(coin.denom))?.amount
|
||||
if (!asset || !maxAmount)
|
||||
return <React.Fragment key={`input-${coin.denom}`}></React.Fragment>
|
||||
@@ -224,7 +226,7 @@ export default function VaultBorrowings(props: VaultBorrowingsProps) {
|
||||
/>
|
||||
</div>
|
||||
{props.borrowings.map((coin) => {
|
||||
const asset = getAssetByDenom(coin.denom)
|
||||
const asset = assets.find(byDenom(coin.denom))
|
||||
const borrowRate = marketAssets?.find((market) => market.denom === coin.denom)?.apy.borrow
|
||||
|
||||
if (!asset || !borrowRate)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import classNames from 'classnames'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Text from 'components/Text'
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import Text from 'components/common/Text'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { ORACLE_DENOM } from 'constants/oracle'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { formatAmountWithSymbol, getCoinValue } from 'utils/formatters'
|
||||
import { ORACLE_DENOM } from 'constants/oracle'
|
||||
|
||||
interface Props {
|
||||
borrowings: BNCoin[]
|
||||
@@ -16,16 +17,16 @@ interface Props {
|
||||
|
||||
export default function VaultBorrowingsSubTitle(props: Props) {
|
||||
const { data: prices } = usePrices()
|
||||
|
||||
const assets = useAllAssets()
|
||||
const borrowingValue = useMemo(() => {
|
||||
let borrowingValue = BN_ZERO
|
||||
props.borrowings.map((coin) => {
|
||||
const price = prices.find((p) => p.denom === coin.denom)?.amount
|
||||
if (!price || coin.amount.isZero()) return
|
||||
borrowingValue = getCoinValue(coin, prices)
|
||||
borrowingValue = getCoinValue(coin, prices, assets)
|
||||
})
|
||||
return borrowingValue
|
||||
}, [props.borrowings, prices])
|
||||
}, [props.borrowings, prices, assets])
|
||||
|
||||
const borrowingTexts = useMemo(
|
||||
() =>
|
||||
@@ -38,10 +39,10 @@ export default function VaultBorrowingsSubTitle(props: Props) {
|
||||
index !== 0 && 'ml-1 before:pr-1 before:content-["+"]',
|
||||
)}
|
||||
>
|
||||
{formatAmountWithSymbol(borrowing.toCoin())}
|
||||
{formatAmountWithSymbol(borrowing.toCoin(), assets)}
|
||||
</Text>
|
||||
)),
|
||||
[props.borrowings],
|
||||
[assets, props.borrowings],
|
||||
)
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import BigNumber from 'bignumber.js'
|
||||
import { useMemo, useState } from 'react'
|
||||
|
||||
import Button from 'components/Button'
|
||||
import DepositCapMessage from 'components/DepositCapMessage'
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Divider from 'components/Divider'
|
||||
import { Gauge } from 'components/Gauge'
|
||||
import { ArrowRight, ExclamationMarkCircled } from 'components/Icons'
|
||||
import Slider from 'components/Slider'
|
||||
import Switch from 'components/Switch'
|
||||
import Text from 'components/Text'
|
||||
import TokenInput from 'components/TokenInput'
|
||||
import Button from 'components/common/Button'
|
||||
import DepositCapMessage from 'components/common/DepositCapMessage'
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import Divider from 'components/common/Divider'
|
||||
import { Gauge } from 'components/common/Gauge'
|
||||
import { ArrowRight, ExclamationMarkCircled } from 'components/common/Icons'
|
||||
import Slider from 'components/common/Slider'
|
||||
import Switch from 'components/common/Switch'
|
||||
import Text from 'components/common/Text'
|
||||
import TokenInput from 'components/common/TokenInput'
|
||||
import { BN_ZERO } from 'constants/math'
|
||||
import { ORACLE_DENOM } from 'constants/oracle'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { accumulateAmounts } from 'utils/accounts'
|
||||
@@ -41,6 +42,7 @@ export default function VaultDeposit(props: Props) {
|
||||
[account.deposits, account.lends, primaryAsset.denom, secondaryAsset.denom],
|
||||
)
|
||||
const { data: prices } = usePrices()
|
||||
const assets = useAllAssets()
|
||||
const primaryPrice = prices.find(byDenom(primaryAsset.denom))?.amount ?? BN_ZERO
|
||||
const secondaryPrice = prices.find(byDenom(secondaryAsset.denom))?.amount ?? BN_ZERO
|
||||
|
||||
@@ -55,13 +57,13 @@ export default function VaultDeposit(props: Props) {
|
||||
}, [deposits, secondaryAsset.denom])
|
||||
|
||||
const primaryValue = useMemo(
|
||||
() => getValueFromBNCoins([primaryCoin], prices),
|
||||
[primaryCoin, prices],
|
||||
() => getValueFromBNCoins([primaryCoin], prices, assets),
|
||||
[primaryCoin, prices, assets],
|
||||
)
|
||||
|
||||
const totalValue = useMemo(
|
||||
() => getValueFromBNCoins([primaryCoin, secondaryCoin], prices),
|
||||
[primaryCoin, secondaryCoin, prices],
|
||||
() => getValueFromBNCoins([primaryCoin, secondaryCoin], prices, assets),
|
||||
[primaryCoin, secondaryCoin, prices, assets],
|
||||
)
|
||||
|
||||
const primaryValuePercentage = useMemo(() => {
|
||||
|
||||
@@ -2,13 +2,14 @@ import BigNumber from 'bignumber.js'
|
||||
import classNames from 'classnames'
|
||||
import { useMemo } from 'react'
|
||||
|
||||
import DisplayCurrency from 'components/DisplayCurrency'
|
||||
import Text from 'components/Text'
|
||||
import DisplayCurrency from 'components/common/DisplayCurrency'
|
||||
import Text from 'components/common/Text'
|
||||
import { ORACLE_DENOM } from 'constants/oracle'
|
||||
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||
import usePrices from 'hooks/usePrices'
|
||||
import { BNCoin } from 'types/classes/BNCoin'
|
||||
import { formatAmountWithSymbol } from 'utils/formatters'
|
||||
import { getValueFromBNCoins } from 'utils/helpers'
|
||||
import { ORACLE_DENOM } from 'constants/oracle'
|
||||
|
||||
interface Props {
|
||||
primaryAmount: BigNumber
|
||||
@@ -20,28 +21,35 @@ interface Props {
|
||||
|
||||
export default function VaultDepositSubTitle(props: Props) {
|
||||
const { data: prices } = usePrices()
|
||||
const assets = useAllAssets()
|
||||
const primaryText = useMemo(
|
||||
() => (
|
||||
<Text size='xs' className='inline mt-1 text-white/60'>
|
||||
{formatAmountWithSymbol({
|
||||
denom: props.primaryAsset.denom,
|
||||
amount: props.primaryAmount.toString(),
|
||||
})}
|
||||
{formatAmountWithSymbol(
|
||||
{
|
||||
denom: props.primaryAsset.denom,
|
||||
amount: props.primaryAmount.toString(),
|
||||
},
|
||||
assets,
|
||||
)}
|
||||
</Text>
|
||||
),
|
||||
[props.primaryAmount, props.primaryAsset.denom],
|
||||
[assets, props.primaryAmount, props.primaryAsset.denom],
|
||||
)
|
||||
|
||||
const secondaryText = useMemo(
|
||||
() => (
|
||||
<Text size='xs' className='inline mt-1 text-white/60 ml-1 before:pr-1 before:content-["+"]'>
|
||||
{formatAmountWithSymbol({
|
||||
denom: props.secondaryAsset.denom,
|
||||
amount: props.secondaryAmount.toString(),
|
||||
})}
|
||||
{formatAmountWithSymbol(
|
||||
{
|
||||
denom: props.secondaryAsset.denom,
|
||||
amount: props.secondaryAmount.toString(),
|
||||
},
|
||||
assets,
|
||||
)}
|
||||
</Text>
|
||||
),
|
||||
[props.secondaryAmount, props.secondaryAsset.denom],
|
||||
[assets, props.secondaryAmount, props.secondaryAsset.denom],
|
||||
)
|
||||
|
||||
const positionValue = getValueFromBNCoins(
|
||||
@@ -50,6 +58,7 @@ export default function VaultDepositSubTitle(props: Props) {
|
||||
BNCoin.fromDenomAndBigNumber(props.secondaryAsset.denom, props.secondaryAmount),
|
||||
],
|
||||
prices,
|
||||
assets,
|
||||
)
|
||||
|
||||
const showPrimaryText = !props.primaryAmount.isZero()
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user