Edit perps position (#728)
This commit is contained in:
parent
060a8b8797
commit
647a287a6b
@ -7,6 +7,7 @@ import { MarsIncentivesQueryClient } from 'types/generated/mars-incentives/MarsI
|
|||||||
import { MarsMockVaultQueryClient } from 'types/generated/mars-mock-vault/MarsMockVault.client'
|
import { MarsMockVaultQueryClient } from 'types/generated/mars-mock-vault/MarsMockVault.client'
|
||||||
import { MarsOracleOsmosisQueryClient } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.client'
|
import { MarsOracleOsmosisQueryClient } from 'types/generated/mars-oracle-osmosis/MarsOracleOsmosis.client'
|
||||||
import { MarsParamsQueryClient } from 'types/generated/mars-params/MarsParams.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 { MarsRedBankQueryClient } from 'types/generated/mars-red-bank/MarsRedBank.client'
|
||||||
import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client'
|
import { MarsSwapperOsmosisQueryClient } from 'types/generated/mars-swapper-osmosis/MarsSwapperOsmosis.client'
|
||||||
|
|
||||||
@ -18,6 +19,7 @@ let _redBankQueryClient: Map<string, MarsRedBankQueryClient> = new Map()
|
|||||||
let _paramsQueryClient: Map<string, MarsParamsQueryClient> = new Map()
|
let _paramsQueryClient: Map<string, MarsParamsQueryClient> = new Map()
|
||||||
let _incentivesQueryClient: Map<string, MarsIncentivesQueryClient> = new Map()
|
let _incentivesQueryClient: Map<string, MarsIncentivesQueryClient> = new Map()
|
||||||
let _swapperOsmosisClient: Map<string, MarsSwapperOsmosisQueryClient> = new Map()
|
let _swapperOsmosisClient: Map<string, MarsSwapperOsmosisQueryClient> = new Map()
|
||||||
|
let _perpsClient: Map<string, MarsPerpsQueryClient> = new Map()
|
||||||
let _ICNSQueryClient: Map<string, ICNSQueryClient> = new Map()
|
let _ICNSQueryClient: Map<string, ICNSQueryClient> = new Map()
|
||||||
|
|
||||||
const getClient = async (rpc: string) => {
|
const getClient = async (rpc: string) => {
|
||||||
@ -159,6 +161,22 @@ const getSwapperQueryClient = async (chainConfig: ChainConfig) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getPerpsQueryClient = async (chainConfig: ChainConfig) => {
|
||||||
|
try {
|
||||||
|
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 _perpsClient.get(key)!
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const getICNSQueryClient = async (chainConfig: ChainConfig) => {
|
const getICNSQueryClient = async (chainConfig: ChainConfig) => {
|
||||||
try {
|
try {
|
||||||
const contract = chainConfig.contracts.params
|
const contract = chainConfig.contracts.params
|
||||||
@ -186,4 +204,5 @@ export {
|
|||||||
getRedBankQueryClient,
|
getRedBankQueryClient,
|
||||||
getSwapperQueryClient,
|
getSwapperQueryClient,
|
||||||
getVaultQueryClient,
|
getVaultQueryClient,
|
||||||
|
getPerpsQueryClient,
|
||||||
}
|
}
|
||||||
|
10
src/api/perps/getOpeningFee.ts
Normal file
10
src/api/perps/getOpeningFee.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { getPerpsQueryClient } from 'api/cosmwasm-client'
|
||||||
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
|
||||||
|
export default async function getOpeningFee(chainConfig: ChainConfig, denom: string, size: string) {
|
||||||
|
const perpsClient = await getPerpsQueryClient(chainConfig)
|
||||||
|
|
||||||
|
return perpsClient
|
||||||
|
.openingFee({ denom, size: size as any })
|
||||||
|
.then((resp) => BNCoin.fromCoin(resp.fee))
|
||||||
|
}
|
18
src/components/Asset/AssetAmount.tsx
Normal file
18
src/components/Asset/AssetAmount.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { FormattedNumber } from 'components/FormattedNumber'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
asset: Asset
|
||||||
|
amount: number
|
||||||
|
}
|
||||||
|
export default function AssetAmount(props: Props) {
|
||||||
|
return (
|
||||||
|
<FormattedNumber
|
||||||
|
amount={props.amount}
|
||||||
|
options={{
|
||||||
|
decimals: props.asset.decimals,
|
||||||
|
maxDecimals: props.asset.decimals,
|
||||||
|
suffix: props.asset.symbol,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
@ -13,7 +13,7 @@ export default function DropDownButton(props: Props) {
|
|||||||
const [isOpen, toggleIsOpen] = useToggle(false)
|
const [isOpen, toggleIsOpen] = useToggle(false)
|
||||||
return (
|
return (
|
||||||
<Tooltip
|
<Tooltip
|
||||||
content={<DropDown {...props} />}
|
content={<DropDown closeMenu={() => toggleIsOpen(false)} {...props} />}
|
||||||
type='info'
|
type='info'
|
||||||
placement='bottom'
|
placement='bottom'
|
||||||
contentClassName='!bg-white/10 border border-white/20 backdrop-blur-xl !p-0'
|
contentClassName='!bg-white/10 border border-white/20 backdrop-blur-xl !p-0'
|
||||||
@ -34,26 +34,35 @@ export default function DropDownButton(props: Props) {
|
|||||||
|
|
||||||
interface DropDownProps {
|
interface DropDownProps {
|
||||||
items: DropDownItem[]
|
items: DropDownItem[]
|
||||||
|
closeMenu: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropDown(props: DropDownProps) {
|
function DropDown(props: DropDownProps) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{props.items.map((item) => (
|
{props.items.map((item) => (
|
||||||
<DropDownItem key={item.text} {...item} />
|
<DropDownItem key={item.text} item={item} closeMenu={props.closeMenu} />
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function DropDownItem(props: DropDownItem) {
|
interface DropDownItemProps {
|
||||||
|
closeMenu: () => void
|
||||||
|
item: DropDownItem
|
||||||
|
}
|
||||||
|
|
||||||
|
function DropDownItem(props: DropDownItemProps) {
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
onClick={props.onClick}
|
onClick={() => {
|
||||||
|
props.item.onClick()
|
||||||
|
props.closeMenu()
|
||||||
|
}}
|
||||||
className=' px-4 py-3 flex gap-2 items-center hover:bg-white/5 w-full [&:not(:last-child)]:border-b border-white/10'
|
className=' px-4 py-3 flex gap-2 items-center hover:bg-white/5 w-full [&:not(:last-child)]:border-b border-white/10'
|
||||||
>
|
>
|
||||||
<div className='flex justify-center w-5 h-5'>{props.icon}</div>
|
<div className='flex justify-center w-5 h-5'>{props.item.icon}</div>
|
||||||
<Text size='sm'>{props.text}</Text>
|
<Text size='sm'>{props.item.text}</Text>
|
||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,3 @@
|
|||||||
<svg viewBox="0 0 13 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path
|
<path d="M3.33337 8.00065H12.6667M12.6667 8.00065L8.00004 3.33398M12.6667 8.00065L8.00004 12.6673" stroke="currentColor" stroke-width="0.666667" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
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>
|
</svg>
|
||||||
|
Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 299 B |
@ -1,10 +1,13 @@
|
|||||||
import React, { useMemo } from 'react'
|
import React, { useMemo } from 'react'
|
||||||
|
import { useSearchParams } from 'react-router-dom'
|
||||||
|
|
||||||
import DropDownButton from 'components/Button/DropDownButton'
|
import DropDownButton from 'components/Button/DropDownButton'
|
||||||
import { Cross, Edit } from 'components/Icons'
|
import { Cross, Edit } from 'components/Icons'
|
||||||
import { PerpPositionRow } from 'components/Perps/BalancesTable/usePerpsBalancesData'
|
import { PerpPositionRow } from 'components/Perps/BalancesTable/usePerpsBalancesData'
|
||||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
|
import { SearchParams } from 'types/enums/searchParams'
|
||||||
|
import { getSearchParamsObject } from 'utils/route'
|
||||||
|
|
||||||
export const MANAGE_META = { id: 'manage', header: 'Manage' }
|
export const MANAGE_META = { id: 'manage', header: 'Manage' }
|
||||||
|
|
||||||
@ -14,6 +17,7 @@ interface Props {
|
|||||||
|
|
||||||
export default function Manage(props: Props) {
|
export default function Manage(props: Props) {
|
||||||
const currentAccount = useCurrentAccount()
|
const currentAccount = useCurrentAccount()
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams()
|
||||||
|
|
||||||
const closePerpPosition = useStore((s) => s.closePerpPosition)
|
const closePerpPosition = useStore((s) => s.closePerpPosition)
|
||||||
const ITEMS: DropDownItem[] = useMemo(
|
const ITEMS: DropDownItem[] = useMemo(
|
||||||
@ -21,7 +25,14 @@ export default function Manage(props: Props) {
|
|||||||
{
|
{
|
||||||
icon: <Edit />,
|
icon: <Edit />,
|
||||||
text: 'Edit Position Size',
|
text: 'Edit Position Size',
|
||||||
onClick: () => {},
|
onClick: () => {
|
||||||
|
const params = getSearchParamsObject(searchParams)
|
||||||
|
setSearchParams({
|
||||||
|
...params,
|
||||||
|
[SearchParams.PERPS_MARKET]: props.perpPosition.asset.denom,
|
||||||
|
[SearchParams.PERPS_MANAGE]: 'true',
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: <Cross width={16} />,
|
icon: <Cross width={16} />,
|
||||||
@ -35,7 +46,13 @@ export default function Manage(props: Props) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[closePerpPosition, currentAccount, props.perpPosition.asset.denom],
|
[
|
||||||
|
closePerpPosition,
|
||||||
|
currentAccount,
|
||||||
|
props.perpPosition.asset.denom,
|
||||||
|
searchParams,
|
||||||
|
setSearchParams,
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -14,7 +14,7 @@ export default function TradeDirection(props: Props) {
|
|||||||
<Text
|
<Text
|
||||||
size='xs'
|
size='xs'
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'capitalize px-1 py-0.5 rounded-sm inline',
|
'capitalize px-1 py-0.5 rounded-sm inline-block',
|
||||||
tradeDirection === 'short' && 'text-error bg-error/20',
|
tradeDirection === 'short' && 'text-error bg-error/20',
|
||||||
tradeDirection === 'long' && 'text-success bg-success/20',
|
tradeDirection === 'long' && 'text-success bg-success/20',
|
||||||
)}
|
)}
|
||||||
|
19
src/components/Perps/Module/OpeningFee.tsx
Normal file
19
src/components/Perps/Module/OpeningFee.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
|
||||||
|
import { CircularProgress } from 'components/CircularProgress'
|
||||||
|
import DisplayCurrency from 'components/DisplayCurrency'
|
||||||
|
import useOpeningFee from 'hooks/perps/useOpeningFee'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
denom: string
|
||||||
|
amount: BigNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function OpeningFee(props: Props) {
|
||||||
|
const { data: openingFee, isLoading } = useOpeningFee(props.denom, props.amount)
|
||||||
|
|
||||||
|
if (isLoading) return <CircularProgress className='h-full' size={12} />
|
||||||
|
if (props.amount.isZero() || !openingFee) return '-'
|
||||||
|
|
||||||
|
return <DisplayCurrency coin={openingFee} />
|
||||||
|
}
|
74
src/components/Perps/Module/PerpsManageModule/index.tsx
Normal file
74
src/components/Perps/Module/PerpsManageModule/index.tsx
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import classNames from 'classnames'
|
||||||
|
import { useState } from 'react'
|
||||||
|
|
||||||
|
import { Cross } from 'components/Icons'
|
||||||
|
import { LeverageButtons } from 'components/Perps/Module/LeverageButtons'
|
||||||
|
import { Or } from 'components/Perps/Module/Or'
|
||||||
|
import usePerpsManageModule from 'components/Perps/Module/PerpsManageModule/usePerpsManageModule'
|
||||||
|
import PerpsSummary from 'components/Perps/Module/Summary'
|
||||||
|
import RangeInput from 'components/RangeInput'
|
||||||
|
import { Spacer } from 'components/Spacer'
|
||||||
|
import Text from 'components/Text'
|
||||||
|
import AssetAmountInput from 'components/Trade/TradeModule/SwapForm/AssetAmountInput'
|
||||||
|
import { TradeDirectionSelector } from 'components/TradeDirectionSelector'
|
||||||
|
import { BN } from 'utils/helpers'
|
||||||
|
|
||||||
|
export function PerpsManageModule() {
|
||||||
|
const [tradeDirection, setTradeDirection] = useState<TradeDirection | null>(null)
|
||||||
|
const [amount, setAmount] = useState<BigNumber | null>(null)
|
||||||
|
|
||||||
|
const {
|
||||||
|
closeManagePerpModule,
|
||||||
|
previousAmount,
|
||||||
|
previousTradeDirection,
|
||||||
|
previousLeverage,
|
||||||
|
leverage,
|
||||||
|
asset,
|
||||||
|
} = usePerpsManageModule(amount)
|
||||||
|
|
||||||
|
if (!asset) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames(
|
||||||
|
'px-4 gap-5 flex flex-col h-full pt-6 pb-4 w-full bg-white/5 absolute rounded-base isolate',
|
||||||
|
'before:content-[" "] before:absolute before:inset-0 before:-z-1 before:rounded-base before:p-[1px] before:border-glas',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div className='flex justify-between mb-3'>
|
||||||
|
<Text>Manage Position</Text>
|
||||||
|
<button onClick={closeManagePerpModule} className='mr-1.5'>
|
||||||
|
<Cross width={16} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<TradeDirectionSelector
|
||||||
|
direction={tradeDirection ?? previousTradeDirection}
|
||||||
|
onChangeDirection={setTradeDirection}
|
||||||
|
/>
|
||||||
|
<AssetAmountInput
|
||||||
|
label='Amount'
|
||||||
|
max={BN(100000)} // TODO: Implement max calculation
|
||||||
|
amount={amount ?? previousAmount}
|
||||||
|
setAmount={setAmount}
|
||||||
|
asset={asset}
|
||||||
|
maxButtonLabel='Max:'
|
||||||
|
disabled={false}
|
||||||
|
/>
|
||||||
|
<Or />
|
||||||
|
<Text size='sm'>Position Leverage</Text>
|
||||||
|
<RangeInput max={0} value={0} onChange={() => {}} />
|
||||||
|
<LeverageButtons />
|
||||||
|
<Spacer />
|
||||||
|
<PerpsSummary
|
||||||
|
changeTradeDirection
|
||||||
|
amount={amount ?? previousAmount}
|
||||||
|
tradeDirection={tradeDirection ?? previousTradeDirection}
|
||||||
|
asset={asset}
|
||||||
|
leverage={leverage}
|
||||||
|
previousAmount={previousAmount}
|
||||||
|
previousTradeDirection={previousTradeDirection}
|
||||||
|
previousLeverage={previousLeverage}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
import { useCallback, useMemo } from 'react'
|
||||||
|
import { useSearchParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { BN_ZERO } from 'constants/math'
|
||||||
|
import useAllAssets from 'hooks/assets/useAllAssets'
|
||||||
|
import usePerpPosition from 'hooks/perps/usePerpPosition'
|
||||||
|
import usePerpsAsset from 'hooks/perps/usePerpsAsset'
|
||||||
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
|
import usePrice from 'hooks/usePrice'
|
||||||
|
import usePrices from 'hooks/usePrices'
|
||||||
|
import { SearchParams } from 'types/enums/searchParams'
|
||||||
|
import { getAccountNetValue } from 'utils/accounts'
|
||||||
|
import { demagnify } from 'utils/formatters'
|
||||||
|
import { getSearchParamsObject } from 'utils/route'
|
||||||
|
|
||||||
|
export default function usePerpsManageModule(amount: BigNumber | null) {
|
||||||
|
const { perpsAsset } = usePerpsAsset()
|
||||||
|
const [searchParams, setSearchParams] = useSearchParams()
|
||||||
|
const perpPosition = usePerpPosition(perpsAsset.denom)
|
||||||
|
const { data: prices } = usePrices()
|
||||||
|
const assets = useAllAssets()
|
||||||
|
const account = useCurrentAccount()
|
||||||
|
const price = usePrice(perpsAsset.denom)
|
||||||
|
|
||||||
|
const accountNetValue = useMemo(() => {
|
||||||
|
if (!account || !prices || !assets) return BN_ZERO
|
||||||
|
return getAccountNetValue(account, prices, assets)
|
||||||
|
}, [account, assets, prices])
|
||||||
|
|
||||||
|
const closeManagePerpModule = useCallback(() => {
|
||||||
|
const params = getSearchParamsObject(searchParams)
|
||||||
|
delete params[SearchParams.PERPS_MANAGE]
|
||||||
|
setSearchParams({
|
||||||
|
...params,
|
||||||
|
})
|
||||||
|
}, [searchParams, setSearchParams])
|
||||||
|
|
||||||
|
const previousAmount = useMemo(() => perpPosition?.size ?? BN_ZERO, [perpPosition?.size])
|
||||||
|
const previousTradeDirection = useMemo(
|
||||||
|
() => perpPosition?.tradeDirection || 'long',
|
||||||
|
[perpPosition?.tradeDirection],
|
||||||
|
)
|
||||||
|
|
||||||
|
const previousLeverage = useMemo(
|
||||||
|
() =>
|
||||||
|
price.times(demagnify(previousAmount, perpsAsset)).div(accountNetValue).plus(1).toNumber(),
|
||||||
|
[accountNetValue, perpsAsset, previousAmount, price],
|
||||||
|
)
|
||||||
|
|
||||||
|
const leverage = useMemo(
|
||||||
|
() =>
|
||||||
|
price
|
||||||
|
.times(demagnify(amount ?? BN_ZERO, perpsAsset))
|
||||||
|
.div(accountNetValue)
|
||||||
|
.plus(1)
|
||||||
|
.toNumber(),
|
||||||
|
[accountNetValue, amount, perpsAsset, price],
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
closeManagePerpModule,
|
||||||
|
previousAmount,
|
||||||
|
previousTradeDirection,
|
||||||
|
previousLeverage,
|
||||||
|
leverage,
|
||||||
|
asset: perpsAsset,
|
||||||
|
}
|
||||||
|
}
|
@ -20,6 +20,7 @@ export function PerpsModule() {
|
|||||||
const [selectedOrderType, setSelectedOrderType] = useState<AvailableOrderType>('Market')
|
const [selectedOrderType, setSelectedOrderType] = useState<AvailableOrderType>('Market')
|
||||||
const [tradeDirection, setTradeDirection] = useState<TradeDirection>('long')
|
const [tradeDirection, setTradeDirection] = useState<TradeDirection>('long')
|
||||||
const { perpsAsset } = usePerpsAsset()
|
const { perpsAsset } = usePerpsAsset()
|
||||||
|
const [leverage, setLeverage] = useState<number>(1)
|
||||||
|
|
||||||
const [amount, setAmount] = useState<BigNumber>(BN_ZERO)
|
const [amount, setAmount] = useState<BigNumber>(BN_ZERO)
|
||||||
|
|
||||||
@ -48,7 +49,12 @@ export function PerpsModule() {
|
|||||||
<RangeInput max={0} value={0} onChange={() => {}} />
|
<RangeInput max={0} value={0} onChange={() => {}} />
|
||||||
<LeverageButtons />
|
<LeverageButtons />
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<PerpsSummary amount={amount} tradeDirection={tradeDirection} asset={perpsAsset} />
|
<PerpsSummary
|
||||||
|
amount={amount}
|
||||||
|
tradeDirection={tradeDirection}
|
||||||
|
asset={perpsAsset}
|
||||||
|
leverage={leverage}
|
||||||
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,27 @@
|
|||||||
import { useCallback } from 'react'
|
import classNames from 'classnames'
|
||||||
|
import { useCallback, useMemo } from 'react'
|
||||||
|
|
||||||
|
import AssetAmount from 'components/Asset/AssetAmount'
|
||||||
import ActionButton from 'components/Button/ActionButton'
|
import ActionButton from 'components/Button/ActionButton'
|
||||||
|
import { ArrowRight } from 'components/Icons'
|
||||||
|
import TradeDirection from 'components/Perps/BalancesTable/Columns/TradeDirection'
|
||||||
|
import OpeningFee from 'components/Perps/Module/OpeningFee'
|
||||||
import SummaryLine from 'components/SummaryLine'
|
import SummaryLine from 'components/SummaryLine'
|
||||||
import Text from 'components/Text'
|
import Text from 'components/Text'
|
||||||
import useCurrentAccount from 'hooks/useCurrentAccount'
|
import useCurrentAccount from 'hooks/useCurrentAccount'
|
||||||
import useStore from 'store'
|
import useStore from 'store'
|
||||||
import { BNCoin } from 'types/classes/BNCoin'
|
import { BNCoin } from 'types/classes/BNCoin'
|
||||||
|
import { formatLeverage } from 'utils/formatters'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
|
leverage: number
|
||||||
amount: BigNumber
|
amount: BigNumber
|
||||||
tradeDirection: TradeDirection
|
tradeDirection: TradeDirection
|
||||||
asset: Asset
|
asset: Asset
|
||||||
|
changeTradeDirection?: boolean
|
||||||
|
previousAmount?: BigNumber
|
||||||
|
previousTradeDirection?: 'long' | 'short'
|
||||||
|
previousLeverage?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function PerpsSummary(props: Props) {
|
export default function PerpsSummary(props: Props) {
|
||||||
@ -28,20 +39,85 @@ export default function PerpsSummary(props: Props) {
|
|||||||
})
|
})
|
||||||
}, [currentAccount, openPerpPosition, props.amount, props.asset.denom, props.tradeDirection])
|
}, [currentAccount, openPerpPosition, props.amount, props.asset.denom, props.tradeDirection])
|
||||||
|
|
||||||
|
const disabled = useMemo(
|
||||||
|
() =>
|
||||||
|
(props.previousAmount && props.previousAmount.isEqualTo(props.amount)) ||
|
||||||
|
props.amount.isZero(),
|
||||||
|
[props.amount, props.previousAmount],
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='border border-white/10 rounded-sm bg-white/5'>
|
<div className='border border-white/10 rounded-sm bg-white/5'>
|
||||||
|
<ManageSummary {...props} />
|
||||||
<div className='py-4 px-3 flex flex-col gap-1'>
|
<div className='py-4 px-3 flex flex-col gap-1'>
|
||||||
<Text size='xs' className='font-bold mb-2'>
|
<Text size='xs' className='font-bold mb-2'>
|
||||||
Summary
|
Summary
|
||||||
</Text>
|
</Text>
|
||||||
<SummaryLine label='Expected Price'>Something</SummaryLine>
|
<SummaryLine label='Expected Price'>-</SummaryLine>
|
||||||
<SummaryLine label='Fees'>Something</SummaryLine>
|
<SummaryLine label='Fees'>
|
||||||
<SummaryLine label='Total'>Something</SummaryLine>
|
<OpeningFee denom={props.asset.denom} amount={props.amount} />
|
||||||
|
</SummaryLine>
|
||||||
|
<SummaryLine label='Total'>-</SummaryLine>
|
||||||
</div>
|
</div>
|
||||||
<ActionButton onClick={onConfirm} className='w-full py-2.5'>
|
<ActionButton onClick={onConfirm} disabled={disabled} className='w-full py-2.5'>
|
||||||
<span className='capitalize mr-1'>{props.tradeDirection}</span>
|
<span className='capitalize mr-1'>{props.tradeDirection}</span>
|
||||||
{props.asset.symbol}
|
{props.asset.symbol}
|
||||||
</ActionButton>
|
</ActionButton>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ManageSummary(props: Props) {
|
||||||
|
const showTradeDirection =
|
||||||
|
props.previousTradeDirection && props.previousTradeDirection !== props.tradeDirection
|
||||||
|
const showAmount =
|
||||||
|
props.previousAmount && props.amount && !props.previousAmount.isEqualTo(props.amount)
|
||||||
|
const showLeverage =
|
||||||
|
props.previousLeverage &&
|
||||||
|
props.leverage &&
|
||||||
|
props.previousLeverage.toFixed(2) !== props.leverage.toFixed(2)
|
||||||
|
|
||||||
|
if (!showTradeDirection && !showLeverage && !showAmount) return null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='pt-4 px-3 flex flex-col gap-1'>
|
||||||
|
<Text size='xs' className='font-bold mb-2'>
|
||||||
|
Your new position
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{showTradeDirection && props.previousTradeDirection && (
|
||||||
|
<SummaryLine label='Side' contentClassName='flex gap-1'>
|
||||||
|
<TradeDirection tradeDirection={props.previousTradeDirection} />
|
||||||
|
<ArrowRight width={16} />
|
||||||
|
<TradeDirection tradeDirection={props.tradeDirection} />
|
||||||
|
</SummaryLine>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showAmount && props.previousAmount && (
|
||||||
|
<SummaryLine label='Size' contentClassName='flex gap-1'>
|
||||||
|
<AssetAmount asset={props.asset} amount={props.previousAmount.toNumber()} />
|
||||||
|
<ArrowRight
|
||||||
|
width={16}
|
||||||
|
className={classNames(
|
||||||
|
props.previousAmount.isGreaterThan(props.amount) ? 'text-error' : 'text-success',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<AssetAmount asset={props.asset} amount={props.amount.toNumber()} />
|
||||||
|
</SummaryLine>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showLeverage && props.previousLeverage && (
|
||||||
|
<SummaryLine label='Leverage' contentClassName='flex gap-1'>
|
||||||
|
<span>{formatLeverage(props.previousLeverage)}</span>
|
||||||
|
<ArrowRight
|
||||||
|
width={16}
|
||||||
|
className={classNames(
|
||||||
|
props.leverage > props.previousLeverage ? 'text-error' : 'text-success',
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<span>{formatLeverage(props.leverage)}</span>
|
||||||
|
</SummaryLine>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -50,6 +50,8 @@ export function PerpsInfo() {
|
|||||||
]
|
]
|
||||||
}, [assetPrice, market])
|
}, [assetPrice, market])
|
||||||
|
|
||||||
|
if (!market) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card contentClassName='bg-white/10 py-3.5 px-4'>
|
<Card contentClassName='bg-white/10 py-3.5 px-4'>
|
||||||
<div className='flex gap-4 items-center'>
|
<div className='flex gap-4 items-center'>
|
||||||
|
@ -6,13 +6,14 @@ const infoLineClasses = 'flex flex-row justify-between flex-1 mb-1 text-xs text-
|
|||||||
interface SummaryLineProps {
|
interface SummaryLineProps {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
className?: string
|
className?: string
|
||||||
|
contentClassName?: string
|
||||||
label: string
|
label: string
|
||||||
}
|
}
|
||||||
export default function SummaryLine(props: SummaryLineProps) {
|
export default function SummaryLine(props: SummaryLineProps) {
|
||||||
return (
|
return (
|
||||||
<div className={classNames(infoLineClasses, props.className)}>
|
<div className={classNames(infoLineClasses, props.className)}>
|
||||||
<span className='opacity-40'>{props.label}</span>
|
<span className='opacity-40'>{props.label}</span>
|
||||||
<span>{props.children}</span>
|
<span className={props.contentClassName}>{props.children}</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
16
src/hooks/perps/useOpeningFee.ts
Normal file
16
src/hooks/perps/useOpeningFee.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import BigNumber from 'bignumber.js'
|
||||||
|
import useSWR from 'swr'
|
||||||
|
|
||||||
|
import getOpeningFee from 'api/perps/getOpeningFee'
|
||||||
|
import useChainConfig from 'hooks/useChainConfig'
|
||||||
|
import useDebounce from 'hooks/useDebounce'
|
||||||
|
|
||||||
|
export default function useOpeningFee(denom: string, amount: BigNumber) {
|
||||||
|
const chainConfig = useChainConfig()
|
||||||
|
const debouncedAmount = useDebounce<string>(amount.toString(), 500)
|
||||||
|
const enabled = !amount.isZero()
|
||||||
|
|
||||||
|
return useSWR(enabled && `${chainConfig.id}/perps/${denom}/openingFee/${debouncedAmount}`, () =>
|
||||||
|
getOpeningFee(chainConfig, denom, amount.toString()),
|
||||||
|
)
|
||||||
|
}
|
11
src/hooks/perps/usePerpPosition.ts
Normal file
11
src/hooks/perps/usePerpPosition.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
|
import { byDenom } from 'utils/array'
|
||||||
|
|
||||||
|
import useCurrentAccount from '../useCurrentAccount'
|
||||||
|
|
||||||
|
export default function usePerpPosition(denom: string): PerpsPosition | undefined {
|
||||||
|
const account = useCurrentAccount()
|
||||||
|
|
||||||
|
return useMemo(() => account?.perps.find(byDenom(denom)), [account?.perps, denom])
|
||||||
|
}
|
@ -6,10 +6,11 @@ import { LocalStorageKeys } from 'constants/localStorageKeys'
|
|||||||
import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets'
|
import usePerpsEnabledAssets from 'hooks/assets/usePerpsEnabledAssets'
|
||||||
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
|
import useLocalStorage from 'hooks/localStorage/useLocalStorage'
|
||||||
import useChainConfig from 'hooks/useChainConfig'
|
import useChainConfig from 'hooks/useChainConfig'
|
||||||
|
import { getSearchParamsObject } from 'utils/route'
|
||||||
|
|
||||||
export default function usePerpsAsset() {
|
export default function usePerpsAsset() {
|
||||||
const chainConfig = useChainConfig()
|
const chainConfig = useChainConfig()
|
||||||
const [searchParams] = useSearchParams()
|
const [searchParams, setSearchParams] = useSearchParams()
|
||||||
const perpsAssets = usePerpsEnabledAssets()
|
const perpsAssets = usePerpsEnabledAssets()
|
||||||
const perpsAssetInParams = searchParams.get('perpsMarket')
|
const perpsAssetInParams = searchParams.get('perpsMarket')
|
||||||
const [perpsAssetInLocalStorage, setPerpsAssetInLocalStorage] = useLocalStorage<
|
const [perpsAssetInLocalStorage, setPerpsAssetInLocalStorage] = useLocalStorage<
|
||||||
@ -18,9 +19,12 @@ export default function usePerpsAsset() {
|
|||||||
|
|
||||||
const updatePerpsAsset = useCallback(
|
const updatePerpsAsset = useCallback(
|
||||||
(denom: string) => {
|
(denom: string) => {
|
||||||
|
const params = getSearchParamsObject(searchParams)
|
||||||
|
params.perpsMarket = denom
|
||||||
|
setSearchParams(params)
|
||||||
setPerpsAssetInLocalStorage(denom)
|
setPerpsAssetInLocalStorage(denom)
|
||||||
},
|
},
|
||||||
[setPerpsAssetInLocalStorage],
|
[searchParams, setPerpsAssetInLocalStorage, setSearchParams],
|
||||||
)
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { useMemo } from 'react'
|
||||||
|
|
||||||
import useAccounts from 'hooks/accounts/useAccounts'
|
import useAccounts from 'hooks/accounts/useAccounts'
|
||||||
import useAccountId from 'hooks/useAccountId'
|
import useAccountId from 'hooks/useAccountId'
|
||||||
|
|
||||||
@ -5,5 +7,5 @@ export default function useCurrentAccount(): Account | undefined {
|
|||||||
const accountId = useAccountId()
|
const accountId = useAccountId()
|
||||||
const { data: accounts } = useAccounts('default', undefined, false)
|
const { data: accounts } = useAccounts('default', undefined, false)
|
||||||
|
|
||||||
return accounts?.find((account) => account.id === accountId)
|
return useMemo(() => accounts?.find((account) => account.id === accountId), [accountId, accounts])
|
||||||
}
|
}
|
||||||
|
17
src/hooks/useDebounce.ts
Normal file
17
src/hooks/useDebounce.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { useEffect, useState } from 'react'
|
||||||
|
|
||||||
|
export default function useDebounce<T>(value: T, delay: number) {
|
||||||
|
const [debouncedValue, setDebouncedValue] = useState(value)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const handler = setTimeout(() => {
|
||||||
|
setDebouncedValue(value)
|
||||||
|
}, delay)
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(handler)
|
||||||
|
}
|
||||||
|
}, [value, delay])
|
||||||
|
|
||||||
|
return debouncedValue
|
||||||
|
}
|
@ -1,14 +1,22 @@
|
|||||||
|
import { useSearchParams } from 'react-router-dom'
|
||||||
|
|
||||||
|
import { PerpsManageModule } from 'components/Perps/Module/PerpsManageModule'
|
||||||
import { PerpsModule } from 'components/Perps/Module/PerpsModule'
|
import { PerpsModule } from 'components/Perps/Module/PerpsModule'
|
||||||
import { PerpsChart } from 'components/Perps/PerpsChart'
|
import { PerpsChart } from 'components/Perps/PerpsChart'
|
||||||
import { PerpsInfo } from 'components/Perps/PerpsInfo'
|
import { PerpsInfo } from 'components/Perps/PerpsInfo'
|
||||||
import { PerpsPositions } from 'components/Perps/PerpsPositions'
|
import { PerpsPositions } from 'components/Perps/PerpsPositions'
|
||||||
|
import { SearchParams } from 'types/enums/searchParams'
|
||||||
|
|
||||||
export default function PerpsPage() {
|
export default function PerpsPage() {
|
||||||
|
const [searchParams] = useSearchParams()
|
||||||
|
|
||||||
|
const isManagingPosition = searchParams.get(SearchParams.PERPS_MANAGE) === 'true'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='grid grid-cols-[auto_376px] grid-rows-[min-content_auto_auto] w-full gap-4'>
|
<div className='grid grid-cols-[auto_376px] grid-rows-[min-content_auto_auto] w-full gap-4'>
|
||||||
<PerpsInfo />
|
<PerpsInfo />
|
||||||
<div className='h-full w-[376px] row-span-3'>
|
<div className='h-full w-[376px] row-span-3 relative'>
|
||||||
<PerpsModule />
|
{isManagingPosition ? <PerpsManageModule /> : <PerpsModule />}
|
||||||
</div>
|
</div>
|
||||||
<PerpsChart />
|
<PerpsChart />
|
||||||
<PerpsPositions />
|
<PerpsPositions />
|
||||||
|
5
src/types/enums/searchParams.ts
Normal file
5
src/types/enums/searchParams.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export enum SearchParams {
|
||||||
|
ACCOUNT_ID = 'accountId',
|
||||||
|
PERPS_MARKET = 'perpsMarket',
|
||||||
|
PERPS_MANAGE = 'perpsManage',
|
||||||
|
}
|
@ -1,275 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
/**
|
|
||||||
* This file was automatically generated by @cosmwasm/ts-codegen@0.33.0.
|
|
||||||
* DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
|
|
||||||
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { MsgExecuteContractEncodeObject } from '@cosmjs/cosmwasm-stargate'
|
|
||||||
import { MsgExecuteContract } from 'cosmjs-types/cosmwasm/wasm/v1/tx'
|
|
||||||
import { toUtf8 } from '@cosmjs/encoding'
|
|
||||||
import {
|
|
||||||
HealthContractBaseForString,
|
|
||||||
IncentivesUnchecked,
|
|
||||||
Decimal,
|
|
||||||
Uint128,
|
|
||||||
OracleBaseForString,
|
|
||||||
ParamsBaseForString,
|
|
||||||
RedBankUnchecked,
|
|
||||||
SwapperBaseForString,
|
|
||||||
ZapperBaseForString,
|
|
||||||
InstantiateMsg,
|
|
||||||
ExecuteMsg,
|
|
||||||
AccountKind,
|
|
||||||
Action,
|
|
||||||
ActionAmount,
|
|
||||||
LiquidateRequestForVaultBaseForString,
|
|
||||||
VaultPositionType,
|
|
||||||
AccountNftBaseForString,
|
|
||||||
OwnerUpdate,
|
|
||||||
CallbackMsg,
|
|
||||||
Addr,
|
|
||||||
HealthState,
|
|
||||||
LiquidateRequestForVaultBaseForAddr,
|
|
||||||
ChangeExpected,
|
|
||||||
Coin,
|
|
||||||
ActionCoin,
|
|
||||||
VaultBaseForString,
|
|
||||||
ConfigUpdates,
|
|
||||||
NftConfigUpdates,
|
|
||||||
VaultBaseForAddr,
|
|
||||||
QueryMsg,
|
|
||||||
VaultPositionAmount,
|
|
||||||
VaultAmount,
|
|
||||||
VaultAmount1,
|
|
||||||
UnlockingPositions,
|
|
||||||
VaultPosition,
|
|
||||||
LockingVaultAmount,
|
|
||||||
VaultUnlockingPosition,
|
|
||||||
ArrayOfAccount,
|
|
||||||
Account,
|
|
||||||
ArrayOfCoinBalanceResponseItem,
|
|
||||||
CoinBalanceResponseItem,
|
|
||||||
ArrayOfSharesResponseItem,
|
|
||||||
SharesResponseItem,
|
|
||||||
ArrayOfDebtShares,
|
|
||||||
DebtShares,
|
|
||||||
ArrayOfVaultPositionResponseItem,
|
|
||||||
VaultPositionResponseItem,
|
|
||||||
ConfigResponse,
|
|
||||||
OwnerResponse,
|
|
||||||
RewardsCollector,
|
|
||||||
ArrayOfCoin,
|
|
||||||
Positions,
|
|
||||||
DebtAmount,
|
|
||||||
VaultPositionValue,
|
|
||||||
CoinValue,
|
|
||||||
VaultUtilizationResponse,
|
|
||||||
} from './MarsCreditManager.types'
|
|
||||||
export interface MarsCreditManagerMessage {
|
|
||||||
contractAddress: string
|
|
||||||
sender: string
|
|
||||||
createCreditAccount: (_funds?: Coin[]) => MsgExecuteContractEncodeObject
|
|
||||||
updateCreditAccount: (
|
|
||||||
{
|
|
||||||
accountId,
|
|
||||||
actions,
|
|
||||||
}: {
|
|
||||||
accountId: string
|
|
||||||
actions: Action[]
|
|
||||||
},
|
|
||||||
_funds?: Coin[],
|
|
||||||
) => MsgExecuteContractEncodeObject
|
|
||||||
repayFromWallet: (
|
|
||||||
{
|
|
||||||
accountId,
|
|
||||||
}: {
|
|
||||||
accountId: string
|
|
||||||
},
|
|
||||||
_funds?: Coin[],
|
|
||||||
) => MsgExecuteContractEncodeObject
|
|
||||||
updateConfig: (
|
|
||||||
{
|
|
||||||
updates,
|
|
||||||
}: {
|
|
||||||
updates: ConfigUpdates
|
|
||||||
},
|
|
||||||
_funds?: Coin[],
|
|
||||||
) => MsgExecuteContractEncodeObject
|
|
||||||
updateOwner: (ownerUpdate: OwnerUpdate, _funds?: Coin[]) => MsgExecuteContractEncodeObject
|
|
||||||
updateNftConfig: (
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
ownership,
|
|
||||||
}: {
|
|
||||||
config?: NftConfigUpdates
|
|
||||||
ownership?: Action
|
|
||||||
},
|
|
||||||
_funds?: Coin[],
|
|
||||||
) => MsgExecuteContractEncodeObject
|
|
||||||
callback: (callbackMsg: CallbackMsg, _funds?: Coin[]) => MsgExecuteContractEncodeObject
|
|
||||||
}
|
|
||||||
export class MarsCreditManagerMessageComposer implements MarsCreditManagerMessage {
|
|
||||||
sender: string
|
|
||||||
contractAddress: string
|
|
||||||
|
|
||||||
constructor(sender: string, contractAddress: string) {
|
|
||||||
this.sender = sender
|
|
||||||
this.contractAddress = contractAddress
|
|
||||||
this.createCreditAccount = this.createCreditAccount.bind(this)
|
|
||||||
this.updateCreditAccount = this.updateCreditAccount.bind(this)
|
|
||||||
this.repayFromWallet = this.repayFromWallet.bind(this)
|
|
||||||
this.updateConfig = this.updateConfig.bind(this)
|
|
||||||
this.updateOwner = this.updateOwner.bind(this)
|
|
||||||
this.updateNftConfig = this.updateNftConfig.bind(this)
|
|
||||||
this.callback = this.callback.bind(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
createCreditAccount = (_funds?: Coin[]): MsgExecuteContractEncodeObject => {
|
|
||||||
return {
|
|
||||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
|
||||||
value: MsgExecuteContract.fromPartial({
|
|
||||||
sender: this.sender,
|
|
||||||
contract: this.contractAddress,
|
|
||||||
msg: toUtf8(
|
|
||||||
JSON.stringify({
|
|
||||||
create_credit_account: {},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
funds: _funds,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateCreditAccount = (
|
|
||||||
{
|
|
||||||
accountId,
|
|
||||||
actions,
|
|
||||||
}: {
|
|
||||||
accountId: string
|
|
||||||
actions: Action[]
|
|
||||||
},
|
|
||||||
_funds?: Coin[],
|
|
||||||
): MsgExecuteContractEncodeObject => {
|
|
||||||
return {
|
|
||||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
|
||||||
value: MsgExecuteContract.fromPartial({
|
|
||||||
sender: this.sender,
|
|
||||||
contract: this.contractAddress,
|
|
||||||
msg: toUtf8(
|
|
||||||
JSON.stringify({
|
|
||||||
update_credit_account: {
|
|
||||||
account_id: accountId,
|
|
||||||
actions,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
funds: _funds,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
repayFromWallet = (
|
|
||||||
{
|
|
||||||
accountId,
|
|
||||||
}: {
|
|
||||||
accountId: string
|
|
||||||
},
|
|
||||||
_funds?: Coin[],
|
|
||||||
): MsgExecuteContractEncodeObject => {
|
|
||||||
return {
|
|
||||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
|
||||||
value: MsgExecuteContract.fromPartial({
|
|
||||||
sender: this.sender,
|
|
||||||
contract: this.contractAddress,
|
|
||||||
msg: toUtf8(
|
|
||||||
JSON.stringify({
|
|
||||||
repay_from_wallet: {
|
|
||||||
account_id: accountId,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
funds: _funds,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateConfig = (
|
|
||||||
{
|
|
||||||
updates,
|
|
||||||
}: {
|
|
||||||
updates: ConfigUpdates
|
|
||||||
},
|
|
||||||
_funds?: Coin[],
|
|
||||||
): MsgExecuteContractEncodeObject => {
|
|
||||||
return {
|
|
||||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
|
||||||
value: MsgExecuteContract.fromPartial({
|
|
||||||
sender: this.sender,
|
|
||||||
contract: this.contractAddress,
|
|
||||||
msg: toUtf8(
|
|
||||||
JSON.stringify({
|
|
||||||
update_config: {
|
|
||||||
updates,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
funds: _funds,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateOwner = (ownerUpdate: OwnerUpdate, _funds?: Coin[]): MsgExecuteContractEncodeObject => {
|
|
||||||
return {
|
|
||||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
|
||||||
value: MsgExecuteContract.fromPartial({
|
|
||||||
sender: this.sender,
|
|
||||||
contract: this.contractAddress,
|
|
||||||
msg: toUtf8(
|
|
||||||
JSON.stringify({
|
|
||||||
update_owner: ownerUpdate,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
funds: _funds,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateNftConfig = (
|
|
||||||
{
|
|
||||||
config,
|
|
||||||
ownership,
|
|
||||||
}: {
|
|
||||||
config?: NftConfigUpdates
|
|
||||||
ownership?: Action
|
|
||||||
},
|
|
||||||
_funds?: Coin[],
|
|
||||||
): MsgExecuteContractEncodeObject => {
|
|
||||||
return {
|
|
||||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
|
||||||
value: MsgExecuteContract.fromPartial({
|
|
||||||
sender: this.sender,
|
|
||||||
contract: this.contractAddress,
|
|
||||||
msg: toUtf8(
|
|
||||||
JSON.stringify({
|
|
||||||
update_nft_config: {
|
|
||||||
config,
|
|
||||||
ownership,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
funds: _funds,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
callback = (callbackMsg: CallbackMsg, _funds?: Coin[]): MsgExecuteContractEncodeObject => {
|
|
||||||
return {
|
|
||||||
typeUrl: '/cosmwasm.wasm.v1.MsgExecuteContract',
|
|
||||||
value: MsgExecuteContract.fromPartial({
|
|
||||||
sender: this.sender,
|
|
||||||
contract: this.contractAddress,
|
|
||||||
msg: toUtf8(
|
|
||||||
JSON.stringify({
|
|
||||||
callback: callbackMsg,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
funds: _funds,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,12 +8,13 @@
|
|||||||
import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from '@cosmjs/cosmwasm-stargate'
|
import { CosmWasmClient, SigningCosmWasmClient, ExecuteResult } from '@cosmjs/cosmwasm-stargate'
|
||||||
import { StdFee } from '@cosmjs/amino'
|
import { StdFee } from '@cosmjs/amino'
|
||||||
import {
|
import {
|
||||||
|
Decimal,
|
||||||
Uint128,
|
Uint128,
|
||||||
OracleBaseForString,
|
OracleBaseForString,
|
||||||
|
ParamsBaseForString,
|
||||||
InstantiateMsg,
|
InstantiateMsg,
|
||||||
ExecuteMsg,
|
ExecuteMsg,
|
||||||
OwnerUpdate,
|
OwnerUpdate,
|
||||||
Decimal,
|
|
||||||
SignedDecimal,
|
SignedDecimal,
|
||||||
QueryMsg,
|
QueryMsg,
|
||||||
ConfigForString,
|
ConfigForString,
|
||||||
@ -22,13 +23,17 @@ import {
|
|||||||
ArrayOfDenomStateResponse,
|
ArrayOfDenomStateResponse,
|
||||||
DepositResponse,
|
DepositResponse,
|
||||||
ArrayOfDepositResponse,
|
ArrayOfDepositResponse,
|
||||||
|
TradingFee,
|
||||||
|
Coin,
|
||||||
OwnerResponse,
|
OwnerResponse,
|
||||||
PerpDenomState,
|
PerpDenomState,
|
||||||
PnlValues,
|
DenomPnlValues,
|
||||||
PnL,
|
PnL,
|
||||||
PositionResponse,
|
PositionResponse,
|
||||||
PerpPosition,
|
PerpPosition,
|
||||||
Coin,
|
PositionPnl,
|
||||||
|
PnlCoins,
|
||||||
|
PnlValues,
|
||||||
ArrayOfPositionResponse,
|
ArrayOfPositionResponse,
|
||||||
PositionsByAccountResponse,
|
PositionsByAccountResponse,
|
||||||
ArrayOfUnlockState,
|
ArrayOfUnlockState,
|
||||||
@ -74,6 +79,7 @@ export interface MarsPerpsReadOnlyInterface {
|
|||||||
}) => Promise<ArrayOfPositionResponse>
|
}) => Promise<ArrayOfPositionResponse>
|
||||||
positionsByAccount: ({ accountId }: { accountId: string }) => Promise<PositionsByAccountResponse>
|
positionsByAccount: ({ accountId }: { accountId: string }) => Promise<PositionsByAccountResponse>
|
||||||
totalPnl: () => Promise<SignedDecimal>
|
totalPnl: () => Promise<SignedDecimal>
|
||||||
|
openingFee: ({ denom, size }: { denom: string; size: SignedDecimal }) => Promise<TradingFee>
|
||||||
}
|
}
|
||||||
export class MarsPerpsQueryClient implements MarsPerpsReadOnlyInterface {
|
export class MarsPerpsQueryClient implements MarsPerpsReadOnlyInterface {
|
||||||
client: CosmWasmClient
|
client: CosmWasmClient
|
||||||
@ -95,6 +101,7 @@ export class MarsPerpsQueryClient implements MarsPerpsReadOnlyInterface {
|
|||||||
this.positions = this.positions.bind(this)
|
this.positions = this.positions.bind(this)
|
||||||
this.positionsByAccount = this.positionsByAccount.bind(this)
|
this.positionsByAccount = this.positionsByAccount.bind(this)
|
||||||
this.totalPnl = this.totalPnl.bind(this)
|
this.totalPnl = this.totalPnl.bind(this)
|
||||||
|
this.openingFee = this.openingFee.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
owner = async (): Promise<OwnerResponse> => {
|
owner = async (): Promise<OwnerResponse> => {
|
||||||
@ -212,6 +219,20 @@ export class MarsPerpsQueryClient implements MarsPerpsReadOnlyInterface {
|
|||||||
total_pnl: {},
|
total_pnl: {},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
openingFee = async ({
|
||||||
|
denom,
|
||||||
|
size,
|
||||||
|
}: {
|
||||||
|
denom: string
|
||||||
|
size: SignedDecimal
|
||||||
|
}): Promise<TradingFee> => {
|
||||||
|
return this.client.queryContractSmart(this.contractAddress, {
|
||||||
|
opening_fee: {
|
||||||
|
denom,
|
||||||
|
size,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
export interface MarsPerpsInterface extends MarsPerpsReadOnlyInterface {
|
export interface MarsPerpsInterface extends MarsPerpsReadOnlyInterface {
|
||||||
contractAddress: string
|
contractAddress: string
|
||||||
|
@ -9,12 +9,13 @@ import { UseQueryOptions, useQuery, useMutation, UseMutationOptions } from '@tan
|
|||||||
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
|
import { ExecuteResult } from '@cosmjs/cosmwasm-stargate'
|
||||||
import { StdFee } from '@cosmjs/amino'
|
import { StdFee } from '@cosmjs/amino'
|
||||||
import {
|
import {
|
||||||
|
Decimal,
|
||||||
Uint128,
|
Uint128,
|
||||||
OracleBaseForString,
|
OracleBaseForString,
|
||||||
|
ParamsBaseForString,
|
||||||
InstantiateMsg,
|
InstantiateMsg,
|
||||||
ExecuteMsg,
|
ExecuteMsg,
|
||||||
OwnerUpdate,
|
OwnerUpdate,
|
||||||
Decimal,
|
|
||||||
SignedDecimal,
|
SignedDecimal,
|
||||||
QueryMsg,
|
QueryMsg,
|
||||||
ConfigForString,
|
ConfigForString,
|
||||||
@ -23,13 +24,17 @@ import {
|
|||||||
ArrayOfDenomStateResponse,
|
ArrayOfDenomStateResponse,
|
||||||
DepositResponse,
|
DepositResponse,
|
||||||
ArrayOfDepositResponse,
|
ArrayOfDepositResponse,
|
||||||
|
TradingFee,
|
||||||
|
Coin,
|
||||||
OwnerResponse,
|
OwnerResponse,
|
||||||
PerpDenomState,
|
PerpDenomState,
|
||||||
PnlValues,
|
DenomPnlValues,
|
||||||
PnL,
|
PnL,
|
||||||
PositionResponse,
|
PositionResponse,
|
||||||
PerpPosition,
|
PerpPosition,
|
||||||
Coin,
|
PositionPnl,
|
||||||
|
PnlCoins,
|
||||||
|
PnlValues,
|
||||||
ArrayOfPositionResponse,
|
ArrayOfPositionResponse,
|
||||||
PositionsByAccountResponse,
|
PositionsByAccountResponse,
|
||||||
ArrayOfUnlockState,
|
ArrayOfUnlockState,
|
||||||
@ -75,6 +80,8 @@ export const marsPerpsQueryKeys = {
|
|||||||
] as const,
|
] as const,
|
||||||
totalPnl: (contractAddress: string | undefined, args?: Record<string, unknown>) =>
|
totalPnl: (contractAddress: string | undefined, args?: Record<string, unknown>) =>
|
||||||
[{ ...marsPerpsQueryKeys.address(contractAddress)[0], method: 'total_pnl', args }] as const,
|
[{ ...marsPerpsQueryKeys.address(contractAddress)[0], method: 'total_pnl', args }] as const,
|
||||||
|
openingFee: (contractAddress: string | undefined, args?: Record<string, unknown>) =>
|
||||||
|
[{ ...marsPerpsQueryKeys.address(contractAddress)[0], method: 'opening_fee', args }] as const,
|
||||||
}
|
}
|
||||||
export interface MarsPerpsReactQuery<TResponse, TData = TResponse> {
|
export interface MarsPerpsReactQuery<TResponse, TData = TResponse> {
|
||||||
client: MarsPerpsQueryClient | undefined
|
client: MarsPerpsQueryClient | undefined
|
||||||
@ -85,6 +92,29 @@ export interface MarsPerpsReactQuery<TResponse, TData = TResponse> {
|
|||||||
initialData?: undefined
|
initialData?: undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
export interface MarsPerpsOpeningFeeQuery<TData> extends MarsPerpsReactQuery<TradingFee, TData> {
|
||||||
|
args: {
|
||||||
|
denom: string
|
||||||
|
size: SignedDecimal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export function useMarsPerpsOpeningFeeQuery<TData = TradingFee>({
|
||||||
|
client,
|
||||||
|
args,
|
||||||
|
options,
|
||||||
|
}: MarsPerpsOpeningFeeQuery<TData>) {
|
||||||
|
return useQuery<TradingFee, Error, TData>(
|
||||||
|
marsPerpsQueryKeys.openingFee(client?.contractAddress, args),
|
||||||
|
() =>
|
||||||
|
client
|
||||||
|
? client.openingFee({
|
||||||
|
denom: args.denom,
|
||||||
|
size: args.size,
|
||||||
|
})
|
||||||
|
: Promise.reject(new Error('Invalid client')),
|
||||||
|
{ ...options, enabled: !!client && (options?.enabled != undefined ? options.enabled : true) },
|
||||||
|
)
|
||||||
|
}
|
||||||
export interface MarsPerpsTotalPnlQuery<TData> extends MarsPerpsReactQuery<SignedDecimal, TData> {}
|
export interface MarsPerpsTotalPnlQuery<TData> extends MarsPerpsReactQuery<SignedDecimal, TData> {}
|
||||||
export function useMarsPerpsTotalPnlQuery<TData = SignedDecimal>({
|
export function useMarsPerpsTotalPnlQuery<TData = SignedDecimal>({
|
||||||
client,
|
client,
|
||||||
|
@ -5,14 +5,20 @@
|
|||||||
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
|
* and run the @cosmwasm/ts-codegen generate command to regenerate this file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
export type Decimal = string
|
||||||
export type Uint128 = string
|
export type Uint128 = string
|
||||||
export type OracleBaseForString = string
|
export type OracleBaseForString = string
|
||||||
|
export type ParamsBaseForString = string
|
||||||
export interface InstantiateMsg {
|
export interface InstantiateMsg {
|
||||||
base_denom: string
|
base_denom: string
|
||||||
|
closing_fee_rate: Decimal
|
||||||
cooldown_period: number
|
cooldown_period: number
|
||||||
credit_manager: string
|
credit_manager: string
|
||||||
min_position_value: Uint128
|
max_position_in_base_denom?: Uint128 | null
|
||||||
|
min_position_in_base_denom: Uint128
|
||||||
|
opening_fee_rate: Decimal
|
||||||
oracle: OracleBaseForString
|
oracle: OracleBaseForString
|
||||||
|
params: ParamsBaseForString
|
||||||
}
|
}
|
||||||
export type ExecuteMsg =
|
export type ExecuteMsg =
|
||||||
| {
|
| {
|
||||||
@ -74,7 +80,6 @@ export type OwnerUpdate =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
| 'clear_emergency_owner'
|
| 'clear_emergency_owner'
|
||||||
export type Decimal = string
|
|
||||||
export interface SignedDecimal {
|
export interface SignedDecimal {
|
||||||
abs: Decimal
|
abs: Decimal
|
||||||
negative: boolean
|
negative: boolean
|
||||||
@ -142,12 +147,22 @@ export type QueryMsg =
|
|||||||
| {
|
| {
|
||||||
total_pnl: {}
|
total_pnl: {}
|
||||||
}
|
}
|
||||||
|
| {
|
||||||
|
opening_fee: {
|
||||||
|
denom: string
|
||||||
|
size: SignedDecimal
|
||||||
|
}
|
||||||
|
}
|
||||||
export interface ConfigForString {
|
export interface ConfigForString {
|
||||||
base_denom: string
|
base_denom: string
|
||||||
|
closing_fee_rate: Decimal
|
||||||
cooldown_period: number
|
cooldown_period: number
|
||||||
credit_manager: string
|
credit_manager: string
|
||||||
min_position_value: Uint128
|
max_position_in_base_denom?: Uint128 | null
|
||||||
|
min_position_in_base_denom: Uint128
|
||||||
|
opening_fee_rate: Decimal
|
||||||
oracle: OracleBaseForString
|
oracle: OracleBaseForString
|
||||||
|
params: ParamsBaseForString
|
||||||
}
|
}
|
||||||
export interface DenomStateResponse {
|
export interface DenomStateResponse {
|
||||||
denom: string
|
denom: string
|
||||||
@ -155,14 +170,11 @@ export interface DenomStateResponse {
|
|||||||
funding: Funding
|
funding: Funding
|
||||||
last_updated: number
|
last_updated: number
|
||||||
total_cost_base: SignedDecimal
|
total_cost_base: SignedDecimal
|
||||||
total_size: SignedDecimal
|
|
||||||
}
|
}
|
||||||
export interface Funding {
|
export interface Funding {
|
||||||
accumulated_size_weighted_by_index: SignedDecimal
|
last_funding_accrued_per_unit_in_base_denom: SignedDecimal
|
||||||
constant_factor: SignedDecimal
|
last_funding_rate: SignedDecimal
|
||||||
index: SignedDecimal
|
|
||||||
max_funding_velocity: Decimal
|
max_funding_velocity: Decimal
|
||||||
rate: SignedDecimal
|
|
||||||
skew_scale: Decimal
|
skew_scale: Decimal
|
||||||
}
|
}
|
||||||
export type ArrayOfDenomStateResponse = DenomStateResponse[]
|
export type ArrayOfDenomStateResponse = DenomStateResponse[]
|
||||||
@ -172,6 +184,15 @@ export interface DepositResponse {
|
|||||||
shares: Uint128
|
shares: Uint128
|
||||||
}
|
}
|
||||||
export type ArrayOfDepositResponse = DepositResponse[]
|
export type ArrayOfDepositResponse = DepositResponse[]
|
||||||
|
export interface TradingFee {
|
||||||
|
fee: Coin
|
||||||
|
rate: Decimal
|
||||||
|
}
|
||||||
|
export interface Coin {
|
||||||
|
amount: Uint128
|
||||||
|
denom: string
|
||||||
|
[k: string]: unknown
|
||||||
|
}
|
||||||
export interface OwnerResponse {
|
export interface OwnerResponse {
|
||||||
abolished: boolean
|
abolished: boolean
|
||||||
emergency_owner?: string | null
|
emergency_owner?: string | null
|
||||||
@ -180,19 +201,17 @@ export interface OwnerResponse {
|
|||||||
proposed?: string | null
|
proposed?: string | null
|
||||||
}
|
}
|
||||||
export interface PerpDenomState {
|
export interface PerpDenomState {
|
||||||
constant_factor: SignedDecimal
|
|
||||||
denom: string
|
denom: string
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
index: SignedDecimal
|
pnl_values: DenomPnlValues
|
||||||
pnl_values: PnlValues
|
|
||||||
rate: SignedDecimal
|
rate: SignedDecimal
|
||||||
total_cost_base: SignedDecimal
|
total_entry_cost: SignedDecimal
|
||||||
total_size: SignedDecimal
|
total_entry_funding: SignedDecimal
|
||||||
}
|
}
|
||||||
export interface PnlValues {
|
export interface DenomPnlValues {
|
||||||
accrued_funding: SignedDecimal
|
accrued_funding: SignedDecimal
|
||||||
pnl: SignedDecimal
|
pnl: SignedDecimal
|
||||||
unrealized_pnl: SignedDecimal
|
price_pnl: SignedDecimal
|
||||||
}
|
}
|
||||||
export type PnL =
|
export type PnL =
|
||||||
| 'break_even'
|
| 'break_even'
|
||||||
@ -212,14 +231,22 @@ export interface PerpPosition {
|
|||||||
current_price: Decimal
|
current_price: Decimal
|
||||||
denom: string
|
denom: string
|
||||||
entry_price: Decimal
|
entry_price: Decimal
|
||||||
pnl: PnL
|
pnl: PositionPnl
|
||||||
size: SignedDecimal
|
size: SignedDecimal
|
||||||
unrealised_funding_accrued: SignedDecimal
|
|
||||||
}
|
}
|
||||||
export interface Coin {
|
export interface PositionPnl {
|
||||||
amount: Uint128
|
coins: PnlCoins
|
||||||
denom: string
|
values: PnlValues
|
||||||
[k: string]: unknown
|
}
|
||||||
|
export interface PnlCoins {
|
||||||
|
closing_fee: Coin
|
||||||
|
pnl: PnL
|
||||||
|
}
|
||||||
|
export interface PnlValues {
|
||||||
|
accrued_funding: SignedDecimal
|
||||||
|
closing_fee: SignedDecimal
|
||||||
|
pnl: SignedDecimal
|
||||||
|
price_pnl: SignedDecimal
|
||||||
}
|
}
|
||||||
export type ArrayOfPositionResponse = PositionResponse[]
|
export type ArrayOfPositionResponse = PositionResponse[]
|
||||||
export interface PositionsByAccountResponse {
|
export interface PositionsByAccountResponse {
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { SearchParams } from 'types/enums/searchParams'
|
||||||
|
|
||||||
export function getRoute(
|
export function getRoute(
|
||||||
page: Page,
|
page: Page,
|
||||||
searchParams: URLSearchParams,
|
searchParams: URLSearchParams,
|
||||||
@ -19,8 +21,8 @@ export function getRoute(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (accountId) {
|
if (accountId) {
|
||||||
url.searchParams.delete('accountId')
|
url.searchParams.delete(SearchParams.ACCOUNT_ID)
|
||||||
url.searchParams.append('accountId', accountId)
|
url.searchParams.append(SearchParams.ACCOUNT_ID, accountId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return url.pathname + url.search
|
return url.pathname + url.search
|
||||||
@ -53,3 +55,11 @@ export function getPage(pathname: string): Page {
|
|||||||
|
|
||||||
return 'trade' as Page
|
return 'trade' as Page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getSearchParamsObject(searchParams: URLSearchParams) {
|
||||||
|
const params: { [key: string]: string } = {}
|
||||||
|
|
||||||
|
Array.from(searchParams?.entries() || []).forEach(([key, value]) => (params[key] = value))
|
||||||
|
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user