Mp 2435 display currency (#155)

This commit is contained in:
Bob van der Helm 2023-04-14 14:52:44 +02:00 committed by GitHub
parent 4847121180
commit 0a796a3d94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 180 additions and 96 deletions

View File

@ -28,14 +28,6 @@ const accountCardHeaderClasses = classNames(
'border border-transparent border-b-white/20',
)
// TODO: Make this dynamic (token select)
const formatOptions = {
decimals: ASSETS[0].decimals,
minDecimals: 0,
maxDecimals: ASSETS[0].decimals,
suffix: ` ${ASSETS[0].symbol}`,
}
export default function AccountList(props: Props) {
const router = useRouter()
const params = useParams()
@ -108,11 +100,7 @@ export default function AccountList(props: Props) {
{isActive ? (
<>
<div className='w-full border border-transparent border-b-white/20 p-4'>
<AccountStats
balance={formatValue(selectedAccountBalance, formatOptions)}
risk={75}
health={0.85}
/>
<AccountStats balance={selectedAccountBalance} risk={75} health={0.85} />
</div>
<div className='grid grid-flow-row grid-cols-2 gap-4 p-4'>
<Button
@ -158,11 +146,7 @@ export default function AccountList(props: Props) {
</>
) : (
<div className='w-full p-4'>
<AccountStats
balance={formatValue(positionBalance, formatOptions)}
risk={60}
health={0.5}
/>
<AccountStats balance={positionBalance} risk={60} health={0.5} />
</div>
)}
</Card>

View File

@ -1,10 +1,11 @@
'use client'
import DisplayCurrency from 'components/DisplayCurrency'
import { Heart, Shield } from 'components/Icons'
import { Text } from 'components/Text'
import useStore from 'store'
interface Props {
balance: string
balance: number
risk: number
health: number
}
@ -12,12 +13,14 @@ interface Props {
export default function AccountStats(props: Props) {
const enableAnimations = useStore((s) => s.enableAnimations)
const healthBarWidth = 53 * props.health
const baseCurrency = useStore((s) => s.baseCurrency)
return (
<div className='w-full flex-wrap'>
<Text className='w-full' size='xl'>
{props.balance}
</Text>
<DisplayCurrency
coin={{ amount: props.balance.toString(), denom: baseCurrency.denom }}
className='w-full text-xl'
/>
<div className='mt-1 flex w-full items-center'>
<Text size='xs' className='flex items-center'>
<Shield className='mr-1.5 h-3' />

View File

@ -1,5 +1,6 @@
import { FormattedNumber } from 'components/FormattedNumber'
import TitleAndSubCell from 'components/TitleAndSubCell'
import DisplayCurrency from 'components/DisplayCurrency'
interface Props {
asset: Asset
@ -15,12 +16,7 @@ export default function AmountAndValue(props: Props) {
options={{ decimals: props.asset.decimals, abbreviated: true }}
/>
}
sub={
<FormattedNumber
amount={props.amount}
options={{ prefix: '$', abbreviated: true, decimals: props.asset.decimals }}
/>
}
sub={<DisplayCurrency coin={{ amount: props.amount, denom: props.asset.denom }} />}
className='justify-end'
/>
)

View File

@ -1,4 +1,3 @@
import BigNumber from 'bignumber.js'
import Image from 'next/image'
import { useState } from 'react'

View File

@ -0,0 +1,51 @@
import { Coin } from '@cosmjs/stargate'
import BigNumber from 'bignumber.js'
import useStore from 'store'
import { getMarketAssets } from 'utils/assets'
import { FormattedNumber } from './FormattedNumber'
interface Props {
coin: Coin
className?: string
prefixClassName?: string
valueClassName?: string
isApproximation?: boolean
}
export default function DisplayCurrency(props: Props) {
const displayCurrency = useStore((s) => s.displayCurrency)
const prices = useStore((s) => s.prices)
function convertToDisplayAmount(coin: Coin) {
const price = prices.find((price) => price.denom === coin.denom)
const asset = getMarketAssets().find((asset) => asset.denom === coin.denom)
const displayPrice = prices.find((price) => price.denom === displayCurrency.denom)
if (!price || !asset || !displayPrice) return '0'
return new BigNumber(coin.amount)
.times(price.amount)
.div(displayPrice.amount)
.integerValue(BigNumber.ROUND_HALF_DOWN)
.toNumber()
}
return (
<FormattedNumber
amount={convertToDisplayAmount(props.coin)}
options={{
minDecimals: 0,
maxDecimals: 2,
abbreviated: true,
decimals: displayCurrency.decimals,
prefix: `${props.isApproximation ? '~' : ''}${
displayCurrency.prefix ? displayCurrency.prefix : ''
}`,
suffix: displayCurrency.symbol ? ` ${displayCurrency.symbol}` : '',
}}
/>
)
}

View File

@ -8,15 +8,29 @@ import { Overlay } from 'components/Overlay/Overlay'
import Switch from 'components/Switch'
import { Text } from 'components/Text'
import { Tooltip } from 'components/Tooltip'
import { ENABLE_ANIMATIONS_KEY } from 'constants/localStore'
import { DISPLAY_CURRENCY_KEY, ENABLE_ANIMATIONS_KEY } from 'constants/localStore'
import { useAnimations } from 'hooks/useAnimations'
import useStore from 'store'
import { getDisplayCurrencies } from 'utils/assets'
import { ASSETS } from 'constants/assets'
export default function Settings() {
useAnimations()
const [showMenu, setShowMenu] = useState(false)
const enableAnimations = useStore((s) => s.enableAnimations)
const displayCurrency = useStore((s) => s.displayCurrency)
const displayCurrencies = getDisplayCurrencies()
const storageDisplayCurrency = localStorage.getItem(DISPLAY_CURRENCY_KEY)
if (storageDisplayCurrency) {
const storedDisplayCurrency = ASSETS.find(
(asset) => asset.symbol === JSON.parse(storageDisplayCurrency).symbol,
)
if (storedDisplayCurrency && storedDisplayCurrency !== displayCurrency) {
setDisplayCurrency(storedDisplayCurrency)
}
}
function handleReduceMotion(val: boolean) {
useStore.setState({ enableAnimations: !val })
@ -24,6 +38,18 @@ export default function Settings() {
window.localStorage.setItem(ENABLE_ANIMATIONS_KEY, val ? 'false' : 'true')
}
function handleCurrencyChange(e: React.ChangeEvent<HTMLSelectElement>) {
const displayCurrency = displayCurrencies.find((c) => c.symbol === e.target.value)
if (!displayCurrency) return
setDisplayCurrency(displayCurrency)
}
function setDisplayCurrency(displayCurrency: Asset) {
useStore.setState({ displayCurrency: displayCurrency })
localStorage.setItem(DISPLAY_CURRENCY_KEY, JSON.stringify(displayCurrency))
}
return (
<div className='relative'>
<Button
@ -54,6 +80,31 @@ export default function Settings() {
</div>
<Switch name='reduceMotion' checked={!enableAnimations} onChange={handleReduceMotion} />
</div>
<div className='mt-4 flex w-full flex-col'>
<div className='flex'>
<Text size='sm' className='mr-2'>
Display Currency
</Text>
<Tooltip
content={
<Text size='sm'>
Sets the denomination of values to a different currency. While OSMO is the
currency the TWAP oracles return. All other values are fetched from liquidity
pools.
</Text>
}
/>
</div>
<select
value={displayCurrency.symbol}
onChange={handleCurrencyChange}
className='mt-2 w-full rounded-sm border border-white/20 bg-transparent p-1 text-sm'
>
{displayCurrencies.map((currency) => (
<option key={currency.denom}>{currency.symbol}</option>
))}
</select>
</div>
</div>
</Overlay>
</div>

View File

@ -11,6 +11,7 @@ export const ASSETS: Asset[] = [
logo: '/tokens/osmo.svg',
isEnabled: true,
isMarket: true,
isDisplayCurrency: true,
},
{
symbol: 'ATOM',
@ -22,6 +23,7 @@ export const ASSETS: Asset[] = [
hasOraclePrice: true,
isEnabled: IS_TESTNET ? true : false,
isMarket: true,
isDisplayCurrency: true,
},
{
symbol: 'CRO',
@ -58,5 +60,6 @@ export const ASSETS: Asset[] = [
hasOraclePrice: true,
isMarket: IS_TESTNET,
isEnabled: true,
isDisplayCurrency: true,
},
]

View File

@ -37,3 +37,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(200).json(data)
}
interface TokenPricesResult {
prices: {
[key: string]: {
denom: string
price: string
}
}
}

View File

@ -3,12 +3,16 @@ import { devtools } from 'zustand/middleware'
import { BroadcastSlice, createBroadcastSlice } from 'store/slices/broadcast'
import { CommonSlice, createCommonSlice } from 'store/slices/common'
import { createCurrencySlice, CurrencySlice } from 'store/slices/currency'
import { createModalSlice, ModalSlice } from 'store/slices/modal'
export interface Store extends CommonSlice, BroadcastSlice {}
export interface Store extends CommonSlice, BroadcastSlice, CurrencySlice, ModalSlice {}
const store = (set: SetState<any>, get: GetState<any>) => ({
...createCommonSlice(set, get),
...createBroadcastSlice(set, get),
...createCurrencySlice(set, get),
...createModalSlice(set, get),
})
let useStore: UseBoundStore<StoreApi<Store>>

View File

@ -2,78 +2,23 @@ import { SigningCosmWasmClient } from '@cosmjs/cosmwasm-stargate'
import { WalletClient, WalletConnectionStatus } from '@marsprotocol/wallet-connector'
import { GetState, SetState } from 'zustand'
import { ENV } from 'constants/env'
import { MarsAccountNftClient } from 'types/generated/mars-account-nft/MarsAccountNft.client'
import { MarsCreditManagerClient } from 'types/generated/mars-credit-manager/MarsCreditManager.client'
import { MarsSwapperBaseClient } from 'types/generated/mars-swapper-base/MarsSwapperBase.client'
export interface CommonSlice {
accounts: Account[] | null
address?: string
borrowModal: {
asset: Asset
marketData: BorrowAsset | BorrowAssetActive
isRepay?: boolean
} | null
client?: WalletClient
clients: {
accountNft?: MarsAccountNftClient
creditManager?: MarsCreditManagerClient
swapperBase?: MarsSwapperBaseClient
}
createAccountModal: boolean
deleteAccountModal: boolean
enableAnimations: boolean
fundAccountModal: boolean
isOpen: boolean
prices: Coin[]
selectedAccount: string | null
signingClient?: SigningCosmWasmClient
client?: WalletClient
status: WalletConnectionStatus
withdrawModal: boolean
initClients: (address: string, signingClient: SigningCosmWasmClient) => void
}
export function createCommonSlice(set: SetState<CommonSlice>, get: GetState<CommonSlice>) {
return {
accounts: null,
borrowModal: null,
createAccountModal: false,
clients: {},
deleteAccountModal: false,
creditAccounts: null,
enableAnimations: true,
fundAccountModal: false,
isOpen: true,
prices: [],
repayModal: false,
selectedAccount: null,
status: WalletConnectionStatus.Unconnected,
withdrawModal: false,
initClients: (address: string, signingClient: SigningCosmWasmClient) => {
if (!signingClient) return
const accountNft = new MarsAccountNftClient(
signingClient,
address,
ENV.ADDRESS_ACCOUNT_NFT || '',
)
const creditManager = new MarsCreditManagerClient(
signingClient,
address,
ENV.ADDRESS_CREDIT_MANAGER || '',
)
const swapperBase = new MarsSwapperBaseClient(
signingClient,
address,
ENV.ADDRESS_SWAPPER || '',
)
set(() => ({
clients: {
accountNft,
creditManager,
swapperBase,
},
}))
},
}
}

View File

@ -0,0 +1,18 @@
import { Coin } from '@cosmjs/stargate'
import { GetState, SetState } from 'zustand'
import { ASSETS } from 'constants/assets'
export interface CurrencySlice {
baseCurrency: Asset
displayCurrency: Asset
prices: Coin[]
}
export function createCurrencySlice(set: SetState<CurrencySlice>, get: GetState<CurrencySlice>) {
return {
baseCurrency: ASSETS[0],
displayCurrency: ASSETS.find((asset) => asset.denom === ASSETS[0].denom)!,
prices: [],
}
}

23
src/store/slices/modal.ts Normal file
View File

@ -0,0 +1,23 @@
import { GetState, SetState } from 'zustand'
export interface ModalSlice {
borrowModal: {
asset: Asset
marketData: BorrowAsset | BorrowAssetActive
isRepay?: boolean
} | null
createAccountModal: boolean
deleteAccountModal: boolean
fundAccountModal: boolean
withdrawModal: boolean
}
export function createModalSlice(set: SetState<ModalSlice>, get: GetState<ModalSlice>) {
return {
borrowModal: null,
createAccountModal: false,
deleteAccountModal: false,
fundAccountModal: false,
withdrawModal: false,
}
}

View File

@ -3,6 +3,7 @@ interface Asset {
name: string
denom: string
symbol: 'OSMO' | 'ATOM' | 'CRO' | 'MARS' | 'JUNO'
prefix?: string
contract_addr?: string
logo: string
decimals: number
@ -10,6 +11,7 @@ interface Asset {
poolId?: number
isEnabled: boolean
isMarket: boolean
isDisplayCurrency?: boolean
}
interface OtherAsset extends Omit<Asset, 'symbol'> {

View File

@ -1,8 +0,0 @@
interface TokenPricesResult {
prices: {
[key: string]: {
denom: string
price: string
}
}
}

View File

@ -15,3 +15,7 @@ export function getMarketAssets(): Asset[] {
export function getBaseAsset() {
return ASSETS.find((asset) => asset.denom === 'uosmo')!
}
export function getDisplayCurrencies() {
return ASSETS.filter((asset) => asset.isDisplayCurrency)
}