This commit is contained in:
Linkie Link
2024-01-25 14:30:01 +01:00
committed by GitHub
parent 68c60c29df
commit f6f46d3372
678 changed files with 11366 additions and 8062 deletions
+9 -4
View File
@@ -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
View File
@@ -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,
}
+3 -6
View File
@@ -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
+11 -6
View File
@@ -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(),
})
})
+9 -8
View File
@@ -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 -4
View File
@@ -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)
+6 -4
View File
@@ -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,
}),
),
+5 -5
View File
@@ -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 }),
+9 -7
View File
@@ -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 -3
View File
@@ -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(
+5 -3
View File
@@ -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>(
+4 -5
View File
@@ -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,
+25 -14
View File
@@ -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
}
}
+5 -3
View File
@@ -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) {
+14
View File
@@ -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))
}
+3 -4
View File
@@ -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
+8 -4
View File
@@ -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) {
+6 -6
View File
@@ -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
View File
@@ -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
}
-20
View File
@@ -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
View File
@@ -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]
}
+5 -4
View File
@@ -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 []
}
}
+16 -4
View File
@@ -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
}
+6 -2
View File
@@ -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)
+53
View File
@@ -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
}
-42
View File
@@ -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
}
+6 -2
View File
@@ -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,
+45 -25
View File
@@ -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 {
+10 -5
View File
@@ -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
+5 -3
View File
@@ -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) {
-21
View File
@@ -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
}
}
+2 -3
View File
@@ -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(
+6 -10
View File
@@ -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)
+3 -2
View File
@@ -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'))
}
+7 -4
View File
@@ -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))
}
+6 -4
View File
@@ -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
+5 -4
View File
@@ -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>
)
}
-50
View File
@@ -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>
)
}
-59
View File
@@ -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>
)
}
-82
View File
@@ -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>
)
}
-83
View File
@@ -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>
)
}
-9
View File
@@ -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

-68
View File
@@ -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 (
+4 -4
View File
@@ -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],
)
}
+15 -15
View File
@@ -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,
+2 -2
View File
@@ -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'
+3 -3
View File
@@ -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'
+2 -2
View File
@@ -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,
+3 -3
View File
@@ -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,
+21 -13
View File
@@ -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,
],
)
+4 -4
View File
@@ -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],
)
+10 -5
View File
@@ -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 (
<>
+3 -3
View File
@@ -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
+3 -3
View File
@@ -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
+39 -22
View File
@@ -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'
+2 -2
View File
@@ -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'
+16 -14
View File
@@ -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 (
+16 -14
View File
@@ -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