Merge branch 'main' into share-positions-2
This commit is contained in:
commit
73c3d111cf
|
@ -35,3 +35,5 @@ yarn-error.log*
|
|||
|
||||
# Sentry
|
||||
.sentryclirc
|
||||
|
||||
.idea
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { AccountInfo, PublicKey, Transaction } from '@solana/web3.js'
|
||||
import { Market, OpenOrders } from '@project-serum/serum'
|
||||
import { Event } from '@project-serum/serum/lib/queue'
|
||||
import { I80F48 } from '@blockworks-foundation/mango-client/lib/src/fixednum'
|
||||
import { I80F48 } from '@blockworks-foundation/mango-client'
|
||||
|
||||
export interface Token {
|
||||
chainId: number // 101,
|
||||
|
|
|
@ -40,6 +40,7 @@ export default function AccountInfo() {
|
|||
const { mangoAccount, initialLoad } = useMangoAccount()
|
||||
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
|
||||
const mangoClient = useMangoStore((s) => s.connection.client)
|
||||
const wallet = useMangoStore((s) => s.wallet.current)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.sm : false
|
||||
|
@ -49,6 +50,8 @@ export default function AccountInfo() {
|
|||
const [showWithdrawModal, setShowWithdrawModal] = useState(false)
|
||||
const [showAlertsModal, setShowAlertsModal] = useState(false)
|
||||
|
||||
const canWithdraw = mangoAccount?.owner.equals(wallet.publicKey)
|
||||
|
||||
const handleCloseDeposit = useCallback(() => {
|
||||
setShowDepositModal(false)
|
||||
}, [])
|
||||
|
@ -361,7 +364,7 @@ export default function AccountInfo() {
|
|||
<Button
|
||||
onClick={() => setShowWithdrawModal(true)}
|
||||
className="w-full"
|
||||
disabled={!connected || !mangoAccount}
|
||||
disabled={!connected || !mangoAccount || !canWithdraw}
|
||||
>
|
||||
<span>{t('withdraw')}</span>
|
||||
</Button>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { FunctionComponent, useEffect, useState } from 'react'
|
||||
import { RadioGroup } from '@headlessui/react'
|
||||
import { CheckCircleIcon } from '@heroicons/react/solid'
|
||||
import { PlusCircleIcon } from '@heroicons/react/outline'
|
||||
import { PlusCircleIcon, UsersIcon } from '@heroicons/react/outline'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { MangoAccount, MangoGroup } from '@blockworks-foundation/mango-client'
|
||||
import { abbreviateAddress, formatUsdValue } from '../utils'
|
||||
|
@ -11,6 +11,7 @@ import { ElementTitle } from './styles'
|
|||
import Button, { LinkButton } from './Button'
|
||||
import NewAccount from './NewAccount'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Tooltip from './Tooltip'
|
||||
|
||||
export const LAST_ACCOUNT_KEY = 'lastAccountViewed-3.0'
|
||||
|
||||
|
@ -34,6 +35,7 @@ const AccountsModal: FunctionComponent<AccountsModalProps> = ({
|
|||
const setMangoStore = useMangoStore((s) => s.set)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
const [, setLastAccountViewed] = useLocalStorageState(LAST_ACCOUNT_KEY)
|
||||
const wallet = useMangoStore.getState().wallet.current
|
||||
|
||||
const handleMangoAccountChange = (mangoAccount: MangoAccount) => {
|
||||
setLastAccountViewed(mangoAccount.publicKey.toString())
|
||||
|
@ -119,9 +121,22 @@ const AccountsModal: FunctionComponent<AccountsModalProps> = ({
|
|||
<div className="text-sm">
|
||||
<RadioGroup.Label className="cursor-pointer flex items-center text-th-fgd-1">
|
||||
<div>
|
||||
<div className="pb-0.5">
|
||||
<div className="pb-0.5 flex items-center">
|
||||
{account?.name ||
|
||||
abbreviateAddress(account.publicKey)}
|
||||
{!account?.owner.equals(
|
||||
wallet?.publicKey
|
||||
) ? (
|
||||
<Tooltip
|
||||
content={t(
|
||||
'delegate:delegated-account'
|
||||
)}
|
||||
>
|
||||
<UsersIcon className="h-3 w-3 ml-1.5" />
|
||||
</Tooltip>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
{mangoGroup ? (
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
|
|
|
@ -56,6 +56,9 @@ const BalancesTable = ({
|
|||
const { width } = useViewport()
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
const isMobile = width ? width < breakpoints.md : false
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const wallet = useMangoStore((s) => s.wallet.current)
|
||||
const canWithdraw = mangoAccount?.owner.equals(wallet.publicKey)
|
||||
|
||||
const handleSizeClick = (size, symbol) => {
|
||||
const step = selectedMarket.minOrderSize
|
||||
|
@ -70,7 +73,7 @@ const BalancesTable = ({
|
|||
const baseSize = Math.floor(size / priceOrDefault / step) * step
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = baseSize
|
||||
state.tradeForm.quoteSize = (baseSize * priceOrDefault).toFixed(2)
|
||||
state.tradeForm.quoteSize = baseSize * priceOrDefault
|
||||
state.tradeForm.side = 'buy'
|
||||
})
|
||||
} else {
|
||||
|
@ -78,7 +81,7 @@ const BalancesTable = ({
|
|||
const quoteSize = roundedSize * priceOrDefault
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize.toFixed(2)
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = 'sell'
|
||||
})
|
||||
}
|
||||
|
@ -95,9 +98,7 @@ const BalancesTable = ({
|
|||
}, [])
|
||||
|
||||
async function handleSettleAll() {
|
||||
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
|
||||
const markets = useMangoStore.getState().selectedMangoGroup.markets
|
||||
const wallet = useMangoStore.getState().wallet.current
|
||||
|
||||
try {
|
||||
setSubmitting(true)
|
||||
|
@ -344,7 +345,7 @@ const BalancesTable = ({
|
|||
</thead>
|
||||
<tbody>
|
||||
{items.map((balance, index) => (
|
||||
<TrBody index={index} key={`${balance.symbol}${index}`}>
|
||||
<TrBody key={`${balance.symbol}${index}`}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -411,6 +412,7 @@ const BalancesTable = ({
|
|||
onClick={() =>
|
||||
handleOpenWithdrawModal(balance.symbol)
|
||||
}
|
||||
disabled={!canWithdraw}
|
||||
>
|
||||
{t('withdraw')}
|
||||
</Button>
|
||||
|
@ -449,8 +451,8 @@ const BalancesTable = ({
|
|||
{items.map((balance, index) => (
|
||||
<ExpandableRow
|
||||
buttonTemplate={
|
||||
<div className="flex items-center justify-between text-fgd-1 w-full">
|
||||
<div className="flex items-center text-fgd-1">
|
||||
<div className="flex items-center justify-between text-th-fgd-1 w-full">
|
||||
<div className="flex items-center text-th-fgd-1">
|
||||
<img
|
||||
alt=""
|
||||
width="20"
|
||||
|
@ -461,7 +463,7 @@ const BalancesTable = ({
|
|||
|
||||
{balance.symbol}
|
||||
</div>
|
||||
<div className="text-fgd-1 text-right">
|
||||
<div className="text-th-fgd-1 text-right">
|
||||
{balance.net.toFixed()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -6,6 +6,7 @@ interface ButtonGroupProps {
|
|||
onChange: (x) => void
|
||||
unit?: string
|
||||
values: Array<string>
|
||||
names?: Array<string>
|
||||
}
|
||||
|
||||
const ButtonGroup: FunctionComponent<ButtonGroupProps> = ({
|
||||
|
@ -14,6 +15,7 @@ const ButtonGroup: FunctionComponent<ButtonGroupProps> = ({
|
|||
unit,
|
||||
values,
|
||||
onChange,
|
||||
names,
|
||||
}) => {
|
||||
return (
|
||||
<div className="bg-th-bkg-3 rounded-md">
|
||||
|
@ -44,8 +46,7 @@ const ButtonGroup: FunctionComponent<ButtonGroupProps> = ({
|
|||
width: `${100 / values.length}%`,
|
||||
}}
|
||||
>
|
||||
{v}
|
||||
{unit}
|
||||
{names ? (unit ? names[i] + unit : names[i]) : unit ? v + unit : v}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -25,7 +25,6 @@ import {
|
|||
import { formatUsdValue } from '../utils'
|
||||
|
||||
interface CloseAccountModalProps {
|
||||
accountName?: string
|
||||
lamports?: number
|
||||
isOpen: boolean
|
||||
onClose?: (x?) => void
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import { FunctionComponent, useState } from 'react'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { ExclamationCircleIcon } from '@heroicons/react/outline'
|
||||
import Input from './Input'
|
||||
import Button from './Button'
|
||||
import Modal from './Modal'
|
||||
import { ElementTitle } from './styles'
|
||||
import { notify } from '../utils/notifications'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
|
||||
interface DelegateModalProps {
|
||||
delegate?: PublicKey
|
||||
isOpen: boolean
|
||||
onClose?: (x?) => void
|
||||
}
|
||||
|
||||
const DelegateModal: FunctionComponent<DelegateModalProps> = ({
|
||||
delegate,
|
||||
isOpen,
|
||||
onClose,
|
||||
}) => {
|
||||
const { t } = useTranslation(['common', 'delegate'])
|
||||
|
||||
const [keyBase58, setKeyBase58] = useState(
|
||||
delegate.equals(PublicKey.default) ? '' : delegate.toBase58()
|
||||
)
|
||||
const [invalidKeyMessage, setInvalidKeyMessage] = useState('')
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const mangoClient = useMangoStore((s) => s.connection.client)
|
||||
const actions = useMangoStore((s) => s.actions)
|
||||
|
||||
const setDelegate = async () => {
|
||||
const wallet = useMangoStore.getState().wallet.current
|
||||
|
||||
try {
|
||||
const key = keyBase58.length
|
||||
? new PublicKey(keyBase58)
|
||||
: PublicKey.default
|
||||
const txid = await mangoClient.setDelegate(
|
||||
mangoGroup,
|
||||
mangoAccount,
|
||||
wallet,
|
||||
key
|
||||
)
|
||||
actions.reloadMangoAccount()
|
||||
onClose()
|
||||
notify({
|
||||
title: t('delegate:delegate-updated'),
|
||||
txid,
|
||||
})
|
||||
} catch (err) {
|
||||
console.warn('Error setting delegate key:', err)
|
||||
notify({
|
||||
title: t('delegate:set-error'),
|
||||
description: `${err}`,
|
||||
txid: err.txid,
|
||||
type: 'error',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const validateKeyInput = () => {
|
||||
if (keyBase58.length != 44 && keyBase58.length != 0) {
|
||||
setInvalidKeyMessage(t('delegate:invalid-key'))
|
||||
return false
|
||||
} else {
|
||||
setInvalidKeyMessage('')
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
const onChangeKeyInput = (name) => {
|
||||
setKeyBase58(name)
|
||||
validateKeyInput()
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal onClose={onClose} isOpen={isOpen}>
|
||||
<Modal.Header>
|
||||
<div className="flex items-center">
|
||||
<ElementTitle noMarginBottom>
|
||||
{t('delegate:delegate-your-account')}
|
||||
</ElementTitle>
|
||||
</div>
|
||||
</Modal.Header>
|
||||
<div className="flex items-center justify-center text-th-fgd-3 pb-4">
|
||||
<p className="text-center">{t('delegate:info')}</p>
|
||||
</div>
|
||||
<div className="pb-2 text-th-fgd-1">{t('delegate:public-key')}</div>
|
||||
<Input
|
||||
type="text"
|
||||
className={`border border-th-fgd-4 flex-grow`}
|
||||
error={!!invalidKeyMessage}
|
||||
value={keyBase58}
|
||||
onBlur={validateKeyInput}
|
||||
onChange={(e) => onChangeKeyInput(e.target.value)}
|
||||
/>
|
||||
{invalidKeyMessage ? (
|
||||
<div className="flex items-center pt-1.5 text-th-red">
|
||||
<ExclamationCircleIcon className="h-4 w-4 mr-1.5" />
|
||||
{invalidKeyMessage}
|
||||
</div>
|
||||
) : null}
|
||||
<Button
|
||||
onClick={() => setDelegate()}
|
||||
disabled={keyBase58.length != 44 && keyBase58.length != 0}
|
||||
className="mt-4 w-full"
|
||||
>
|
||||
{t('delegate:set-delegate')}
|
||||
</Button>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default DelegateModal
|
|
@ -39,7 +39,7 @@ export default function MarketBalances() {
|
|||
const baseSize = Math.floor(size / priceOrDefault / step) * step
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = baseSize
|
||||
state.tradeForm.quoteSize = (baseSize * priceOrDefault).toFixed(2)
|
||||
state.tradeForm.quoteSize = baseSize * priceOrDefault
|
||||
state.tradeForm.side = 'buy'
|
||||
})
|
||||
} else {
|
||||
|
@ -47,7 +47,7 @@ export default function MarketBalances() {
|
|||
const quoteSize = roundedSize * priceOrDefault
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize.toFixed(2)
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = 'sell'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
|||
|
||||
const orderbook = useMangoStore.getState().selectedMarket.orderBook
|
||||
const markPrice = useMangoStore.getState().selectedMarket.markPrice
|
||||
const referrerPk = useMangoStore.getState().referrerPk
|
||||
|
||||
// The reference price is the book mid if book is double sided; else mark price
|
||||
const bb = orderbook?.bids?.length > 0 && Number(orderbook.bids[0][0])
|
||||
|
@ -70,7 +71,8 @@ const MarketCloseModal: FunctionComponent<MarketCloseModalProps> = ({
|
|||
'ioc',
|
||||
0, // client order id
|
||||
side === 'buy' ? askInfo : bidInfo,
|
||||
true // reduce only
|
||||
true, // reduce only
|
||||
referrerPk ? referrerPk : undefined
|
||||
)
|
||||
await sleep(500)
|
||||
actions.reloadMangoAccount()
|
||||
|
|
|
@ -8,12 +8,17 @@ import ManualRefresh from './ManualRefresh'
|
|||
import useOraclePrice from '../hooks/useOraclePrice'
|
||||
import DayHighLow from './DayHighLow'
|
||||
import { useEffect } from 'react'
|
||||
import { getDecimalCount, usdFormatter } from '../utils'
|
||||
import {
|
||||
getDecimalCount,
|
||||
patchInternalMarketName,
|
||||
usdFormatter,
|
||||
} from '../utils'
|
||||
import { PerpMarket } from '@blockworks-foundation/mango-client'
|
||||
import BN from 'bn.js'
|
||||
import { useViewport } from '../hooks/useViewport'
|
||||
import { breakpoints } from './TradePageGrid'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import Tooltip from './Tooltip'
|
||||
|
||||
const SECONDS = 1000
|
||||
|
||||
|
@ -65,10 +70,28 @@ const MarketDetails = () => {
|
|||
const isMobile = width ? width < breakpoints.sm : false
|
||||
|
||||
const [ohlcv, setOhlcv] = useState(null)
|
||||
const [change24h, setChange24h] = useState(0)
|
||||
const [, setLoading] = useState(false)
|
||||
const [perpStats, setPerpStats] = useState([])
|
||||
const [perpVolume, setPerpVolume] = useState(0)
|
||||
const change = ohlcv ? ((ohlcv.c[0] - ohlcv.o[0]) / ohlcv.o[0]) * 100 : ''
|
||||
|
||||
const fetchMarketInfo = useCallback(async () => {
|
||||
const marketInfo = await fetch(
|
||||
`https://event-history-api-candles.herokuapp.com/markets/${patchInternalMarketName(
|
||||
selectedMarketName
|
||||
)}`
|
||||
)
|
||||
const parsedMarketInfo = await marketInfo.json()
|
||||
setChange24h(parsedMarketInfo?.change24h)
|
||||
}, [selectedMarketName])
|
||||
|
||||
useInterval(() => {
|
||||
fetchMarketInfo()
|
||||
}, 120 * SECONDS)
|
||||
|
||||
useEffect(() => {
|
||||
fetchMarketInfo()
|
||||
}, [fetchMarketInfo])
|
||||
|
||||
const fetchPerpStats = useCallback(async () => {
|
||||
const urlParams = new URLSearchParams({ mangoGroup: groupConfig.name })
|
||||
|
@ -187,19 +210,19 @@ const MarketDetails = () => {
|
|||
</div>
|
||||
<div className="flex items-center justify-between md:block">
|
||||
<div className="text-th-fgd-3 tiny-text pb-0.5">
|
||||
{t('daily-change')}
|
||||
{t('rolling-change')}
|
||||
</div>
|
||||
{change || change === 0 ? (
|
||||
{change24h || change24h === 0 ? (
|
||||
<div
|
||||
className={`md:text-xs ${
|
||||
change > 0
|
||||
change24h > 0
|
||||
? `text-th-green`
|
||||
: change < 0
|
||||
: change24h < 0
|
||||
? `text-th-red`
|
||||
: `text-th-fgd-1`
|
||||
}`}
|
||||
>
|
||||
{change.toFixed(2) + '%'}
|
||||
{(change24h * 100).toFixed(2) + '%'}
|
||||
</div>
|
||||
) : (
|
||||
<MarketDataLoader />
|
||||
|
@ -221,18 +244,23 @@ const MarketDetails = () => {
|
|||
) : null}
|
||||
{isPerpMarket && selectedMarket instanceof PerpMarket ? (
|
||||
<>
|
||||
<div className="flex items-center justify-between md:block">
|
||||
<div className="text-th-fgd-3 tiny-text pb-0.5">
|
||||
{t('average-funding')}
|
||||
<Tooltip
|
||||
content="Funding is paid continuously. The 1hr rate displayed is a rolling average of the past 60 mins."
|
||||
placement={'bottom'}
|
||||
>
|
||||
<div className="flex items-center justify-between md:block hover:cursor-help">
|
||||
<div className="flex text-th-fgd-3 tiny-text pb-0.5 items-center">
|
||||
{t('average-funding')}
|
||||
</div>
|
||||
<div className="text-th-fgd-1 md:text-xs">
|
||||
{selectedMarket ? (
|
||||
`${funding1hStr}% (${fundingAprStr}% APR)`
|
||||
) : (
|
||||
<MarketDataLoader />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-th-fgd-1 md:text-xs">
|
||||
{selectedMarket ? (
|
||||
`${funding1hStr}% (${fundingAprStr}% APR)`
|
||||
) : (
|
||||
<MarketDataLoader />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className="flex items-center justify-between md:block">
|
||||
<div className="text-th-fgd-3 tiny-text pb-0.5">
|
||||
{t('open-interest')}
|
||||
|
|
|
@ -38,7 +38,7 @@ export default function MarketMenuItem({ menuTitle = '', linksArray = [] }) {
|
|||
className="cursor-pointer flex flex-col h-10 px-3"
|
||||
>
|
||||
<div
|
||||
className={`default-transition flex items-center h-10 text-th-fgd-3 hover:text-th-primary focus:outline-none ${
|
||||
className={`flex items-center h-10 text-th-fgd-3 hover:text-th-primary focus:outline-none ${
|
||||
isSelected ? 'text-th-primary' : ''
|
||||
}`}
|
||||
>
|
||||
|
|
|
@ -74,8 +74,7 @@ export default function MarketPosition() {
|
|||
const connected = useMangoStore((s) => s.wallet.connected)
|
||||
const setMangoStore = useMangoStore((s) => s.set)
|
||||
const price = useMangoStore((s) => s.tradeForm.price)
|
||||
const perpAccounts =
|
||||
useMangoStore.getState().selectedMangoAccount.perpAccounts
|
||||
const perpAccounts = useMangoStore((s) => s.selectedMangoAccount.perpAccounts)
|
||||
const baseSymbol = marketConfig.baseSymbol
|
||||
const marketName = marketConfig.name
|
||||
|
||||
|
@ -103,7 +102,7 @@ export default function MarketPosition() {
|
|||
const quoteSize = roundedSize * priceOrDefault
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize.toFixed(2)
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = side === 'buy' ? 'sell' : 'buy'
|
||||
})
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ const MarketSelect = () => {
|
|||
<div className="bg-th-bkg-4 flex items-center pl-4 lg:pl-9 pr-1">
|
||||
{isMobile ? (
|
||||
<MenuIcon
|
||||
className="cursor-pointer default-transition h-5 text-th-fgd-1 w-5 hover:text-th-primary"
|
||||
className="cursor-pointer h-5 text-th-fgd-1 w-5 hover:text-th-primary"
|
||||
onClick={() => setShowMarketsModal(true)}
|
||||
/>
|
||||
) : (
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { useRef, useState } from 'react'
|
||||
import { Popover } from '@headlessui/react'
|
||||
import { ChevronDownIcon } from '@heroicons/react/outline'
|
||||
import Link from 'next/link'
|
||||
import { ChevronDownIcon } from '@heroicons/react/outline'
|
||||
|
||||
|
@ -43,7 +44,7 @@ export default function NavDropMenu({
|
|||
className="flex flex-col"
|
||||
>
|
||||
<Popover.Button
|
||||
className="h-10 text-th-fgd-1 hover:text-th-primary md:px-2 lg:px-4 focus:outline-none"
|
||||
className="h-10 text-th-fgd-1 hover:text-th-primary md:px-2 lg:px-4 focus:outline-none transition-none"
|
||||
ref={buttonRef}
|
||||
>
|
||||
<div
|
||||
|
|
|
@ -198,7 +198,7 @@ const NewAccount: FunctionComponent<NewAccountProps> = ({
|
|||
<Button
|
||||
disabled={
|
||||
parseFloat(inputAmount) <= 0 ||
|
||||
parseFloat(inputAmount) > selectedAccount.uiBalance
|
||||
parseFloat(inputAmount) > selectedAccount?.uiBalance
|
||||
}
|
||||
onClick={handleNewAccountDeposit}
|
||||
className="w-full"
|
||||
|
|
|
@ -79,7 +79,7 @@ const DesktopTable = ({
|
|||
const decimals = getDecimalCount(market.account.tickSize)
|
||||
const editThisOrder = editOrderIndex === index
|
||||
return (
|
||||
<TrBody index={index} key={`${order.orderId}${order.side}`}>
|
||||
<TrBody key={`${order.orderId}${order.side}`}>
|
||||
<Td className="w-[14.286%]">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -414,6 +414,7 @@ const OpenOrdersTable = () => {
|
|||
const bidInfo =
|
||||
useMangoStore.getState().accountInfos[marketConfig.bidsKey.toString()]
|
||||
const wallet = useMangoStore.getState().wallet.current
|
||||
const referrerPk = useMangoStore.getState().referrerPk
|
||||
|
||||
if (!wallet || !mangoGroup || !mangoAccount || !market) return
|
||||
setModifyId(order.orderId)
|
||||
|
@ -455,7 +456,9 @@ const OpenOrdersTable = () => {
|
|||
size,
|
||||
orderType,
|
||||
0,
|
||||
order.side === 'buy' ? askInfo : bidInfo
|
||||
order.side === 'buy' ? askInfo : bidInfo,
|
||||
false,
|
||||
referrerPk ? referrerPk : undefined
|
||||
)
|
||||
}
|
||||
notify({ title: t('successfully-placed'), txid })
|
||||
|
|
|
@ -691,7 +691,7 @@ const OrderbookRow = React.memo<any>(
|
|||
<div className="flex justify-between w-full hover:font-semibold">
|
||||
<div
|
||||
onClick={handlePriceClick}
|
||||
className={`z-10 text-xs leading-5 md:leading-6 text-th-fgd-1 md:pl-5 ${
|
||||
className={`z-10 text-xs leading-5 md:leading-6 md:pl-5 ${
|
||||
side === 'buy'
|
||||
? `text-th-green`
|
||||
: `text-th-red brightness-125`
|
||||
|
|
|
@ -51,7 +51,7 @@ const PositionsTable = () => {
|
|||
const quoteSize = roundedSize * priceOrDefault
|
||||
setMangoStore((state) => {
|
||||
state.tradeForm.baseSize = roundedSize
|
||||
state.tradeForm.quoteSize = quoteSize.toFixed(2)
|
||||
state.tradeForm.quoteSize = quoteSize
|
||||
state.tradeForm.side = side === 'buy' ? 'sell' : 'buy'
|
||||
})
|
||||
}
|
||||
|
@ -131,25 +131,19 @@ const PositionsTable = () => {
|
|||
</thead>
|
||||
<tbody>
|
||||
{openPositions.map(
|
||||
(
|
||||
{
|
||||
marketConfig,
|
||||
perpMarket,
|
||||
perpAccount,
|
||||
basePosition,
|
||||
notionalSize,
|
||||
indexPrice,
|
||||
avgEntryPrice,
|
||||
breakEvenPrice,
|
||||
unrealizedPnl,
|
||||
},
|
||||
index
|
||||
) => {
|
||||
({
|
||||
marketConfig,
|
||||
perpMarket,
|
||||
perpAccount,
|
||||
basePosition,
|
||||
notionalSize,
|
||||
indexPrice,
|
||||
avgEntryPrice,
|
||||
breakEvenPrice,
|
||||
unrealizedPnl,
|
||||
}) => {
|
||||
return (
|
||||
<TrBody
|
||||
index={index}
|
||||
key={`${marketConfig.marketIndex}`}
|
||||
>
|
||||
<TrBody key={`${marketConfig.marketIndex}`}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
|
|
@ -4,7 +4,7 @@ import useInterval from '../hooks/useInterval'
|
|||
import ChartApi from '../utils/chartDataConnector'
|
||||
import { ElementTitle } from './styles'
|
||||
import { getDecimalCount, isEqual, usdFormatter } from '../utils/index'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import useMangoStore, { CLUSTER } from '../stores/useMangoStore'
|
||||
import { useViewport } from '../hooks/useViewport'
|
||||
import { breakpoints } from './TradePageGrid'
|
||||
import { ExpandableRow } from './TableElements'
|
||||
|
@ -37,11 +37,15 @@ export default function RecentMarketTrades() {
|
|||
}, [marketConfig, trades])
|
||||
|
||||
useEffect(() => {
|
||||
fetchTradesForChart()
|
||||
if (CLUSTER === 'mainnet') {
|
||||
fetchTradesForChart()
|
||||
}
|
||||
}, [fetchTradesForChart])
|
||||
|
||||
useInterval(async () => {
|
||||
fetchTradesForChart()
|
||||
if (CLUSTER === 'mainnet') {
|
||||
fetchTradesForChart()
|
||||
}
|
||||
}, 2000)
|
||||
|
||||
return !isMobile ? (
|
||||
|
@ -78,7 +82,7 @@ export default function RecentMarketTrades() {
|
|||
)
|
||||
: ''}
|
||||
</div>
|
||||
<div className={`text-right text-th-fgd-4`}>
|
||||
<div className={`text-right text-th-fgd-3`}>
|
||||
{trade.time && new Date(trade.time).toLocaleTimeString()}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -395,7 +395,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
<Disclosure.Panel>
|
||||
<div className="border border-th-bkg-4 border-t-0 p-3 rounded-b-md">
|
||||
<div className="font-bold m-1 mt-0 pb-2 text-th-fgd-1 text-base">
|
||||
Market Data
|
||||
{t('market-data')}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3 grid-flow-row">
|
||||
{inputTokenInfo.market_cap_rank ? (
|
||||
|
@ -539,7 +539,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{topHolders?.inputHolders ? (
|
||||
<div className="pt-4">
|
||||
<div className="font-bold m-1 pb-3 text-th-fgd-1 text-base">
|
||||
Top 10 Holders
|
||||
{t('swap:top-ten')}
|
||||
</div>
|
||||
{topHolders.inputHolders.map((holder) => (
|
||||
<a
|
||||
|
@ -646,7 +646,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
<Disclosure.Panel>
|
||||
<div className="border border-th-bkg-4 border-t-0 p-3 rounded-b-md">
|
||||
<div className="font-bold m-1 mt-0 pb-2 text-th-fgd-1 text-base">
|
||||
Market Data
|
||||
{t('market-data')}
|
||||
</div>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3 grid-flow-row">
|
||||
{outputTokenInfo.market_cap_rank ? (
|
||||
|
@ -790,7 +790,7 @@ const SwapTokenInfo: FunctionComponent<SwapTokenInfoProps> = ({
|
|||
{topHolders?.outputHolders ? (
|
||||
<div className="pt-4">
|
||||
<div className="font-bold m-1 pb-3 text-th-fgd-1 text-base">
|
||||
Top 10 Holders
|
||||
{t('swap:top-ten')}
|
||||
</div>
|
||||
{topHolders.outputHolders.map((holder) => (
|
||||
<a
|
||||
|
|
|
@ -9,18 +9,24 @@ import { numberCompacter, numberFormatter } from './SwapTokenInfo'
|
|||
import Button, { IconButton } from './Button'
|
||||
import Input from './Input'
|
||||
import { SearchIcon, XIcon } from '@heroicons/react/outline'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
|
||||
const filterByVals = ['change-percent', '24h-volume']
|
||||
const timeFrameVals = ['24h', '7d', '30d']
|
||||
const insightTypeVals = ['best', 'worst']
|
||||
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
||||
const [tokenInsights, setTokenInsights] = useState([])
|
||||
const [filteredTokenInsights, setFilteredTokenInsights] = useState([])
|
||||
const [insightType, setInsightType] = useState('Best')
|
||||
const [filterBy, setFilterBy] = useState('Change %')
|
||||
const [timeframe, setTimeframe] = useState('24h')
|
||||
const [insightType, setInsightType] = useState(insightTypeVals[0])
|
||||
const [filterBy, setFilterBy] = useState(filterByVals[0])
|
||||
const [timeframe, setTimeframe] = useState(timeFrameVals[0])
|
||||
const [textFilter, setTextFilter] = useState('')
|
||||
const [showSearch, setShowSearch] = useState(false)
|
||||
const [loading, setLoading] = useState(false)
|
||||
const { t } = useTranslation(['common', 'swap'])
|
||||
|
||||
const getTokenInsights = async () => {
|
||||
setLoading(true)
|
||||
|
@ -36,11 +42,12 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (filterBy === 'Change %' && textFilter === '') {
|
||||
if (filterBy === filterByVals[0] && textFilter === '') {
|
||||
//filter by 'change %'
|
||||
setFilteredTokenInsights(
|
||||
tokenInsights
|
||||
.sort((a, b) =>
|
||||
insightType === 'Best'
|
||||
insightType === insightTypeVals[0] //insight type 'best'
|
||||
? b[`price_change_percentage_${timeframe}_in_currency`] -
|
||||
a[`price_change_percentage_${timeframe}_in_currency`]
|
||||
: a[`price_change_percentage_${timeframe}_in_currency`] -
|
||||
|
@ -49,11 +56,12 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
.slice(0, 10)
|
||||
)
|
||||
}
|
||||
if (filterBy === '24h Volume' && textFilter === '') {
|
||||
if (filterBy === filterByVals[1] && textFilter === '') {
|
||||
//filter by 24h vol
|
||||
setFilteredTokenInsights(
|
||||
tokenInsights
|
||||
.sort((a, b) =>
|
||||
insightType === 'Best'
|
||||
insightType === insightTypeVals[0] //insight type 'best'
|
||||
? b.total_volume - a.total_volume
|
||||
: a.total_volume - b.total_volume
|
||||
)
|
||||
|
@ -91,17 +99,18 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
activeValue={filterBy}
|
||||
className="h-10"
|
||||
onChange={(t) => setFilterBy(t)}
|
||||
values={['Change %', '24h Volume']}
|
||||
values={filterByVals}
|
||||
names={filterByVals.map((val) => t(`swap:${val}`))}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex space-x-2">
|
||||
{filterBy === 'Change %' ? (
|
||||
{filterBy === filterByVals[0] ? ( //filter by change %
|
||||
<div className="w-36">
|
||||
<ButtonGroup
|
||||
activeValue={timeframe}
|
||||
className="h-10"
|
||||
onChange={(t) => setTimeframe(t)}
|
||||
values={['24h', '7d', '30d']}
|
||||
values={timeFrameVals}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -110,7 +119,8 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
activeValue={insightType}
|
||||
className="h-10"
|
||||
onChange={(t) => setInsightType(t)}
|
||||
values={['Best', 'Worst']}
|
||||
values={insightTypeVals}
|
||||
names={insightTypeVals.map((val) => t(`swap:${val}`))}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -167,12 +177,12 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
<div className="flex items-center space-x-3">
|
||||
<div
|
||||
className={`min-w-[48px] text-xs ${
|
||||
timeframe === '24h'
|
||||
timeframe === timeFrameVals[0] //timeframe 24h
|
||||
? insight.price_change_percentage_24h_in_currency >=
|
||||
0
|
||||
? 'text-th-green'
|
||||
: 'text-th-red'
|
||||
: timeframe === '7d'
|
||||
: timeframe === timeFrameVals[1] //timeframe 7d
|
||||
? insight.price_change_percentage_7d_in_currency >=
|
||||
0
|
||||
? 'text-th-green'
|
||||
|
@ -183,13 +193,13 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
: 'text-th-red'
|
||||
}`}
|
||||
>
|
||||
{timeframe === '24h'
|
||||
{timeframe === timeFrameVals[0] //timeframe 24h
|
||||
? insight.price_change_percentage_24h_in_currency
|
||||
? `${insight.price_change_percentage_24h_in_currency.toFixed(
|
||||
1
|
||||
)}%`
|
||||
: '?'
|
||||
: timeframe === '7d'
|
||||
: timeframe === timeFrameVals[1] //timeframe 7d
|
||||
? insight.price_change_percentage_7d_in_currency
|
||||
? `${insight.price_change_percentage_7d_in_currency.toFixed(
|
||||
1
|
||||
|
@ -225,7 +235,9 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
</div>
|
||||
<div className="flex items-center pl-2 space-x-3 text-right text-xs">
|
||||
<div>
|
||||
<div className="mb-[4px] text-th-fgd-4">Price</div>
|
||||
<div className="mb-[4px] text-th-fgd-4">
|
||||
{t('price')}
|
||||
</div>
|
||||
<div className="text-th-fgd-3">
|
||||
$
|
||||
{insight.current_price.toLocaleString(undefined, {
|
||||
|
@ -236,7 +248,9 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
</div>
|
||||
<div className="border-l border-th-bkg-4" />
|
||||
<div>
|
||||
<div className="mb-[4px] text-th-fgd-4">24h Vol</div>
|
||||
<div className="mb-[4px] text-th-fgd-4">
|
||||
{t('swap:24h-vol')}
|
||||
</div>
|
||||
<div className="text-th-fgd-3">
|
||||
{insight.total_volume > 0
|
||||
? `$${numberCompacter.format(
|
||||
|
@ -263,7 +277,7 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
})
|
||||
}
|
||||
>
|
||||
Buy
|
||||
{t('buy')}
|
||||
</Button>
|
||||
</div>
|
||||
<Disclosure.Panel className="bg-th-bkg-2 border-b border-th-bkg-4 px-2 pb-2">
|
||||
|
@ -271,7 +285,7 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
{insight.market_cap_rank ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Market Cap Rank
|
||||
{t('swap:market-cap-rank')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1">
|
||||
#{insight.market_cap_rank}
|
||||
|
@ -281,7 +295,7 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
{insight?.market_cap && insight?.market_cap !== 0 ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Market Cap
|
||||
{t('swap:market-cap')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1">
|
||||
${numberCompacter.format(insight.market_cap)}
|
||||
|
@ -291,14 +305,15 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
{insight?.circulating_supply ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Token Supply
|
||||
{t('swap:token-supply')}
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1">
|
||||
{numberCompacter.format(insight.circulating_supply)}
|
||||
</div>
|
||||
{insight?.max_supply ? (
|
||||
<div className="text-th-fgd-2 text-xs">
|
||||
Max Supply:{' '}
|
||||
{t('swap:max-supply')}
|
||||
{': '}
|
||||
{numberCompacter.format(insight.max_supply)}
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -307,7 +322,7 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
{insight?.ath ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
All-Time High
|
||||
{t('swap:ath')}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="font-bold text-th-fgd-1">
|
||||
|
@ -335,7 +350,7 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
{insight?.atl ? (
|
||||
<div className="border border-th-bkg-4 m-1 p-3 rounded-md">
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
All-Time Low
|
||||
{t('swap:atl')}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<div className="font-bold text-th-fgd-1">
|
||||
|
@ -377,7 +392,7 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
})
|
||||
}
|
||||
>
|
||||
Buy
|
||||
{t('buy')}
|
||||
</Button>
|
||||
</Disclosure.Panel>
|
||||
</>
|
||||
|
@ -387,13 +402,13 @@ const SwapTokenInsights = ({ formState, jupiterTokens, setOutputToken }) => {
|
|||
})
|
||||
) : (
|
||||
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
|
||||
No tokens found...
|
||||
{t('swap:no-tokens-found')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-th-bkg-3 mt-3 p-4 rounded-md text-center text-th-fgd-3">
|
||||
Market insights are not available
|
||||
{t('swap:insights-not-available')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { Disclosure } from '@headlessui/react'
|
||||
import { ChevronDownIcon } from '@heroicons/react/outline'
|
||||
import { ReactNode } from 'hoist-non-react-statics/node_modules/@types/react'
|
||||
import { ChevronDownIcon } from '@heroicons/react/solid'
|
||||
import { ReactNode } from 'react'
|
||||
|
||||
export const Table = ({ children }) => (
|
||||
<table className="min-w-full divide-y divide-th-bkg-2">{children}</table>
|
||||
<table className="min-w-full">{children}</table>
|
||||
)
|
||||
|
||||
export const TrHead = ({ children }) => (
|
||||
|
@ -16,10 +16,8 @@ export const Th = ({ children }) => (
|
|||
</th>
|
||||
)
|
||||
|
||||
export const TrBody = ({ children, index }) => (
|
||||
<tr className={`${index % 2 === 0 ? `bg-[rgba(255,255,255,0.03)]` : ''}`}>
|
||||
{children}
|
||||
</tr>
|
||||
export const TrBody = ({ children }) => (
|
||||
<tr className="border-b border-th-bkg-4">{children}</tr>
|
||||
)
|
||||
|
||||
export const Td = ({
|
||||
|
@ -29,11 +27,7 @@ export const Td = ({
|
|||
children: ReactNode
|
||||
className?: string
|
||||
}) => (
|
||||
<td
|
||||
className={`px-4 py-3.5 whitespace-nowrap text-sm text-th-fgd-2 ${className}`}
|
||||
>
|
||||
{children}
|
||||
</td>
|
||||
<td className={`px-4 h-16 text-sm text-th-fgd-2 ${className}`}>{children}</td>
|
||||
)
|
||||
|
||||
type ExpandableRowProps = {
|
||||
|
|
|
@ -51,7 +51,7 @@ const Tabs: FunctionComponent<TabsProps> = ({
|
|||
`}
|
||||
style={{ width: `${100 / tabs.length}%`, maxWidth: '176px' }}
|
||||
>
|
||||
{t(tabName.toLowerCase().replace(' ', '-'))}
|
||||
{t(tabName.toLowerCase().replace(/\s/g, '-'))}
|
||||
{tabCount && tabCount.count > 0 ? (
|
||||
<Count count={tabCount.count} />
|
||||
) : null}
|
||||
|
|
|
@ -13,11 +13,11 @@ import { DEFAULT_MARKET_KEY, initialMarket } from './SettingsModal'
|
|||
import { useTranslation } from 'next-i18next'
|
||||
import Settings from './Settings'
|
||||
|
||||
// const StyledNewLabel = ({ children, ...props }) => (
|
||||
// <div style={{ fontSize: '0.5rem', marginLeft: '1px' }} {...props}>
|
||||
// {children}
|
||||
// </div>
|
||||
// )
|
||||
const StyledNewLabel = ({ children, ...props }) => (
|
||||
<div style={{ fontSize: '0.5rem', marginLeft: '1px' }} {...props}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
const TopBar = () => {
|
||||
const { t } = useTranslation('common')
|
||||
|
@ -56,6 +56,18 @@ const TopBar = () => {
|
|||
<MenuItem href="/account">{t('account')}</MenuItem>
|
||||
<MenuItem href="/borrow">{t('borrow')}</MenuItem>
|
||||
<MenuItem href="/stats">{t('stats')}</MenuItem>
|
||||
<div className="relative">
|
||||
<MenuItem href="/referral">
|
||||
{t('referrals')}
|
||||
<div>
|
||||
<div className="absolute flex items-center justify-center h-4 px-1.5 bg-gradient-to-br from-red-500 to-yellow-500 rounded-full -right-2 -top-3">
|
||||
<StyledNewLabel className="text-white uppercase">
|
||||
new
|
||||
</StyledNewLabel>
|
||||
</div>
|
||||
</div>
|
||||
</MenuItem>
|
||||
</div>
|
||||
<NavDropMenu
|
||||
menuTitle={t('more')}
|
||||
// linksArray: [name: string, href: string, isExternal: boolean]
|
||||
|
|
|
@ -232,12 +232,9 @@ const TradeHistoryTable = ({ numTrades }: { numTrades?: number }) => {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{paginatedData.map((trade: any, index) => {
|
||||
{paginatedData.map((trade: any) => {
|
||||
return (
|
||||
<TrBody
|
||||
index={index}
|
||||
key={`${trade.seqNum}${trade.marketName}`}
|
||||
>
|
||||
<TrBody key={`${trade.seqNum}${trade.marketName}`}>
|
||||
<Td className="!py-2 ">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -263,7 +260,9 @@ const TradeHistoryTable = ({ numTrades }: { numTrades?: number }) => {
|
|||
<Td className="!py-2 ">
|
||||
{formatUsdValue(trade.value)}
|
||||
</Td>
|
||||
<Td className="!py-2 ">{trade.liquidity}</Td>
|
||||
<Td className="!py-2 ">
|
||||
{t(trade.liquidity.toLowerCase())}
|
||||
</Td>
|
||||
<Td className="!py-2 ">
|
||||
{formatUsdValue(trade.feeCost)}
|
||||
</Td>
|
||||
|
@ -286,7 +285,7 @@ const TradeHistoryTable = ({ numTrades }: { numTrades?: number }) => {
|
|||
: trade.taker
|
||||
}`}
|
||||
>
|
||||
View Counterparty
|
||||
{t('view-counterparty')}
|
||||
</a>
|
||||
) : null}
|
||||
</Td>
|
||||
|
|
|
@ -270,6 +270,7 @@ const TVChartContainer = () => {
|
|||
const bidInfo =
|
||||
useMangoStore.getState().accountInfos[marketConfig.bidsKey.toString()]
|
||||
const wallet = useMangoStore.getState().wallet.current
|
||||
const referrerPk = useMangoStore.getState().referrerPk
|
||||
|
||||
if (!wallet || !mangoGroup || !mangoAccount || !market) return
|
||||
|
||||
|
@ -311,7 +312,9 @@ const TVChartContainer = () => {
|
|||
order.size,
|
||||
orderType,
|
||||
0,
|
||||
order.side === 'buy' ? askInfo : bidInfo
|
||||
order.side === 'buy' ? askInfo : bidInfo,
|
||||
false,
|
||||
referrerPk ? referrerPk : undefined
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -43,6 +43,7 @@ export default function AccountBorrows() {
|
|||
const [showDepositModal, setShowDepositModal] = useState(false)
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.sm : false
|
||||
const canWithdraw = mangoAccount?.owner.equals(wallet.publicKey)
|
||||
|
||||
const handleCloseWithdraw = useCallback(() => {
|
||||
setShowBorrowModal(false)
|
||||
|
@ -91,7 +92,7 @@ export default function AccountBorrows() {
|
|||
<tbody>
|
||||
{balances
|
||||
.filter((assets) => assets.borrows.gt(ZERO_I80F48))
|
||||
.map((asset, i) => {
|
||||
.map((asset) => {
|
||||
const token = getTokenBySymbol(
|
||||
mangoConfig,
|
||||
asset.symbol
|
||||
|
@ -100,7 +101,7 @@ export default function AccountBorrows() {
|
|||
token.mintKey
|
||||
)
|
||||
return (
|
||||
<TrBody index={i} key={tokenIndex}>
|
||||
<TrBody key={tokenIndex}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -155,7 +156,11 @@ export default function AccountBorrows() {
|
|||
handleShowBorrow(asset.symbol)
|
||||
}
|
||||
className="ml-3 text-xs pt-0 pb-0 h-8 pl-3 pr-3"
|
||||
disabled={!connected || loadingMangoAccount}
|
||||
disabled={
|
||||
!connected ||
|
||||
loadingMangoAccount ||
|
||||
!canWithdraw
|
||||
}
|
||||
>
|
||||
{t('borrow')}
|
||||
</Button>
|
||||
|
@ -257,7 +262,11 @@ export default function AccountBorrows() {
|
|||
handleShowBorrow(asset.symbol)
|
||||
}
|
||||
className="text-xs pt-0 pb-0 h-8 w-full"
|
||||
disabled={!connected || loadingMangoAccount}
|
||||
disabled={
|
||||
!connected ||
|
||||
loadingMangoAccount ||
|
||||
!canWithdraw
|
||||
}
|
||||
>
|
||||
{t('borrow')}
|
||||
</Button>
|
||||
|
@ -308,7 +317,7 @@ export default function AccountBorrows() {
|
|||
{mangoConfig.tokens.map((token, i) => {
|
||||
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
|
||||
return (
|
||||
<TrBody index={i} key={`${token.symbol}${i}`}>
|
||||
<TrBody key={`${token.symbol}${i}`}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -388,7 +397,11 @@ export default function AccountBorrows() {
|
|||
<Button
|
||||
onClick={() => handleShowBorrow(token.symbol)}
|
||||
className="text-xs pt-0 pb-0 h-8 pl-3 pr-3 ml-3"
|
||||
disabled={!connected || loadingMangoAccount}
|
||||
disabled={
|
||||
!connected ||
|
||||
loadingMangoAccount ||
|
||||
!canWithdraw
|
||||
}
|
||||
>
|
||||
{t('borrow')}
|
||||
</Button>
|
||||
|
@ -496,7 +509,11 @@ export default function AccountBorrows() {
|
|||
<Button
|
||||
onClick={() => handleShowBorrow(token.symbol)}
|
||||
className="text-xs pt-0 pb-0 h-8 w-full"
|
||||
disabled={!connected || loadingMangoAccount}
|
||||
disabled={
|
||||
!connected ||
|
||||
loadingMangoAccount ||
|
||||
!canWithdraw
|
||||
}
|
||||
>
|
||||
{t('borrow')}
|
||||
</Button>
|
||||
|
|
|
@ -239,7 +239,7 @@ const AccountFunding = () => {
|
|||
</thead>
|
||||
<tbody>
|
||||
{fundingStats.length === 0 ? (
|
||||
<TrBody index={0}>
|
||||
<TrBody>
|
||||
<td colSpan={4}>
|
||||
<div className="flex">
|
||||
<div className="mx-auto py-4 text-th-fgd-3">
|
||||
|
@ -249,9 +249,9 @@ const AccountFunding = () => {
|
|||
</td>
|
||||
</TrBody>
|
||||
) : (
|
||||
fundingStats.map(([symbol, stats], index) => {
|
||||
fundingStats.map(([symbol, stats]) => {
|
||||
return (
|
||||
<TrBody index={index} key={symbol}>
|
||||
<TrBody key={symbol}>
|
||||
<Td className="w-1/2">
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -373,12 +373,12 @@ const AccountFunding = () => {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{paginatedData.map((stat, index) => {
|
||||
{paginatedData.map((stat) => {
|
||||
// @ts-ignore
|
||||
const utc = dayjs.utc(stat.time).format()
|
||||
|
||||
return (
|
||||
<TrBody index={index} key={stat.time}>
|
||||
<TrBody key={stat.time}>
|
||||
<Td className="w-1/2">
|
||||
{dayjs(utc).format('DD/MM/YY, h:mma')}
|
||||
</Td>
|
||||
|
|
|
@ -352,7 +352,7 @@ const LiquidationHistoryTable = ({ history, view }) => {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map(({ activity_details, activity_type }, index) => {
|
||||
{items.map(({ activity_details, activity_type }) => {
|
||||
let perpMarket: PerpMarket
|
||||
if (activity_type.includes('perp')) {
|
||||
const symbol = activity_details.perp_market.split('-')[0]
|
||||
|
@ -376,7 +376,7 @@ const LiquidationHistoryTable = ({ history, view }) => {
|
|||
const lostDecimals = assetLost.symbol === 'SOL' ? 9 : 6
|
||||
const gainedDecimals = assetGained.symbol === 'SOL' ? 9 : 6
|
||||
return (
|
||||
<TrBody index={index} key={activity_details.signature}>
|
||||
<TrBody key={activity_details.signature}>
|
||||
<Td>
|
||||
<div>{date.toLocaleDateString()}</div>
|
||||
<div className="text-xs text-th-fgd-3">
|
||||
|
@ -531,10 +531,10 @@ const HistoryTable = ({ history, view }) => {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{items.map((activity_details: any, index) => {
|
||||
{items.map((activity_details: any) => {
|
||||
const date = new Date(activity_details.block_datetime)
|
||||
return (
|
||||
<TrBody index={index} key={activity_details.signature}>
|
||||
<TrBody key={activity_details.signature}>
|
||||
<Td>
|
||||
<div>{date.toLocaleDateString()}</div>
|
||||
<div className="text-xs text-th-fgd-3">
|
||||
|
|
|
@ -311,7 +311,7 @@ const AccountInterest = () => {
|
|||
</thead>
|
||||
<tbody>
|
||||
{interestStats.length === 0 ? (
|
||||
<TrBody index={0}>
|
||||
<TrBody>
|
||||
<td colSpan={4}>
|
||||
<div className="bg-th-bkg-3 flex rounded-md text-th-fgd-3">
|
||||
<div className="mx-auto py-4">{t('no-interest')}</div>
|
||||
|
@ -319,13 +319,13 @@ const AccountInterest = () => {
|
|||
</td>
|
||||
</TrBody>
|
||||
) : (
|
||||
interestStats.map(([symbol, stats], index) => {
|
||||
interestStats.map(([symbol, stats]) => {
|
||||
const decimals = getTokenBySymbol(
|
||||
groupConfig,
|
||||
symbol
|
||||
).decimals
|
||||
return (
|
||||
<TrBody index={index} key={symbol}>
|
||||
<TrBody key={symbol}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -541,11 +541,11 @@ const AccountInterest = () => {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{paginatedData.map((stat, index) => {
|
||||
{paginatedData.map((stat) => {
|
||||
// @ts-ignore
|
||||
const utc = dayjs.utc(stat.time).format()
|
||||
return (
|
||||
<TrBody index={index} key={stat.time}>
|
||||
<TrBody key={stat.time}>
|
||||
<Td className="w-1/3">
|
||||
{dayjs(utc).format('DD/MM/YY, h:mma')}
|
||||
</Td>
|
||||
|
|
|
@ -167,11 +167,11 @@ const AccountPerformance = () => {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{paginatedData.map((stat, index) => {
|
||||
{paginatedData.map((stat) => {
|
||||
// @ts-ignore
|
||||
const utc = dayjs.utc(stat.time).format()
|
||||
return (
|
||||
<TrBody index={index} key={stat.time}>
|
||||
<TrBody key={stat.time}>
|
||||
<Td>{dayjs(utc).format('DD/MM/YY, h:mma')}</Td>
|
||||
<Td>{stat.account_equity.toFixed(6 + 1)}</Td>
|
||||
<Td>{stat.pnl.toFixed(6 + 1)}</Td>
|
||||
|
|
|
@ -168,8 +168,8 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{latestStats.map((stat, index) => (
|
||||
<TrBody key={stat.name} index={index}>
|
||||
{latestStats.map((stat) => (
|
||||
<TrBody key={stat.name}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -248,8 +248,8 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{latestStats.map((stat, index) => (
|
||||
<TrBody key={stat.name} index={index}>
|
||||
{latestStats.map((stat) => (
|
||||
<TrBody key={stat.name}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
@ -297,8 +297,8 @@ export default function StatsTotals({ latestStats, stats }) {
|
|||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{latestStats.map((stat, index) => (
|
||||
<TrBody key={stat.name} index={index}>
|
||||
{latestStats.map((stat) => (
|
||||
<TrBody key={stat.name}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
|
|
|
@ -569,6 +569,7 @@ export default function AdvancedTradeForm({
|
|||
const bidInfo =
|
||||
useMangoStore.getState().accountInfos[marketConfig.bidsKey.toString()]
|
||||
const wallet = useMangoStore.getState().wallet.current
|
||||
const referrerPk = useMangoStore.getState().referrerPk
|
||||
|
||||
if (!wallet || !mangoGroup || !mangoAccount || !market) return
|
||||
|
||||
|
@ -667,7 +668,8 @@ export default function AdvancedTradeForm({
|
|||
perpOrderType,
|
||||
Date.now(),
|
||||
side === 'buy' ? askInfo : bidInfo, // book side used for ConsumeEvents
|
||||
reduceOnly
|
||||
reduceOnly,
|
||||
referrerPk ? referrerPk : undefined
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,9 +123,9 @@ export default function SimpleTradeForm({ initLeverage }) {
|
|||
const setTriggerPrice = (price) =>
|
||||
set((s) => {
|
||||
if (!Number.isNaN(parseFloat(price))) {
|
||||
s.tradeForm.tripperPrice = parseFloat(price)
|
||||
s.tradeForm.triggerPrice = parseFloat(price)
|
||||
} else {
|
||||
s.tradeForm.tripperPrice = price
|
||||
s.tradeForm.triggerPrice = price
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -13,13 +13,13 @@ import {
|
|||
import FloatingElement from '../FloatingElement'
|
||||
|
||||
export default function TradeForm() {
|
||||
const [showAdvancedFrom, setShowAdvancedForm] = useState(true)
|
||||
const [showAdvancedForm, setShowAdvancedForm] = useState(true)
|
||||
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const connected = useMangoStore((s) => s.wallet.connected)
|
||||
|
||||
const handleFormChange = () => {
|
||||
setShowAdvancedForm(!showAdvancedFrom)
|
||||
setShowAdvancedForm(!showAdvancedForm)
|
||||
}
|
||||
|
||||
const initLeverage = useMemo(() => {
|
||||
|
@ -33,8 +33,8 @@ export default function TradeForm() {
|
|||
|
||||
return (
|
||||
<FlipCard>
|
||||
<FlipCardInner flip={showAdvancedFrom}>
|
||||
{showAdvancedFrom ? (
|
||||
<FlipCardInner flip={showAdvancedForm}>
|
||||
{showAdvancedForm ? (
|
||||
<FlipCardFront>
|
||||
<FloatingElement className="h-full px-1 py-0 md:px-4 md:py-4 fadein-floating-element">
|
||||
{/* <div className={`${!connected ? 'filter blur-sm' : ''}`}> */}
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { i80f48ToPercent } from '../utils/index'
|
||||
import { sumBy } from 'lodash'
|
||||
import { I80F48 } from '@blockworks-foundation/mango-client/lib/src/fixednum'
|
||||
import { I80F48 } from '@blockworks-foundation/mango-client'
|
||||
import useMangoAccount from './useMangoAccount'
|
||||
|
||||
export function useBalances(): Balances[] {
|
||||
|
|
|
@ -1,24 +1,34 @@
|
|||
import {
|
||||
CENTIBPS_PER_UNIT,
|
||||
getMarketIndexBySymbol,
|
||||
getSpotMarketByBaseSymbol,
|
||||
PerpMarket,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
import useSrmAccount from '../hooks/useSrmAccount'
|
||||
import { mangoGroupConfigSelector } from '../stores/selectors'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
|
||||
export default function useFees() {
|
||||
export default function useFees(): { makerFee: number; takerFee: number } {
|
||||
const { rates } = useSrmAccount()
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
const mangoGroupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
|
||||
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
|
||||
const market = useMangoStore((s) => s.selectedMarket.current)
|
||||
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
|
||||
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
|
||||
const groupConfig = useMangoStore(mangoGroupConfigSelector)
|
||||
|
||||
const marketIndex = getMarketIndexBySymbol(
|
||||
mangoGroupConfig,
|
||||
marketConfig.baseSymbol
|
||||
)
|
||||
|
||||
if (!mangoGroup) return {}
|
||||
if (!mangoGroup || !market) return { makerFee: 0, takerFee: 0 }
|
||||
|
||||
let takerFee: number, makerFee: number
|
||||
let discount = 0
|
||||
let refSurcharge = 0
|
||||
|
||||
let takerFee, makerFee
|
||||
if (market instanceof PerpMarket) {
|
||||
takerFee = parseFloat(
|
||||
mangoGroup.perpMarkets[marketIndex].takerFee.toFixed()
|
||||
|
@ -26,10 +36,40 @@ export default function useFees() {
|
|||
makerFee = parseFloat(
|
||||
mangoGroup.perpMarkets[marketIndex].makerFee.toFixed()
|
||||
)
|
||||
// @ts-ignore
|
||||
refSurcharge = mangoGroup.refSurchargeCentibps / CENTIBPS_PER_UNIT
|
||||
// @ts-ignore
|
||||
const refShare = mangoGroup.refShareCentibps / CENTIBPS_PER_UNIT
|
||||
|
||||
const mngoConfig = getSpotMarketByBaseSymbol(groupConfig, 'MNGO')
|
||||
const mngoRequired =
|
||||
mangoGroup.refMngoRequired.toNumber() /
|
||||
Math.pow(10, mngoConfig.baseDecimals)
|
||||
|
||||
if (mangoAccount) {
|
||||
const mngoBalance = mangoAccount
|
||||
.getUiDeposit(
|
||||
mangoCache.rootBankCache[mngoConfig.marketIndex],
|
||||
mangoGroup,
|
||||
mngoConfig.marketIndex
|
||||
)
|
||||
.toNumber()
|
||||
|
||||
const hasReferrer = useMangoStore.getState().referrerPk
|
||||
|
||||
if (mngoBalance >= mngoRequired) {
|
||||
discount = refSurcharge
|
||||
} else {
|
||||
discount = hasReferrer ? refSurcharge - refShare : 0
|
||||
}
|
||||
}
|
||||
} else {
|
||||
takerFee = rates.takerWithRebate
|
||||
makerFee = rates.maker
|
||||
}
|
||||
|
||||
return { makerFee, takerFee }
|
||||
return {
|
||||
makerFee: makerFee,
|
||||
takerFee: takerFee + refSurcharge - discount,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect } from 'react'
|
||||
import { AccountInfo } from '@solana/web3.js'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import { AccountInfo, PublicKey } from '@solana/web3.js'
|
||||
import useMangoStore, { programId } from '../stores/useMangoStore'
|
||||
import useInterval from './useInterval'
|
||||
import { Orderbook as SpotOrderBook, Market } from '@project-serum/serum'
|
||||
import {
|
||||
|
@ -8,6 +8,8 @@ import {
|
|||
BookSideLayout,
|
||||
MangoAccountLayout,
|
||||
PerpMarket,
|
||||
ReferrerMemory,
|
||||
ReferrerMemoryLayout,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
import {
|
||||
actionsSelector,
|
||||
|
@ -91,6 +93,7 @@ const useHydrateStore = () => {
|
|||
})
|
||||
}, [marketConfig, markets, setMangoStore])
|
||||
|
||||
// watch selected Mango Account for changes
|
||||
useEffect(() => {
|
||||
if (!mangoAccount) return
|
||||
console.log('in mango account WS useEffect')
|
||||
|
@ -132,6 +135,37 @@ const useHydrateStore = () => {
|
|||
}
|
||||
}, [mangoAccount])
|
||||
|
||||
// fetch referrer for selected Mango Account
|
||||
useEffect(() => {
|
||||
if (mangoAccount) {
|
||||
const fetchReferrer = async () => {
|
||||
try {
|
||||
const [referrerMemoryPk] = await PublicKey.findProgramAddress(
|
||||
[
|
||||
mangoAccount.publicKey.toBytes(),
|
||||
new Buffer('ReferrerMemory', 'utf-8'),
|
||||
],
|
||||
programId
|
||||
)
|
||||
|
||||
const info = await connection.getAccountInfo(referrerMemoryPk)
|
||||
if (info) {
|
||||
const decodedReferrer = ReferrerMemoryLayout.decode(info.data)
|
||||
const referrerMemory = new ReferrerMemory(decodedReferrer)
|
||||
|
||||
setMangoStore((state) => {
|
||||
state.referrerPk = referrerMemory.referrerMangoAccount
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Unable to fetch referrer', e)
|
||||
}
|
||||
}
|
||||
|
||||
fetchReferrer()
|
||||
}
|
||||
}, [mangoAccount])
|
||||
|
||||
// hydrate orderbook with all markets in mango group
|
||||
useEffect(() => {
|
||||
let previousBidInfo: AccountInfo<Buffer> | null = null
|
||||
|
|
|
@ -88,9 +88,12 @@ export const useTradeHistory = (
|
|||
const fills = useMangoStore(fillsSelector)
|
||||
const mangoAccount = useMangoStore(mangoAccountSelector)
|
||||
const selectedMangoGroup = useMangoStore(mangoGroupSelector)
|
||||
let tradeHistory = useMangoStore(tradeHistorySelector)
|
||||
const tradeHistory = useMangoStore(tradeHistorySelector)
|
||||
|
||||
if (!mangoAccount || !selectedMangoGroup) return null
|
||||
|
||||
let combinedTradeHistory = [...tradeHistory.spot, ...tradeHistory.perp]
|
||||
|
||||
const openOrdersAccount =
|
||||
mangoAccount.spotOpenOrdersAccounts[marketConfig.marketIndex]
|
||||
|
||||
|
@ -113,7 +116,7 @@ export const useTradeHistory = (
|
|||
if (mangoAccountFills && mangoAccountFills.length > 0) {
|
||||
const newFills = mangoAccountFills.filter(
|
||||
(fill) =>
|
||||
!tradeHistory.flat().find((t) => {
|
||||
!combinedTradeHistory.flat().find((t) => {
|
||||
if (t.orderId) {
|
||||
return t.orderId === fill.orderId?.toString()
|
||||
} else {
|
||||
|
@ -121,15 +124,15 @@ export const useTradeHistory = (
|
|||
}
|
||||
})
|
||||
)
|
||||
const newTradeHistory = [...newFills, ...tradeHistory]
|
||||
const newTradeHistory = [...newFills, ...combinedTradeHistory]
|
||||
if (newFills.length > 0 && newTradeHistory.length !== allTrades.length) {
|
||||
tradeHistory = newTradeHistory
|
||||
combinedTradeHistory = newTradeHistory
|
||||
}
|
||||
}
|
||||
|
||||
const formattedTradeHistory = formatTradeHistory(
|
||||
mangoAccount.publicKey,
|
||||
tradeHistory
|
||||
combinedTradeHistory
|
||||
)
|
||||
if (opts.excludePerpLiquidations) {
|
||||
return formattedTradeHistory.filter((t) => !('liqor' in t))
|
||||
|
|
|
@ -90,7 +90,7 @@ export default function useWallet() {
|
|||
state.wallet.connected = false
|
||||
state.mangoAccounts = []
|
||||
state.selectedMangoAccount.current = null
|
||||
state.tradeHistory = []
|
||||
state.tradeHistory = { spot: [], perp: [] }
|
||||
})
|
||||
notify({
|
||||
type: 'info',
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@blockworks-foundation/mango-client": "^3.3.7",
|
||||
"@blockworks-foundation/mango-client": "^3.3.15",
|
||||
"@headlessui/react": "^1.2.0",
|
||||
"@heroicons/react": "^1.0.0",
|
||||
"@jup-ag/react-hook": "^1.0.0-beta.4",
|
||||
|
@ -31,6 +31,7 @@
|
|||
"@sentry/nextjs": "^6.17.4",
|
||||
"@solana/web3.js": "^1.31.0",
|
||||
"@solflare-wallet/pfp": "^0.0.6",
|
||||
"@solflare-wallet/sdk": "^1.0.10",
|
||||
"@tippyjs/react": "^4.2.5",
|
||||
"big.js": "^6.1.1",
|
||||
"bn.js": "^5.2.0",
|
||||
|
|
|
@ -18,6 +18,17 @@ import ErrorBoundary from '../components/ErrorBoundary'
|
|||
import GlobalNotification from '../components/GlobalNotification'
|
||||
import { useOpenOrders } from '../hooks/useOpenOrders'
|
||||
import usePerpPositions from '../hooks/usePerpPositions'
|
||||
import { useEffect } from 'react'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import {
|
||||
connectionSelector,
|
||||
mangoClientSelector,
|
||||
mangoGroupSelector,
|
||||
} from '../stores/selectors'
|
||||
import {
|
||||
ReferrerIdRecordLayout,
|
||||
ReferrerIdRecord,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
|
||||
const MangoStoreUpdater = () => {
|
||||
useHydrateStore()
|
||||
|
@ -39,6 +50,47 @@ const PerpPositionsStoreUpdater = () => {
|
|||
return null
|
||||
}
|
||||
|
||||
const FetchReferrer = () => {
|
||||
const setMangoStore = useMangoStore((s) => s.set)
|
||||
const mangoClient = useMangoStore(mangoClientSelector)
|
||||
const mangoGroup = useMangoStore(mangoGroupSelector)
|
||||
const connection = useMangoStore(connectionSelector)
|
||||
const router = useRouter()
|
||||
const { query } = router
|
||||
|
||||
useEffect(() => {
|
||||
const storeReferrer = async () => {
|
||||
if (query.ref && mangoGroup) {
|
||||
let referrerPk
|
||||
if (query.ref.length === 44) {
|
||||
referrerPk = new PublicKey(query.ref)
|
||||
} else {
|
||||
const { referrerPda } = await mangoClient.getReferrerPda(
|
||||
mangoGroup,
|
||||
query.ref as string
|
||||
)
|
||||
console.log('in App referrerPda', referrerPda)
|
||||
const info = await connection.getAccountInfo(referrerPda)
|
||||
console.log('in App referrerPda info', info)
|
||||
if (info) {
|
||||
const decoded = ReferrerIdRecordLayout.decode(info.data)
|
||||
const referrerRecord = new ReferrerIdRecord(decoded)
|
||||
referrerPk = referrerRecord.referrerMangoAccount
|
||||
}
|
||||
}
|
||||
console.log('in App referrerPk from url is:', referrerPk)
|
||||
setMangoStore((state) => {
|
||||
state.referrerPk = referrerPk
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
storeReferrer()
|
||||
}, [query, mangoGroup])
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const PageTitle = () => {
|
||||
const router = useRouter()
|
||||
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
|
||||
|
@ -109,6 +161,7 @@ function App({ Component, pageProps }) {
|
|||
<WalletStoreUpdater />
|
||||
<OpenOrdersStoreUpdater />
|
||||
<PerpPositionsStoreUpdater />
|
||||
<FetchReferrer />
|
||||
</ErrorBoundary>
|
||||
|
||||
<ThemeProvider defaultTheme="Mango">
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
LinkIcon,
|
||||
PencilIcon,
|
||||
TrashIcon,
|
||||
UsersIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import useMangoStore, { serumProgramId } from '../stores/useMangoStore'
|
||||
import PageBodyContainer from '../components/PageBodyContainer'
|
||||
|
@ -39,11 +40,16 @@ import {
|
|||
walletConnectedSelector,
|
||||
} from '../stores/selectors'
|
||||
import CreateAlertModal from '../components/CreateAlertModal'
|
||||
import DelegateModal from '../components/DelegateModal'
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common', 'close-account'])),
|
||||
...(await serverSideTranslations(locale, [
|
||||
'common',
|
||||
'close-account',
|
||||
'delegate',
|
||||
])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
|
@ -59,11 +65,12 @@ const TABS = [
|
|||
]
|
||||
|
||||
export default function Account() {
|
||||
const { t } = useTranslation(['common', 'close-account'])
|
||||
const { t } = useTranslation(['common', 'close-account', 'delegate'])
|
||||
const [showAccountsModal, setShowAccountsModal] = useState(false)
|
||||
const [showNameModal, setShowNameModal] = useState(false)
|
||||
const [showCloseAccountModal, setShowCloseAccountModal] = useState(false)
|
||||
const [showAlertsModal, setShowAlertsModal] = useState(false)
|
||||
const [showDelegateModal, setShowDelegateModal] = useState(false)
|
||||
const [isCopied, setIsCopied] = useState(false)
|
||||
const [resetOnLeave, setResetOnLeave] = useState(false)
|
||||
const connected = useMangoStore(walletConnectedSelector)
|
||||
|
@ -80,6 +87,8 @@ export default function Account() {
|
|||
const isMobile = width ? width < breakpoints.sm : false
|
||||
const router = useRouter()
|
||||
const { pubkey } = router.query
|
||||
const isDelegatedAccount = !mangoAccount?.owner.equals(wallet?.publicKey)
|
||||
const buttonCols = isDelegatedAccount ? 2 : 4
|
||||
|
||||
const handleCloseAlertModal = useCallback(() => {
|
||||
setShowAlertsModal(false)
|
||||
|
@ -97,6 +106,10 @@ export default function Account() {
|
|||
setShowCloseAccountModal(false)
|
||||
}, [])
|
||||
|
||||
const handleCloseDelegateModal = useCallback(() => {
|
||||
setShowDelegateModal(false)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
async function loadUnownedMangoAccount() {
|
||||
try {
|
||||
|
@ -198,16 +211,20 @@ export default function Account() {
|
|||
</div>
|
||||
</div>
|
||||
{!pubkey ? (
|
||||
<div className="grid grid-cols-3 grid-rows-1 gap-2">
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center pt-0 pb-0 h-8 pl-3 pr-3 text-xs"
|
||||
onClick={() => setShowCloseAccountModal(true)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<TrashIcon className="h-4 w-4 mr-1.5" />
|
||||
{t('close-account:close-account')}
|
||||
</div>
|
||||
</Button>
|
||||
<div
|
||||
className={`grid grid-cols-${buttonCols} grid-rows-1 gap-2 auto-cols-min`}
|
||||
>
|
||||
{!isDelegatedAccount && (
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center pt-0 pb-0 h-8 pl-3 pr-3 text-xs"
|
||||
onClick={() => setShowCloseAccountModal(true)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<TrashIcon className="h-4 w-4 mr-1.5" />
|
||||
{t('close-account:close-account')}
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center pt-0 pb-0 h-8 pl-3 pr-3 text-xs"
|
||||
onClick={() => setShowAlertsModal(true)}
|
||||
|
@ -217,6 +234,17 @@ export default function Account() {
|
|||
Alerts
|
||||
</div>
|
||||
</Button>
|
||||
{!isDelegatedAccount && (
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center pt-0 pb-0 h-8 pl-3 pr-3 text-xs"
|
||||
onClick={() => setShowDelegateModal(true)}
|
||||
>
|
||||
<div className="flex items-center">
|
||||
<UsersIcon className="h-4 w-4 mr-1.5" />
|
||||
{t('delegate:set-delegate')}
|
||||
</div>
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
className="col-span-1 flex items-center justify-center pt-0 pb-0 h-8 pl-3 pr-3 text-xs"
|
||||
onClick={() => setShowAccountsModal(true)}
|
||||
|
@ -318,7 +346,6 @@ export default function Account() {
|
|||
) : null}
|
||||
{showCloseAccountModal ? (
|
||||
<CloseAccountModal
|
||||
accountName={mangoAccount?.name}
|
||||
isOpen={showCloseAccountModal}
|
||||
onClose={handleCloseCloseAccountModal}
|
||||
/>
|
||||
|
@ -329,6 +356,13 @@ export default function Account() {
|
|||
onClose={handleCloseAlertModal}
|
||||
/>
|
||||
) : null}
|
||||
{showDelegateModal ? (
|
||||
<DelegateModal
|
||||
delegate={mangoAccount?.delegate}
|
||||
isOpen={showDelegateModal}
|
||||
onClose={handleCloseDelegateModal}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -109,7 +109,9 @@ const PerpMarket = () => {
|
|||
state.selectedMarket.config = newMarket
|
||||
state.tradeForm.price =
|
||||
state.tradeForm.tradeType === 'Limit'
|
||||
? mangoGroup.getPrice(marketIndex, mangoCache).toFixed(2)
|
||||
? parseFloat(
|
||||
mangoGroup.getPrice(marketIndex, mangoCache).toFixed(2)
|
||||
)
|
||||
: ''
|
||||
}
|
||||
})
|
||||
|
|
|
@ -0,0 +1,558 @@
|
|||
import { useEffect, useState, useCallback } from 'react'
|
||||
import PageBodyContainer from '../components/PageBodyContainer'
|
||||
import TopBar from '../components/TopBar'
|
||||
import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
|
||||
import useMangoStore from '../stores/useMangoStore'
|
||||
import {
|
||||
mangoCacheSelector,
|
||||
mangoClientSelector,
|
||||
mangoGroupConfigSelector,
|
||||
mangoGroupSelector,
|
||||
walletSelector,
|
||||
} from '../stores/selectors'
|
||||
import { IconButton } from '../components/Button'
|
||||
import { abbreviateAddress, copyToClipboard } from '../utils'
|
||||
import { notify } from '../utils/notifications'
|
||||
import {
|
||||
getMarketIndexBySymbol,
|
||||
ReferrerIdRecord,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import EmptyState from '../components/EmptyState'
|
||||
import {
|
||||
CheckIcon,
|
||||
CurrencyDollarIcon,
|
||||
DuplicateIcon,
|
||||
LinkIcon,
|
||||
} from '@heroicons/react/outline'
|
||||
import { MngoMonoIcon } from '../components/icons'
|
||||
import Link from 'next/link'
|
||||
import { Table, Td, Th, TrBody, TrHead } from '../components/TableElements'
|
||||
import dayjs from 'dayjs'
|
||||
import AccountsModal from '../components/AccountsModal'
|
||||
import { useViewport } from '../hooks/useViewport'
|
||||
import { breakpoints } from '../components/TradePageGrid'
|
||||
import { ExpandableRow } from '../components/TableElements'
|
||||
import MobileTableHeader from '../components/mobile/MobileTableHeader'
|
||||
import Input from '../components/Input'
|
||||
import InlineNotification from '../components/InlineNotification'
|
||||
import useMangoAccount from '../hooks/useMangoAccount'
|
||||
|
||||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
// Will be passed to the page component as props
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const referralHistory = []
|
||||
// [
|
||||
// {
|
||||
// time: '2022-02-09T19:28:59Z',
|
||||
// referralLink: 'test2',
|
||||
// referee: '22JS1jkvkLcdxhHo1LpWXUh6sTErkt54j1YaszYWZoCi',
|
||||
// fee: 0.22,
|
||||
// },
|
||||
// {
|
||||
// time: '2022-02-08T19:28:59Z',
|
||||
// referralLink: 'test2',
|
||||
// referee: '22JS1jkvkLcdxhHo1LpWXUh6sTErkt54j1YaszYWZoCi',
|
||||
// fee: 0.21,
|
||||
// },
|
||||
// {
|
||||
// time: '2022-02-07T19:28:59Z',
|
||||
// referralLink: 'test2',
|
||||
// referee: '22JS1jkvkLcdxhHo1LpWXUh6sTErkt54j1YaszYWZoCi',
|
||||
// fee: 0.15,
|
||||
// },
|
||||
// ]
|
||||
|
||||
const ProgramDetails = () => {
|
||||
return (
|
||||
<>
|
||||
<h2 className="mb-4">Program Details</h2>
|
||||
<ul className="list-disc pl-3">
|
||||
<li>
|
||||
Your referral code is automatically applied when a user creates a
|
||||
Mango Account using your link.
|
||||
</li>
|
||||
<li>
|
||||
When any of your referrals trade Mango Perps, you earn 16% of their
|
||||
trade fees.
|
||||
</li>
|
||||
<li>
|
||||
Plus, for using your link they get a 4% discount off their Mango Perp
|
||||
fees.
|
||||
</li>
|
||||
<li>
|
||||
You must have at least 10,000 MNGO in your Mango Account to qualify
|
||||
for generating referrals and earning referral rewards.
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function Referral() {
|
||||
const { t } = useTranslation('common')
|
||||
const mangoGroup = useMangoStore(mangoGroupSelector)
|
||||
const mangoCache = useMangoStore(mangoCacheSelector)
|
||||
const { mangoAccount } = useMangoAccount()
|
||||
const groupConfig = useMangoStore(mangoGroupConfigSelector)
|
||||
const client = useMangoStore(mangoClientSelector)
|
||||
const wallet = useMangoStore(walletSelector)
|
||||
const connected = useMangoStore((s) => s.wallet.connected)
|
||||
|
||||
const [customRefLinkInput, setCustomRefLinkInput] = useState('')
|
||||
const [existingCustomRefLinks, setexistingCustomRefLinks] = useState<
|
||||
ReferrerIdRecord[]
|
||||
>([])
|
||||
const [hasCopied, setHasCopied] = useState(null)
|
||||
const [showAccountsModal, setShowAccountsModal] = useState(false)
|
||||
// const [hasReferrals] = useState(false) // Placeholder to show/hide users referral stats
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [inputError, setInputError] = useState('')
|
||||
const { width } = useViewport()
|
||||
const isMobile = width ? width < breakpoints.sm : false
|
||||
|
||||
const fetchCustomReferralLinks = useCallback(async () => {
|
||||
setLoading(true)
|
||||
const referrerIds = await client.getReferrerIdsForMangoAccount(mangoAccount)
|
||||
|
||||
setexistingCustomRefLinks(referrerIds)
|
||||
setLoading(false)
|
||||
}, [mangoAccount])
|
||||
|
||||
useEffect(() => {
|
||||
if (mangoAccount) {
|
||||
fetchCustomReferralLinks()
|
||||
}
|
||||
}, [mangoAccount])
|
||||
|
||||
useEffect(() => {
|
||||
let timer
|
||||
if (hasCopied) {
|
||||
timer = setTimeout(() => setHasCopied(null), 1000)
|
||||
}
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [hasCopied])
|
||||
|
||||
const onChangeRefIdInput = (value) => {
|
||||
const id = value.replace(/ /g, '')
|
||||
setCustomRefLinkInput(id)
|
||||
if (id.length > 32) {
|
||||
setInputError('Referral IDs must be less then 33 characters')
|
||||
} else {
|
||||
setInputError('')
|
||||
}
|
||||
}
|
||||
|
||||
const validateRefIdInput = () => {
|
||||
if (customRefLinkInput.length >= 33) {
|
||||
setInputError('Referral IDs must be less then 33 characters')
|
||||
}
|
||||
if (customRefLinkInput.length === 0) {
|
||||
setInputError('Enter a referral ID')
|
||||
}
|
||||
}
|
||||
|
||||
const submitRefLink = async () => {
|
||||
if (!inputError) {
|
||||
try {
|
||||
const txid = await client.registerReferrerId(
|
||||
mangoGroup,
|
||||
mangoAccount,
|
||||
wallet,
|
||||
customRefLinkInput
|
||||
)
|
||||
notify({
|
||||
txid,
|
||||
title: 'Custom referral link created',
|
||||
})
|
||||
fetchCustomReferralLinks()
|
||||
} catch (e) {
|
||||
notify({
|
||||
type: 'error',
|
||||
title: 'Unable to create referral link',
|
||||
description: e.message,
|
||||
txid: e.txid,
|
||||
})
|
||||
}
|
||||
} else return
|
||||
}
|
||||
|
||||
const handleCopyLink = (link, index) => {
|
||||
copyToClipboard(link)
|
||||
setHasCopied(index)
|
||||
}
|
||||
|
||||
const mngoIndex = getMarketIndexBySymbol(groupConfig, 'MNGO')
|
||||
|
||||
const hasRequiredMngo =
|
||||
mangoGroup && mangoAccount
|
||||
? mangoAccount
|
||||
.getUiDeposit(
|
||||
mangoCache.rootBankCache[mngoIndex],
|
||||
mangoGroup,
|
||||
mngoIndex
|
||||
)
|
||||
.toNumber() >= 10000
|
||||
: false
|
||||
const hasCustomRefLinks =
|
||||
existingCustomRefLinks && existingCustomRefLinks.length > 0
|
||||
|
||||
return (
|
||||
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
|
||||
<TopBar />
|
||||
<PageBodyContainer>
|
||||
<div className="py-4 md:pb-4 md:pt-10">
|
||||
<h1 className={`mb-1 text-th-fgd-1 text-2xl font-semibold`}>
|
||||
Sow the Mango Seed
|
||||
</h1>
|
||||
<div className="flex flex-col sm:flex-row items-start">
|
||||
<p className="mb-0 mr-2 text-th-fgd-1">
|
||||
Earn 16% of the perp fees paid by anyone you refer. Plus, they get
|
||||
a 4% perp fee discount.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-th-bkg-2 grid grid-cols-12 grid-flow-row gap-x-6 gap-y-8 p-4 sm:p-6 rounded-lg">
|
||||
{connected ? (
|
||||
mangoAccount ? (
|
||||
<>
|
||||
{/* {hasReferrals ? (
|
||||
<div className="col-span-12">
|
||||
<h2 className="mb-4">Your Referrals</h2>
|
||||
<div className="border-b border-th-bkg-4 sm:border-b-0 grid grid-cols-2 grid-row-flow sm:gap-6">
|
||||
<div className="sm:border-b border-t border-th-bkg-4 col-span-2 sm:col-span-1 p-3 sm:p-4">
|
||||
<div className="pb-0.5 text-th-fgd-3 text-xs sm:text-sm">
|
||||
Total Earnings
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-xl sm:text-2xl">
|
||||
$150.50
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:border-b border-t border-th-bkg-4 col-span-2 sm:col-span-1 p-3 sm:p-4">
|
||||
<div className="pb-0.5 text-th-fgd-3 text-xs sm:text-sm">
|
||||
Total referrals
|
||||
</div>
|
||||
<div className="font-bold text-th-fgd-1 text-xl sm:text-2xl">
|
||||
15
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : null} */}
|
||||
<div className="col-span-12">
|
||||
<div className="flex flex-col xl:flex-row xl:space-x-6 space-y-4 xl:space-y-0 w-full">
|
||||
<div className="min-w-[25%] bg-th-bkg-3 flex-1 p-6 rounded-md">
|
||||
<ProgramDetails />
|
||||
</div>
|
||||
<div className="flex flex-col w-full">
|
||||
{hasRequiredMngo ? (
|
||||
<div className="bg-th-bkg-3 flex-1 p-6 rounded-md">
|
||||
<h2 className="mb-4">Your Links</h2>
|
||||
{!loading ? (
|
||||
!hasCustomRefLinks ? (
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th>Link</Th>
|
||||
<Th>Copy Link</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
<TrBody>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
{!isMobile ? (
|
||||
<LinkIcon className="h-4 mr-1.5 w-4" />
|
||||
) : null}
|
||||
<p className="mb-0 text-th-fgd-1 max-w-md">
|
||||
{isMobile
|
||||
? abbreviateAddress(
|
||||
mangoAccount.publicKey
|
||||
)
|
||||
: `https://trade.mango.markets?ref=${mangoAccount.publicKey.toString()}`}
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="flex items-center justify-end">
|
||||
<IconButton
|
||||
className={`flex-shrink-0 ${
|
||||
hasCopied === 1 && 'bg-th-green'
|
||||
}`}
|
||||
disabled={hasCopied}
|
||||
onClick={() =>
|
||||
handleCopyLink(
|
||||
`https://trade.mango.markets?ref=${mangoAccount.publicKey.toString()}`,
|
||||
1
|
||||
)
|
||||
}
|
||||
>
|
||||
{hasCopied === 1 ? (
|
||||
<CheckIcon className="h-5 w-5" />
|
||||
) : (
|
||||
<DuplicateIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</Td>
|
||||
</TrBody>
|
||||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th>Link</Th>
|
||||
<Th>
|
||||
<div className="flex justify-end">
|
||||
Copy Link
|
||||
</div>
|
||||
</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{existingCustomRefLinks.map(
|
||||
(customRefs, index) => (
|
||||
<TrBody key={customRefs.referrerId}>
|
||||
<Td>
|
||||
<div className="flex items-center">
|
||||
{!isMobile ? (
|
||||
<LinkIcon className="h-4 mr-1.5 w-4" />
|
||||
) : null}
|
||||
<p className="mb-0 text-th-fgd-1">
|
||||
{isMobile
|
||||
? customRefs.referrerId
|
||||
: `https://trade.mango.markets?ref=${customRefs.referrerId}`}
|
||||
</p>
|
||||
</div>
|
||||
</Td>
|
||||
<Td className="flex items-center justify-end">
|
||||
<IconButton
|
||||
className={`flex-shrink-0 ${
|
||||
hasCopied === index + 1 &&
|
||||
'bg-th-green'
|
||||
}`}
|
||||
disabled={hasCopied}
|
||||
onClick={() =>
|
||||
handleCopyLink(
|
||||
`https://trade.mango.markets?ref=${customRefs.referrerId}`,
|
||||
index + 1
|
||||
)
|
||||
}
|
||||
>
|
||||
{hasCopied === index + 1 ? (
|
||||
<CheckIcon className="h-5 w-5" />
|
||||
) : (
|
||||
<DuplicateIcon className="h-4 w-4" />
|
||||
)}
|
||||
</IconButton>
|
||||
</Td>
|
||||
</TrBody>
|
||||
)
|
||||
)}
|
||||
</tbody>
|
||||
</Table>
|
||||
)
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<div className="animate-pulse bg-th-bkg-4 h-16" />
|
||||
<div className="animate-pulse bg-th-bkg-4 h-16" />
|
||||
<div className="animate-pulse bg-th-bkg-4 h-16" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-th-bkg-3 flex flex-col flex-1 items-center justify-center px-4 py-8 rounded-md text-center">
|
||||
<MngoMonoIcon className="h-6 mb-2 text-th-fgd-2 w-6" />
|
||||
<p className="mb-0">
|
||||
You need 10,000 MNGO in your Mango Account
|
||||
</p>
|
||||
|
||||
<Link href={'/?name=MNGO/USDC'} shallow={true}>
|
||||
<a className="mt-4 px-6 py-2 bg-th-bkg-4 font-bold rounded-full text-th-fgd-1 hover:brightness-[1.15] hover:text-th-fgd-1 focus:outline-none">
|
||||
Buy MNGO
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{hasRequiredMngo ? (
|
||||
<div className="min-w-[25%] bg-th-bkg-3 p-6 rounded-md w-full xl:w-1/3">
|
||||
<h2 className="mb-1">Custom Referral Links</h2>
|
||||
<p className="mb-4">
|
||||
You can generate up to 5 custom referral links.
|
||||
</p>
|
||||
<div className="pb-6">
|
||||
<label className="block mb-2 text-th-fgd-3 text-xs">
|
||||
Referral ID
|
||||
</label>
|
||||
<Input
|
||||
className="bg-th-bkg-1 border border-th-fgd-4 default-transition font-bold pl-4 h-12 focus:outline-none rounded-md text-base tracking-wide w-full hover:border-th-primary focus:border-th-primary"
|
||||
error={!!inputError}
|
||||
type="text"
|
||||
placeholder="ElonMusk"
|
||||
onBlur={validateRefIdInput}
|
||||
onChange={(e) => onChangeRefIdInput(e.target.value)}
|
||||
value={customRefLinkInput}
|
||||
disabled={existingCustomRefLinks.length === 5}
|
||||
/>
|
||||
{inputError ? (
|
||||
<div className="pt-2">
|
||||
<InlineNotification
|
||||
type="error"
|
||||
desc={inputError}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<button
|
||||
className="bg-th-primary flex items-center justify-center text-th-bkg-1 text-sm px-4 py-2 rounded-full hover:brightness-[1.15] focus:outline-none disabled:bg-th-bkg-4 disabled:text-th-fgd-4 disabled:cursor-not-allowed disabled:hover:brightness-100"
|
||||
onClick={submitRefLink}
|
||||
disabled={existingCustomRefLinks.length === 5}
|
||||
>
|
||||
<LinkIcon className="h-4 mr-1.5 w-4" />
|
||||
Generate Custom Link
|
||||
</button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{referralHistory.length > 0 ? (
|
||||
<div className="col-span-12">
|
||||
<h2 className="mb-4">Earnings History</h2>
|
||||
{!isMobile ? (
|
||||
<Table>
|
||||
<thead>
|
||||
<TrHead>
|
||||
<Th>{t('date')}</Th>
|
||||
<Th>Referral ID</Th>
|
||||
<Th>Referee</Th>
|
||||
<Th>
|
||||
<div className="flex justify-end">Fee Earned</div>
|
||||
</Th>
|
||||
</TrHead>
|
||||
</thead>
|
||||
<tbody>
|
||||
{referralHistory.map((ref, index) => (
|
||||
<TrBody key={ref.fee + index}>
|
||||
<Td>
|
||||
{dayjs(ref.time).format('DD MMM YYYY h:mma')}
|
||||
</Td>
|
||||
<Td>{ref.referralLink}</Td>
|
||||
<Td>
|
||||
<Link
|
||||
href={`/account?pubkey=${ref.referee}`}
|
||||
shallow={true}
|
||||
>
|
||||
<a className="text-th-fgd-2 underline hover:no-underline hover:text-th-fgd-3">
|
||||
{abbreviateAddress(mangoAccount.publicKey)}
|
||||
</a>
|
||||
</Link>
|
||||
</Td>
|
||||
<Td className="flex items-center justify-end">
|
||||
${ref.fee}
|
||||
</Td>
|
||||
</TrBody>
|
||||
))}
|
||||
</tbody>
|
||||
</Table>
|
||||
) : (
|
||||
<>
|
||||
<MobileTableHeader
|
||||
colOneHeader={t('date')}
|
||||
colTwoHeader="Fee Earned"
|
||||
/>
|
||||
{referralHistory.map((ref, index) => (
|
||||
<ExpandableRow
|
||||
buttonTemplate={
|
||||
<div className="flex items-center justify-between text-th-fgd-1 w-full">
|
||||
<div>
|
||||
{dayjs(ref.time).format('DD MMM YYYY h:mma')}
|
||||
</div>
|
||||
<div className="text-right">${ref.fee}</div>
|
||||
</div>
|
||||
}
|
||||
key={`${ref.fee + index}`}
|
||||
index={index}
|
||||
panelTemplate={
|
||||
<>
|
||||
<div className="grid grid-cols-2 grid-flow-row gap-4 pb-4">
|
||||
<div className="text-left">
|
||||
<div className="pb-0.5 text-th-fgd-3 text-xs">
|
||||
Referral ID
|
||||
</div>
|
||||
<div>{ref.referralLink}</div>
|
||||
</div>
|
||||
<div className="text-left">
|
||||
<div className="pb-0.5 text-th-fgd-3 text-xs">
|
||||
Referee
|
||||
</div>
|
||||
<Link
|
||||
href={`/account?pubkey=${ref.referee}`}
|
||||
shallow={true}
|
||||
>
|
||||
<a className="text-th-fgd-2 underline hover:no-underline hover:text-th-fgd-3">
|
||||
{abbreviateAddress(
|
||||
mangoAccount.publicKey
|
||||
)}
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="col-span-12 lg:col-span-4 bg-th-bkg-3 p-6 rounded-md">
|
||||
<ProgramDetails />
|
||||
</div>
|
||||
<div className="col-span-12 lg:col-span-8 bg-th-bkg-3 p-6 rounded-md flex items-center justify-center">
|
||||
<EmptyState
|
||||
buttonText={t('create-account')}
|
||||
icon={<CurrencyDollarIcon />}
|
||||
onClickButton={() => setShowAccountsModal(true)}
|
||||
title={t('no-account-found')}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
<div className="col-span-12 lg:col-span-4 bg-th-bkg-3 p-6 rounded-md">
|
||||
<ProgramDetails />
|
||||
</div>
|
||||
<div className="col-span-12 lg:col-span-8 bg-th-bkg-3 p-6 rounded-md flex items-center justify-center">
|
||||
<EmptyState
|
||||
buttonText={t('connect')}
|
||||
icon={<LinkIcon />}
|
||||
onClickButton={() => wallet.connect()}
|
||||
title={t('connect-wallet')}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</PageBodyContainer>
|
||||
{showAccountsModal ? (
|
||||
<AccountsModal
|
||||
onClose={() => setShowAccountsModal(false)}
|
||||
isOpen={showAccountsModal}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -33,7 +33,7 @@ import { PublicKey } from '@solana/web3.js'
|
|||
export async function getStaticProps({ locale }) {
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ['common'])),
|
||||
...(await serverSideTranslations(locale, ['common', 'calculator'])),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,14 @@ interface ScenarioCalculator {
|
|||
}
|
||||
|
||||
export default function RiskCalculator() {
|
||||
const { t } = useTranslation('common') // TOTRANSLATE
|
||||
const { t } = useTranslation(['common', 'calculator'])
|
||||
const riskRanks = [
|
||||
t('calculator:great'),
|
||||
t('calculator:ok'),
|
||||
t('calculator:poor'),
|
||||
t('calculator:very-poor'),
|
||||
t('calculator:rekt'),
|
||||
]
|
||||
|
||||
// Get mango account data
|
||||
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
|
||||
|
@ -916,14 +923,14 @@ export default function RiskCalculator() {
|
|||
|
||||
riskRanking =
|
||||
maintHealth > 0.4
|
||||
? 'Great'
|
||||
? riskRanks[0]
|
||||
: maintHealth > 0.3
|
||||
? 'OK'
|
||||
? riskRanks[1]
|
||||
: initHealth > 0
|
||||
? 'Poor'
|
||||
? riskRanks[2]
|
||||
: maintHealth > 0
|
||||
? 'Very Poor'
|
||||
: 'Rekt'
|
||||
? riskRanks[3]
|
||||
: riskRanks[4]
|
||||
|
||||
// Calculate percent to liquidation
|
||||
const scenarioBaseLine = getHealthComponents(1)
|
||||
|
@ -1289,12 +1296,9 @@ export default function RiskCalculator() {
|
|||
<PageBodyContainer>
|
||||
<div className="flex flex-col pt-8 pb-3 sm:pb-6 md:pt-10">
|
||||
<h1 className={`mb-2 text-th-fgd-1 text-2xl font-semibold`}>
|
||||
Risk Calculator
|
||||
{t('calculator:risk-calculator')}
|
||||
</h1>
|
||||
<p className="mb-0">
|
||||
IN TESTING (Use at your own risk): Please report any bugs or
|
||||
comments in our #dev-ui discord channel.
|
||||
</p>
|
||||
<p className="mb-0">{t('calculator:in-testing-warning')}</p>
|
||||
</div>
|
||||
{scenarioBars?.rowData.length > 0 ? (
|
||||
<div className="rounded-lg bg-th-bkg-2">
|
||||
|
@ -1302,7 +1306,7 @@ export default function RiskCalculator() {
|
|||
<div className="col-span-12 md:col-span-8 p-4">
|
||||
<div className="flex justify-between pb-2 lg:pb-3 px-0 lg:px-3">
|
||||
<div className="pb-4 lg:pb-0 text-th-fgd-1 text-lg">
|
||||
Scenario Balances
|
||||
{t('calculator:scenario-balances')}
|
||||
</div>
|
||||
<div className="flex justify-between lg:justify-start">
|
||||
<Button
|
||||
|
@ -1315,14 +1319,14 @@ export default function RiskCalculator() {
|
|||
>
|
||||
<div className="flex items-center hover:text-th-primary">
|
||||
<RefreshIcon className="h-5 w-5 mr-1.5" />
|
||||
Reset
|
||||
{t('reset')}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-th-bkg-1 border border-th-fgd-4 flex items-center mb-3 lg:mx-3 px-3 h-8 rounded">
|
||||
<div className="pr-5 text-th-fgd-3 text-xs whitespace-nowrap">
|
||||
Edit All Prices
|
||||
{t('calculator:edit-all-prices')}
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Slider
|
||||
|
@ -1349,7 +1353,7 @@ export default function RiskCalculator() {
|
|||
<LinkButton
|
||||
onClick={() => setSliderPercentage(defaultSliderVal)}
|
||||
>
|
||||
Reset
|
||||
{t('reset')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1369,11 +1373,11 @@ export default function RiskCalculator() {
|
|||
className="text-xs"
|
||||
onChange={() => toggleOrdersAsBalance(!ordersAsBalance)}
|
||||
>
|
||||
Simulate orders cancelled
|
||||
{t('calculator:simulate-orders-cancelled')}
|
||||
</Switch>
|
||||
</div>
|
||||
<div className="flex justify-between lg:justify-start">
|
||||
<Tooltip content="Set current pricing to be the anchor point (0%) for slider">
|
||||
<Tooltip content={t('calculator:tooltip-anchor-slider')}>
|
||||
<Button
|
||||
className={`text-xs flex items-center justify-center sm:ml-3 pt-0 pb-0 h-8 pl-3 pr-3 rounded`}
|
||||
onClick={() => {
|
||||
|
@ -1383,7 +1387,7 @@ export default function RiskCalculator() {
|
|||
>
|
||||
<div className="flex items-center hover:text-th-primary">
|
||||
<AnchorIcon className="h-5 w-5 mr-1.5" />
|
||||
Anchor slider
|
||||
{t('calculator:anchor-slider')}
|
||||
</div>
|
||||
</Button>
|
||||
</Tooltip>
|
||||
|
@ -1397,8 +1401,8 @@ export default function RiskCalculator() {
|
|||
<Disclosure.Button className="bg-th-bkg-1 default-transition flex items-center justify-between p-3 w-full hover:bg-th-bkg-1 focus:outline-none">
|
||||
<div className="text-th-fgd-3">
|
||||
{open
|
||||
? 'Scenario Details'
|
||||
: 'Scenario Maintenance Health:'}
|
||||
? t('calculator:scenario-details')
|
||||
: t('calculator:scenario-maint-health')}
|
||||
</div>
|
||||
{open ? null : (
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
|
@ -1424,7 +1428,7 @@ export default function RiskCalculator() {
|
|||
<div className="text-th-fgd-1 text-xs">
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Maintenance Health
|
||||
{t('maint-health')}
|
||||
</div>
|
||||
{scenarioDetails.get('maintHealth') * 100 >= 9999
|
||||
? '>10000'
|
||||
|
@ -1437,7 +1441,7 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Initial Health
|
||||
{t('init-health')}
|
||||
</div>
|
||||
{scenarioDetails.get('initHealth') * 100 >= 9999
|
||||
? '>10000'
|
||||
|
@ -1450,7 +1454,7 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
New Positions Can Be Opened
|
||||
{t('calculator:new-positions-openable')}
|
||||
</div>
|
||||
<div
|
||||
className={`font-bold ${
|
||||
|
@ -1460,14 +1464,12 @@ export default function RiskCalculator() {
|
|||
}`}
|
||||
>
|
||||
{scenarioDetails.get('initHealth') * 100 >= 0
|
||||
? 'Yes'
|
||||
: 'No'}
|
||||
? t('calculator:yes')
|
||||
: t('calculator:no')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Account Health
|
||||
</div>
|
||||
<div className="text-th-fgd-3">{t('health')}</div>
|
||||
<div className="font-bold">
|
||||
{
|
||||
<div
|
||||
|
@ -1476,20 +1478,20 @@ export default function RiskCalculator() {
|
|||
0
|
||||
? 'text-th-red'
|
||||
: scenarioDetails.get('riskRanking') ===
|
||||
'Very Poor'
|
||||
riskRanks[3]
|
||||
? 'text-th-red'
|
||||
: scenarioDetails.get('riskRanking') ===
|
||||
'Poor'
|
||||
riskRanks[2]
|
||||
? 'text-th-orange'
|
||||
: scenarioDetails.get('riskRanking') ===
|
||||
'OK'
|
||||
riskRanks[1]
|
||||
? 'text-th-primary'
|
||||
: 'text-th-green'
|
||||
}`}
|
||||
>
|
||||
{scenarioDetails.get('maintHealth') * 100 <
|
||||
0
|
||||
? 'Rekt'
|
||||
? riskRanks[4]
|
||||
: scenarioDetails.get('riskRanking')}
|
||||
</div>
|
||||
}
|
||||
|
@ -1498,7 +1500,7 @@ export default function RiskCalculator() {
|
|||
<div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Account Value
|
||||
{t('account-value')}
|
||||
</div>
|
||||
<div className="font-bold">
|
||||
{formatUsdValue(
|
||||
|
@ -1509,7 +1511,7 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Percent Move To Liquidation
|
||||
{t('calculator:percent-move-liquidation')}
|
||||
</div>
|
||||
<div className="font-bold">
|
||||
{scenarioDetails.get(
|
||||
|
@ -1537,18 +1539,18 @@ export default function RiskCalculator() {
|
|||
scope="col"
|
||||
className={`px-1 lg:px-3 py-1 text-left font-normal`}
|
||||
>
|
||||
Asset
|
||||
{t('asset')}
|
||||
</Th>
|
||||
<Th
|
||||
scope="col"
|
||||
className={`px-1 lg:px-3 py-1 text-left font-normal`}
|
||||
>
|
||||
<div className="flex justify-start md:justify-between">
|
||||
<div className="pr-2">Spot</div>
|
||||
<div className="pr-2">{t('spot')}</div>
|
||||
<LinkButton
|
||||
onClick={() => resetScenarioColumn('spotNet')}
|
||||
>
|
||||
Reset
|
||||
{t('reset')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
</Th>
|
||||
|
@ -1557,13 +1559,13 @@ export default function RiskCalculator() {
|
|||
className={`px-1 lg:px-3 py-1 text-left font-normal`}
|
||||
>
|
||||
<div className="flex justify-start md:justify-between">
|
||||
<div className="pr-2">Perp</div>
|
||||
<div className="pr-2">{t('perp')}</div>
|
||||
<LinkButton
|
||||
onClick={() =>
|
||||
resetScenarioColumn('perpBasePosition')
|
||||
}
|
||||
>
|
||||
Reset
|
||||
{t('reset')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
</Th>
|
||||
|
@ -1572,13 +1574,15 @@ export default function RiskCalculator() {
|
|||
className={`px-1 lg:px-3 py-1 text-left font-normal`}
|
||||
>
|
||||
<div className="flex justify-start md:justify-between">
|
||||
<div className="pr-2">Perp Entry</div>
|
||||
<div className="pr-2">
|
||||
{t('calculator:perp-entry')}
|
||||
</div>
|
||||
<LinkButton
|
||||
onClick={() =>
|
||||
resetScenarioColumn('perpAvgEntryPrice')
|
||||
}
|
||||
>
|
||||
Reset
|
||||
{t('reset')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
</Th>
|
||||
|
@ -1587,11 +1591,11 @@ export default function RiskCalculator() {
|
|||
className={`px-1 lg:px-3 py-1 font-normal`}
|
||||
>
|
||||
<div className="flex justify-start md:justify-between">
|
||||
<div className="pr-2">Price</div>
|
||||
<div className="pr-2">{t('price')}</div>
|
||||
<LinkButton
|
||||
onClick={() => resetScenarioColumn('price')}
|
||||
>
|
||||
Reset
|
||||
{t('reset')}
|
||||
</LinkButton>
|
||||
</div>
|
||||
</Th>
|
||||
|
@ -1600,8 +1604,10 @@ export default function RiskCalculator() {
|
|||
className={`px-1 lg:px-3 py-1 text-left font-normal`}
|
||||
>
|
||||
<div className="flex justify-start md:justify-between">
|
||||
<Tooltip content="Spot Value + Perp Balance">
|
||||
<div className="pr-2">Value</div>
|
||||
<Tooltip
|
||||
content={t('calculator:spot-val-perp-val')}
|
||||
>
|
||||
<div className="pr-2">{t('value')}</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Th>
|
||||
|
@ -1610,8 +1616,12 @@ export default function RiskCalculator() {
|
|||
className={`px-1 lg:px-3 py-1 text-left font-normal`}
|
||||
>
|
||||
<div className="flex justify-start md:justify-between">
|
||||
<Tooltip content="Single asset liquidation price assuming all other asset prices remain constant">
|
||||
<div className="pr-2">Liq. Price</div>
|
||||
<Tooltip
|
||||
content={t('calculator:single-asset-liq')}
|
||||
>
|
||||
<div className="pr-2">
|
||||
{t('calculator:liq-price')}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Th>
|
||||
|
@ -1936,7 +1946,7 @@ export default function RiskCalculator() {
|
|||
{scenarioBars?.rowData.length > 0 ? (
|
||||
<div className="bg-th-bkg-3 col-span-4 hidden md:block p-4 relative rounded-r-lg">
|
||||
<div className="pb-4 text-th-fgd-1 text-lg">
|
||||
Scenario Details
|
||||
{t('calculator:scenario-details')}
|
||||
</div>
|
||||
{/* Joke Wrapper */}
|
||||
<div className="relative col-span-4">
|
||||
|
@ -1944,10 +1954,10 @@ export default function RiskCalculator() {
|
|||
scenarioDetails.get('equity') === 0 ? (
|
||||
<div className="bg-th-green-dark border border-th-green-dark flex flex-col items-center mb-6 p-3 rounded text-center text-th-fgd-1">
|
||||
<div className="pb-0.5 text-th-fgd-1">
|
||||
Let's get this party started
|
||||
{t('calculator:joke-get-party-started')}
|
||||
</div>
|
||||
<div className="text-th-fgd-1 text-xs">
|
||||
The mangoes are ripe for the picking...
|
||||
{t('calculator:joke-mangoes-are-ripe')}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
|
@ -1955,64 +1965,70 @@ export default function RiskCalculator() {
|
|||
scenarioDetails.get('equity') > 0 ? (
|
||||
<div className="border border-th-green flex flex-col items-center mb-6 p-3 rounded text-center text-th-fgd-1">
|
||||
<div className="pb-0.5 text-th-fgd-1">
|
||||
0 Borrows = 0 Risk
|
||||
{t('calculator:joke-zero-borrows-risk')}
|
||||
</div>
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Come on, live a little...
|
||||
{t('calculator:joke-live-a-little')}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{scenarioDetails.get('riskRanking') === 'Great' &&
|
||||
{scenarioDetails.get('riskRanking') === riskRanks[0] &&
|
||||
scenarioDetails.get('leverage') !== 0 ? (
|
||||
<div className="border border-th-green flex flex-col items-center mb-6 p-3 rounded text-center text-th-fgd-1">
|
||||
<div className="pb-0.5 text-th-fgd-1">Looking good</div>
|
||||
<div className="pb-0.5 text-th-fgd-1">
|
||||
{t('calculator:joke-looking-good')}
|
||||
</div>
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
The sun is shining and the mangoes are ripe...
|
||||
{t('calculator:joke-sun-shining')}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{scenarioDetails.get('riskRanking') === 'OK' ? (
|
||||
{scenarioDetails.get('riskRanking') === riskRanks[1] ? (
|
||||
<div className="border border-th-orange flex flex-col items-center mb-6 p-3 rounded text-center text-th-fgd-1">
|
||||
<div className="pb-0.5 text-th-fgd-1">
|
||||
Liquidator activity is increasing
|
||||
{t('calculator:joke-liquidator-activity')}
|
||||
</div>
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
It might be time to re-think your positions
|
||||
{t('calculator:joke-rethink-positions')}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{scenarioDetails.get('riskRanking') === 'Poor' ? (
|
||||
{scenarioDetails.get('riskRanking') === riskRanks[2] ? (
|
||||
<div className="border border-th-red flex flex-col items-center mb-6 p-3 rounded text-center text-th-fgd-1">
|
||||
<div className="pb-0.5 text-th-fgd-1">
|
||||
Liquidators are closing in
|
||||
{t('calculator:joke-liquidators-closing')}
|
||||
</div>
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Hit 'em with everything you've got...
|
||||
{t('calculator:joke-hit-em-with')}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{scenarioDetails.get('riskRanking') === 'Very Poor' ? (
|
||||
{scenarioDetails.get('riskRanking') === riskRanks[3] ? (
|
||||
<div className="border border-th-red flex flex-col items-center mb-6 p-3 rounded text-center text-th-fgd-1">
|
||||
<div className="pb-0.5 text-th-fgd-1">
|
||||
Liquidators have spotted you
|
||||
{t('calculator:joke-liquidators-spotted-you')}
|
||||
</div>
|
||||
<div className="text-th-fgd-3 text-xs">
|
||||
Throw some money at them to make them go away...
|
||||
{t('calculator:joke-throw-some-money')}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
{scenarioDetails.get('riskRanking') === 'Rekt' ? (
|
||||
{scenarioDetails.get('riskRanking') === riskRanks[4] ? (
|
||||
<div className="bg-th-red border border-th-red flex flex-col items-center mb-6 p-3 rounded text-center text-th-fgd-1">
|
||||
<div className="pb-0.5 text-th-fgd-1">Liquidated!</div>
|
||||
<div className="pb-0.5 text-th-fgd-1">
|
||||
{t('calculator:joke-liquidated')}
|
||||
</div>
|
||||
<div className="text-th-fgd-1 text-xs">
|
||||
Insert coin to continue...
|
||||
{t('calculator:joke-insert-coin')}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<Tooltip content="Maintenance health must be above 0% to avoid liquidation.">
|
||||
<div className="text-th-fgd-3">Maintenance Health</div>
|
||||
<Tooltip content={t('calculator:tooltip-maint-health')}>
|
||||
<div className="text-th-fgd-3">
|
||||
{t('calculator:maintenance-health')}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className="font-bold">
|
||||
{scenarioDetails.get('maintHealth') * 100 >= 9999
|
||||
|
@ -2024,8 +2040,10 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<Tooltip content="Initial health must be above 0% to open new positions.">
|
||||
<div className="text-th-fgd-3">Initial Health</div>
|
||||
<Tooltip content={t('calculator:tooltip-init-health')}>
|
||||
<div className="text-th-fgd-3">
|
||||
{t('calculator:initial-health')}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<div className="font-bold">
|
||||
{scenarioDetails.get('initHealth') * 100 >= 9999
|
||||
|
@ -2038,7 +2056,7 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
New Positions Can Be Opened
|
||||
{t('calculator:new-positions-openable')}
|
||||
</div>
|
||||
<div
|
||||
className={`font-bold ${
|
||||
|
@ -2048,53 +2066,56 @@ export default function RiskCalculator() {
|
|||
}`}
|
||||
>
|
||||
{scenarioDetails.get('initHealth') * 100 >= 0
|
||||
? 'Yes'
|
||||
: 'No'}
|
||||
? t('calculator:yes')
|
||||
: t('calculator:no')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3 mb-6">
|
||||
<div className="text-th-fgd-3">Account Health</div>
|
||||
<div className="text-th-fgd-3">{t('account-health')}</div>
|
||||
{
|
||||
<div
|
||||
className={`font-bold ${
|
||||
scenarioDetails.get('maintHealth') * 100 < 0
|
||||
? 'text-th-red'
|
||||
: scenarioDetails.get('riskRanking') === 'Very Poor'
|
||||
: scenarioDetails.get('riskRanking') ===
|
||||
riskRanks[3]
|
||||
? 'text-th-red'
|
||||
: scenarioDetails.get('riskRanking') === 'Poor'
|
||||
: scenarioDetails.get('riskRanking') ===
|
||||
riskRanks[2]
|
||||
? 'text-th-orange'
|
||||
: scenarioDetails.get('riskRanking') === 'OK'
|
||||
: scenarioDetails.get('riskRanking') ===
|
||||
riskRanks[1]
|
||||
? 'text-th-primary'
|
||||
: 'text-th-green'
|
||||
}`}
|
||||
>
|
||||
{scenarioDetails.get('maintHealth') * 100 < 0
|
||||
? 'Rekt'
|
||||
? riskRanks[4]
|
||||
: scenarioDetails.get('riskRanking')}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">Account Value</div>
|
||||
<div className="text-th-fgd-3">{t('account-value')}</div>
|
||||
<div className="font-bold">
|
||||
{formatUsdValue(scenarioDetails.get('equity'))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">Assets</div>
|
||||
<div className="text-th-fgd-3">{t('assets')}</div>
|
||||
<div className="font-bold">
|
||||
{formatUsdValue(scenarioDetails.get('assets'))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3 mb-6">
|
||||
<div className="text-th-fgd-3">Liabilities</div>
|
||||
<div className="text-th-fgd-3">{t('liabilities')}</div>
|
||||
<div className="font-bold">
|
||||
{formatUsdValue(scenarioDetails.get('liabilities'))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Maint. Weighted Assets Value
|
||||
{t('calculator:maint-weighted-assets')}
|
||||
</div>
|
||||
<div className="font-bold">
|
||||
{formatUsdValue(scenarioDetails.get('maintWeightAssets'))}
|
||||
|
@ -2102,7 +2123,7 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Maint. Weighted Liabilities Value
|
||||
{t('calculator:maint-weighted-liabilities')}
|
||||
</div>
|
||||
<div className="font-bold">
|
||||
{formatUsdValue(
|
||||
|
@ -2112,7 +2133,7 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Init. Weighted Assets Value
|
||||
{t('calculator:init-weighted-assets')}
|
||||
</div>
|
||||
<div className="font-bold">
|
||||
{formatUsdValue(scenarioDetails.get('initWeightAssets'))}
|
||||
|
@ -2120,7 +2141,7 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
<div className="flex items-center justify-between pb-3 mb-6">
|
||||
<div className="text-th-fgd-3">
|
||||
Init. Weighted Liabilities Value
|
||||
{t('calculator:init-weighted-assets')}
|
||||
</div>
|
||||
<div className="font-bold">
|
||||
{formatUsdValue(
|
||||
|
@ -2129,14 +2150,14 @@ export default function RiskCalculator() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">Leverage</div>
|
||||
<div className="text-th-fgd-3">{t('leverage')}</div>
|
||||
<div className="font-bold">
|
||||
{scenarioDetails.get('leverage').toFixed(2)}x
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center justify-between pb-3">
|
||||
<div className="text-th-fgd-3">
|
||||
Percent Move To Liquidation
|
||||
{t('calculator:percent-move-liquidation')}
|
||||
</div>
|
||||
<div className="font-bold">
|
||||
{scenarioDetails.get('percentToLiquidationAbsolute')}%
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"anchor-slider": "Anchor slider",
|
||||
"edit-all-prices": "Edit All Prices",
|
||||
"great": "Great",
|
||||
"in-testing-warning": "IN TESTING (Use at your own risk): Please report any bugs or comments in our #dev-ui discord channel.",
|
||||
"init-weighted-assets": "Init. Weighted Assets Value",
|
||||
"init-weighted-liabilities": "Init. Weighted Liabilities Value",
|
||||
"initial-health": "Initial Health",
|
||||
"joke-get-party-started": "Let's get this party started",
|
||||
"joke-hit-em-with": "Hit 'em with everything you've got...",
|
||||
"joke-insert-coin": "Insert coin to continue...",
|
||||
"joke-liquidated": "Liquidated!",
|
||||
"joke-liquidator-activity": "Liquidator activity is increasing",
|
||||
"joke-liquidators-closing": "Liquidators are closing in",
|
||||
"joke-liquidators-spotted-you": "Liquidators have spotted you",
|
||||
"joke-live-a-little": "Come on, live a little",
|
||||
"joke-looking-good": "Looking good",
|
||||
"joke-mangoes-are-ripe": "The mangoes are ripe for the picking...",
|
||||
"joke-rethink-positions": "It might be time to re-think your positions",
|
||||
"joke-sun-shining": "The sun is shining and the mangoes are ripe...",
|
||||
"joke-throw-some-money": "Throw some money at them to make them go away...",
|
||||
"joke-zero-borrows-risk": "0 Borrows = 0 Risk",
|
||||
"liq-price": "Liq. Price",
|
||||
"maint-weighted-assets": "Maint. Weighted Assets Value",
|
||||
"maint-weighted-liabilities": "Maint. Weighted Liabilities Value",
|
||||
"maintenance-health": "Maintenance Health",
|
||||
"new-positions-openable": "New Positions Can Be Opened",
|
||||
"no": "No",
|
||||
"ok": "OK",
|
||||
"percent-move-liquidation": "Percent Move To Liquidation",
|
||||
"perp-entry": "Perp Entry",
|
||||
"poor": "Poor",
|
||||
"rekt": "Rekt",
|
||||
"risk-calculator": "Risk Calculator",
|
||||
"scenario-balances": "Scenario Balances",
|
||||
"scenario-details": "Scenario Details",
|
||||
"scenario-maint-health": "Scenario Maintenance Health:",
|
||||
"simulate-orders-cancelled": "Simulate orders cancelled",
|
||||
"single-asset-liq": "Single asset liquidation price assuming all other asset prices remain constant",
|
||||
"spot-val-perp-val": "Spot Value + Perp Balance",
|
||||
"tooltip-anchor-slider": "Set current pricing to be the anchor point (0%) for slider",
|
||||
"tooltip-init-health": "Initial health must be above 0% to open new positions.",
|
||||
"tooltip-maint-health": "Maintenance health must be above 0% to avoid liquidation.",
|
||||
"very-poor": "Very Poor",
|
||||
"yes": "Yes"
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
"account-details-tip-title": "Account Details",
|
||||
"account-equity": "Account Equity",
|
||||
"account-equity-chart-title": "Account Equity",
|
||||
"account-health": "Account Health",
|
||||
"account-health-tip-desc": "To avoid liquidation you must keep your account health above 0%. To increase the health of your account, reduce borrows or deposit funds.",
|
||||
"account-health-tip-title": "Account Health",
|
||||
"account-name": "Account Name",
|
||||
|
@ -23,6 +24,7 @@
|
|||
"add-name": "Add Name",
|
||||
"alert-health": "Alert when health is below",
|
||||
"alert-info": "Email when health <= {{health}}%",
|
||||
"alerts": "Alerts",
|
||||
"alerts-disclaimer": "Do not rely solely on alerts to protect your account. We can't guarantee they will be delivered.",
|
||||
"alerts-max": "You've reached the maximum number of active alerts.",
|
||||
"all-assets": "All Assets",
|
||||
|
@ -35,7 +37,7 @@
|
|||
"average-borrow": "Average Borrow Rates",
|
||||
"average-deposit": "Average Deposit Rates",
|
||||
"average-entry": "Avg Entry Price",
|
||||
"average-funding": "Avg. 1h Funding Rate",
|
||||
"average-funding": "1h Funding Rate",
|
||||
"back": "Back",
|
||||
"balance": "Balance",
|
||||
"balances": "Balances",
|
||||
|
@ -177,6 +179,7 @@
|
|||
"lets-go": "Let's Go",
|
||||
"leverage": "Leverage",
|
||||
"leverage-too-high": "Leverage too high. Reduce the amount to withdraw",
|
||||
"liabilities": "Liabilities",
|
||||
"light": "Light",
|
||||
"limit": "Limit",
|
||||
"limit-order": "Limit",
|
||||
|
@ -189,6 +192,7 @@
|
|||
"low": "Low",
|
||||
"maint-health": "Maint Health",
|
||||
"make-trade": "Make a trade",
|
||||
"maker": "Maker",
|
||||
"maker-fee": "Maker Fee",
|
||||
"mango": "Mango",
|
||||
"mango-accounts": "Mango Accounts",
|
||||
|
@ -255,8 +259,8 @@
|
|||
"orderbook": "Orderbook",
|
||||
"orderbook-animation": "Orderbook Animation",
|
||||
"orders": "Orders",
|
||||
"performance-insights": "Performance Insights",
|
||||
"performance": "Performance",
|
||||
"performance-insights": "Performance Insights",
|
||||
"period-progress": "Period Progress",
|
||||
"perp": "Perp",
|
||||
"perp-fees": "Mango Perp Fees",
|
||||
|
@ -288,8 +292,9 @@
|
|||
"recent": "Recent",
|
||||
"recent-trades": "Recent Trades",
|
||||
"redeem-failure": "Error redeeming MNGO",
|
||||
"redeem-success": "Successfully redeemed MNGO",
|
||||
"redeem-pnl": "Redeem PnL",
|
||||
"redeem-success": "Successfully redeemed MNGO",
|
||||
"referrals": "Referrals",
|
||||
"refresh": "Refresh",
|
||||
"refresh-data": "Refresh Data",
|
||||
"repay": "Repay",
|
||||
|
@ -297,6 +302,8 @@
|
|||
"repay-full": "100% repay borrow",
|
||||
"repay-partial": "Repay {{percentage}}% of borrow",
|
||||
"reposition": "Drag to reposition",
|
||||
"rolling-change" : "24hr Change",
|
||||
"reset": "Reset",
|
||||
"rpc-endpoint": "RPC Endpoint",
|
||||
"save": "Save",
|
||||
"save-name": "Save Name",
|
||||
|
@ -322,6 +329,7 @@
|
|||
"size": "Size",
|
||||
"slippage-warning": "This order will likely have extremely large slippage! Consider using Stop Limit or Take Profit Limit order instead.",
|
||||
"spanish": "Español",
|
||||
"spot": "Spot",
|
||||
"spread": "Spread",
|
||||
"stats": "Stats",
|
||||
"stop-limit": "Stop Limit",
|
||||
|
@ -332,6 +340,7 @@
|
|||
"swap": "Swap",
|
||||
"take-profit": "Take Profit",
|
||||
"take-profit-limit": "Take Profit Limit",
|
||||
"taker": "Taker",
|
||||
"taker-fee": "Taker Fee",
|
||||
"target-period-length": "Target Period Length",
|
||||
"themes-tip-desc": "Mango, Dark or Light (if you're that way inclined).",
|
||||
|
@ -394,6 +403,7 @@
|
|||
"v3-welcome": "Welcome to Mango",
|
||||
"value": "Value",
|
||||
"view-all-trades": "View all trades in the Account page",
|
||||
"view-counterparty": "View Counterparty",
|
||||
"view-transaction": "View Transaction",
|
||||
"wallet": "Wallet",
|
||||
"wallet-connected": "Wallet connected",
|
||||
|
@ -408,4 +418,4 @@
|
|||
"your-account": "Your Account",
|
||||
"your-assets": "Your Assets",
|
||||
"your-borrows": "Your Borrows"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"set-delegate": "Set Delegate",
|
||||
"delegate-your-account": "Delegate Your Account",
|
||||
"delegated-account": "Delegated Account",
|
||||
"info": "Grant control to another account to use Mango on your behalf.",
|
||||
"public-key": "Delegate Public Key",
|
||||
"delegate-updated": "Delegate Updated",
|
||||
"set-error": "Could not set Delegate",
|
||||
"invalid-key": "Invalid public key"
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
{
|
||||
"24h-vol": "24h Vol",
|
||||
"24h-volume": "24h Volume",
|
||||
"ata-deposit": "Deposit",
|
||||
"ata-deposit-details": "{{cost}} SOL for {{count}} ATA Account",
|
||||
"ata-deposit-details_plural": "{{cost}} SOL for {{count}} ATA Accounts",
|
||||
"ath": "All-Time High",
|
||||
"atl": "All-Time Low",
|
||||
"bal": "Bal:",
|
||||
"best": "Best",
|
||||
"best-swap": "Best Swap",
|
||||
"change-percent": "Change %",
|
||||
"chart-not-available": "Chart not available",
|
||||
"cheaper": "cheaper than",
|
||||
"fees-paid-to": "Fees paid to {{feeRecipient}}",
|
||||
|
@ -14,6 +18,7 @@
|
|||
"got-it": "Got It",
|
||||
"heres-how": "Here's how",
|
||||
"input-info-unavailable": "Input token information is not available.",
|
||||
"insights-not-available": "Market insights are not available",
|
||||
"jupiter-error": "Error in Jupiter – Try changing your input",
|
||||
"market-cap": "Market Cap",
|
||||
"market-cap-rank": "Market Cap Rank",
|
||||
|
@ -21,6 +26,7 @@
|
|||
"minimum-received": "Minimum Received",
|
||||
"more-expensive": "more expensive than",
|
||||
"need-ata-account": "You need to have an Associated Token Account.",
|
||||
"no-tokens-found": "No tokens found...",
|
||||
"other-routes": "{{numberOfRoutes}} other routes",
|
||||
"output-info-unavailable": "Output token information is not available.",
|
||||
"pay": "Pay",
|
||||
|
@ -42,8 +48,10 @@
|
|||
"swapping": "Swapping...",
|
||||
"to": "to",
|
||||
"token-supply": "Token Supply",
|
||||
"top-ten": "Top 10 Holders",
|
||||
"transaction-fee": "Transaction Fee",
|
||||
"unavailable": "Unavailable",
|
||||
"worst": "Worst",
|
||||
"you-pay": "You pay",
|
||||
"you-receive": "You receive"
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"anchor-slider": "Anchor slider",
|
||||
"edit-all-prices": "Edit All Prices",
|
||||
"great": "Great",
|
||||
"in-testing-warning": "IN TESTING (Use at your own risk): Please report any bugs or comments in our #dev-ui discord channel.",
|
||||
"init-weighted-assets": "Init. Weighted Assets Value",
|
||||
"init-weighted-liabilities": "Init. Weighted Liabilities Value",
|
||||
"initial-health": "Initial Health",
|
||||
"joke-get-party-started": "Let's get this party started",
|
||||
"joke-hit-em-with": "Hit 'em with everything you've got...",
|
||||
"joke-insert-coin": "Insert coin to continue...",
|
||||
"joke-liquidated": "Liquidated!",
|
||||
"joke-liquidator-activity": "Liquidator activity is increasing",
|
||||
"joke-liquidators-closing": "Liquidators are closing in",
|
||||
"joke-liquidators-spotted-you": "Liquidators have spotted you",
|
||||
"joke-live-a-little": "Come on, live a little",
|
||||
"joke-looking-good": "Looking good",
|
||||
"joke-mangoes-are-ripe": "The mangoes are ripe for the picking...",
|
||||
"joke-rethink-positions": "It might be time to re-think your positions",
|
||||
"joke-sun-shining": "The sun is shining and the mangoes are ripe...",
|
||||
"joke-throw-some-money": "Throw some money at them to make them go away...",
|
||||
"joke-zero-borrows-risk": "0 Borrows = 0 Risk",
|
||||
"liq-price": "Liq. Price",
|
||||
"maint-weighted-assets": "Maint. Weighted Assets Value",
|
||||
"maint-weighted-liabilities": "Maint. Weighted Liabilities Value",
|
||||
"maintenance-health": "Maintenance Health",
|
||||
"new-positions-openable": "New Positions Can Be Opened",
|
||||
"no": "No",
|
||||
"ok": "OK",
|
||||
"percent-move-liquidation": "Percent Move To Liquidation",
|
||||
"perp-entry": "Perp Entry",
|
||||
"poor": "Poor",
|
||||
"rekt": "Rekt",
|
||||
"risk-calculator": "Risk Calculator",
|
||||
"scenario-balances": "Scenario Balances",
|
||||
"scenario-details": "Scenario Details",
|
||||
"scenario-maint-health": "Scenario Maintenance Health:",
|
||||
"simulate-orders-cancelled": "Simulate orders cancelled",
|
||||
"single-asset-liq": "Single asset liquidation price assuming all other asset prices remain constant",
|
||||
"spot-val-perp-val": "Spot Value + Perp Balance",
|
||||
"tooltip-anchor-slider": "Set current pricing to be the anchor point (0%) for slider",
|
||||
"tooltip-init-health": "Initial health must be above 0% to open new positions.",
|
||||
"tooltip-maint-health": "Maintenance health must be above 0% to avoid liquidation.",
|
||||
"very-poor": "Very Poor",
|
||||
"yes": "Yes"
|
||||
}
|
|
@ -9,6 +9,7 @@
|
|||
"account-details-tip-title": "Detalles de la cuenta",
|
||||
"account-equity": "Account Equity",
|
||||
"account-equity-chart-title": "Account Equity",
|
||||
"account-health": "Account Health",
|
||||
"account-health-tip-desc": "Para evitar la liquidación, debe mantener el estado de su cuenta por encima del 0%. Para mejorar el estado de su cuenta, reduzca los préstamos o los fondos de depósito.",
|
||||
"account-health-tip-title": "Estado de la cuenta",
|
||||
"account-name": "Nombre de la cuenta",
|
||||
|
@ -23,6 +24,7 @@
|
|||
"add-name": "Añadir nombre",
|
||||
"alert-health": "Alert when health is below",
|
||||
"alert-info": "Email when health <= {{health}}%",
|
||||
"alerts": "Alerts",
|
||||
"alerts-disclaimer": "Do not rely solely on alerts to protect your account. We can't guarantee they will be delivered.",
|
||||
"alerts-max": "You've reached the maximum number of active alerts.",
|
||||
"all-assets": "Todos los activos",
|
||||
|
@ -177,6 +179,7 @@
|
|||
"lets-go": "Vamos",
|
||||
"leverage": "Apalancamiento",
|
||||
"leverage-too-high": "Apalancamiento demasiado alto. Reducir la cantidad a retirar",
|
||||
"liabilities": "Liabilities",
|
||||
"light": "Ligera",
|
||||
"limit": "Limite",
|
||||
"limit-order": "orden de límite",
|
||||
|
@ -189,6 +192,7 @@
|
|||
"low": "Bajo",
|
||||
"maint-health": "Salud de mantenimiento",
|
||||
"make-trade": "Hacer un trato",
|
||||
"maker": "Maker",
|
||||
"maker-fee": "orden límite",
|
||||
"mango": "Mango",
|
||||
"mango-accounts": "Cuentas Mango",
|
||||
|
@ -255,6 +259,7 @@
|
|||
"orderbook": "libro de ordenes",
|
||||
"orderbook-animation": "Animación del libro de ordenes",
|
||||
"orders": "ordenes",
|
||||
"performance": "Performance",
|
||||
"performance-insights": "Performance Insights",
|
||||
"period-progress": "Period Progress",
|
||||
"perp": "perpetuo",
|
||||
|
@ -287,8 +292,8 @@
|
|||
"recent": "Reciente",
|
||||
"recent-trades": "Operaciones recientes",
|
||||
"redeem-failure": "Error al canjear MNGO",
|
||||
"redeem-success": "MNGO canjeado con éxito",
|
||||
"redeem-pnl": "Resolver",
|
||||
"redeem-success": "MNGO canjeado con éxito",
|
||||
"refresh": "Actualizar",
|
||||
"refresh-data": "Actualizar datos",
|
||||
"repay": "Pagar",
|
||||
|
@ -296,6 +301,7 @@
|
|||
"repay-full": "Pagar 100% lo prestado",
|
||||
"repay-partial": "Pagar el {{porcentaje}}% del préstamo",
|
||||
"reposition": "Arrastra para reposicionar",
|
||||
"reset": "Reset",
|
||||
"rpc-endpoint": "Punto final de RPC",
|
||||
"save": "Ahorrar",
|
||||
"save-name": "Guardar nombre",
|
||||
|
@ -321,6 +327,7 @@
|
|||
"size": "Tamaño",
|
||||
"slippage-warning": "This order will likely have extremely large slippage! Consider using Stop Limit or Take Profit Limit order instead.",
|
||||
"spanish": "Español",
|
||||
"spot": "Spot",
|
||||
"spread": "Propago",
|
||||
"stats": "Estadisticas",
|
||||
"stop-limit": "Límite de parada",
|
||||
|
@ -331,6 +338,7 @@
|
|||
"swap": "Swap",
|
||||
"take-profit": "Tomar ganancias",
|
||||
"take-profit-limit": "Tomar el límite de ganancias",
|
||||
"taker": "Taker",
|
||||
"taker-fee": "Orden mercado",
|
||||
"target-period-length": "Target Period Length",
|
||||
"themes-tip-desc": "Mango, Dark o Light (si te gusta eso).",
|
||||
|
@ -393,6 +401,7 @@
|
|||
"v3-welcome": "Bienvenido a Mango V3",
|
||||
"value": "Valor",
|
||||
"view-all-trades": "Ver todas las operaciones en la página de la cuenta",
|
||||
"view-counterparty": "View Counterparty",
|
||||
"view-transaction": "View Transaction",
|
||||
"wallet": "Wallet",
|
||||
"wallet-connected": "Wallet connected",
|
||||
|
@ -406,7 +415,5 @@
|
|||
"you-must-leave-enough-sol": "You must leave enough SOL in your wallet to pay for the transaction",
|
||||
"your-account": "Su cuenta",
|
||||
"your-assets": "Sus activos",
|
||||
"your-borrows": "Sus préstamos",
|
||||
|
||||
"performance": "Performance"
|
||||
"your-borrows": "Sus préstamos"
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"set-delegate": "Set Delegate",
|
||||
"delegate-your-account": "Delegate Your Account",
|
||||
"delegated-account": "Delegated Account",
|
||||
"info": "Grant control to another account to use Mango on your behalf.",
|
||||
"public-key": "Delegate Public Key",
|
||||
"delegate-updated": "Delegate Updated",
|
||||
"set-error": "Could not set Delegate",
|
||||
"invalid-key": "Invalid public key"
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
{
|
||||
"24h-vol": "24h Vol",
|
||||
"24h-volume": "24h Volume",
|
||||
"ata-deposit": "Deposit",
|
||||
"ata-deposit-details": "{{cost}} SOL for {{count}} ATA Account",
|
||||
"ata-deposit-details_plural": "{{cost}} SOL for {{count}} ATA Accounts",
|
||||
"ath": "All-Time High",
|
||||
"atl": "All-Time Low",
|
||||
"bal": "Bal:",
|
||||
"best": "Best",
|
||||
"best-swap": "Best Swap",
|
||||
"change-percent": "Change %",
|
||||
"chart-not-available": "Chart not available",
|
||||
"cheaper": "cheaper than",
|
||||
"fees-paid-to": "Fees paid to {{feeRecipient}}",
|
||||
|
@ -14,6 +18,7 @@
|
|||
"got-it": "Got It",
|
||||
"heres-how": "Here's how",
|
||||
"input-info-unavailable": "Input token information is not available.",
|
||||
"insights-not-available": "Market insights are not available",
|
||||
"jupiter-error": "Error in Jupiter – try changing your input",
|
||||
"market-cap": "Market Cap",
|
||||
"market-cap-rank": "Market Cap Rank",
|
||||
|
@ -21,6 +26,7 @@
|
|||
"minimum-received": "Minimum Received",
|
||||
"more-expensive": "more expensive than",
|
||||
"need-ata-account": "You need to have an Associated Token Account.",
|
||||
"no-tokens-found": "No tokens found...",
|
||||
"other-routes": "{{numberOfRoutes}} other routes",
|
||||
"output-info-unavailable": "Output token information is not available.",
|
||||
"pay": "Pay",
|
||||
|
@ -42,8 +48,10 @@
|
|||
"swapping": "Swapping...",
|
||||
"to": "to",
|
||||
"token-supply": "Token Supply",
|
||||
"top-ten": "Top 10 Holders",
|
||||
"transaction-fee": "Transaction Fee",
|
||||
"unavailable": "Unavailable",
|
||||
"worst": "Worst",
|
||||
"you-pay": "You pay",
|
||||
"you-receive": "You receive"
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"anchor-slider": "定滑快",
|
||||
"edit-all-prices": "调整所有价格",
|
||||
"great": "很好",
|
||||
"in-testing-warning": "在试验中! (风险自负): 遇到问题请在discord #dev-ui频道上报到。",
|
||||
"init-weighted-assets": "初始加权资产价值",
|
||||
"init-weighted-liabilities": "初始加权借贷价值",
|
||||
"initial-health": "初始健康度",
|
||||
"joke-get-party-started": "派对刚才开始喽...",
|
||||
"joke-hit-em-with": "加油!硬着头皮!",
|
||||
"joke-insert-coin": "糟糕!别放弃。必须保持百折不挠的精神喔!",
|
||||
"joke-liquidated": "您遭受清算了!",
|
||||
"joke-liquidator-activity": "清算者在醒起来...",
|
||||
"joke-liquidators-closing": "左右为难...",
|
||||
"joke-liquidators-spotted-you": "有点焦虑不安...",
|
||||
"joke-live-a-little": "仍有利可图!",
|
||||
"joke-looking-good": "都井井有条",
|
||||
"joke-mangoes-are-ripe": "芒果熟了等您摘一摘...",
|
||||
"joke-rethink-positions": "帐户余额停滞不前...",
|
||||
"joke-sun-shining": "有机可乘...",
|
||||
"joke-throw-some-money": "未雨绸缪很重要...",
|
||||
"joke-zero-borrows-risk": "皇天不负苦心人",
|
||||
"liq-price": "清算价格",
|
||||
"maint-weighted-assets": "维持加权资产价值",
|
||||
"maint-weighted-liabilities": "维持加权借贷价值",
|
||||
"maintenance-health": "维持健康度",
|
||||
"new-positions-openable": "可扩大当前持仓",
|
||||
"no": "不行",
|
||||
"ok": "OK",
|
||||
"percent-move-liquidation": "多大涨落导致清算",
|
||||
"perp-entry": "永续合约入点",
|
||||
"poor": "不好",
|
||||
"rekt": "糟糕了",
|
||||
"risk-calculator": "风险计算器",
|
||||
"scenario-balances": "模拟余额",
|
||||
"scenario-details": "模拟细节",
|
||||
"scenario-maint-health": "模拟维持健康度:",
|
||||
"simulate-orders-cancelled": "架设取消挂单",
|
||||
"single-asset-liq": "单个资产虚拟清算价格会假设所有其他资产价格不变",
|
||||
"spot-val-perp-val": "现货价直+合约余额",
|
||||
"tooltip-anchor-slider": "将目前资产价格定为滑快起点(0%)",
|
||||
"tooltip-init-health": "为了扩大当前持仓,初始健康度必须高于0%。",
|
||||
"tooltip-maint-health": "为了避免清算,维持健康度必须高于0%。",
|
||||
"very-poor": "很不好",
|
||||
"yes": "行"
|
||||
}
|
|
@ -4,11 +4,12 @@
|
|||
"accept": "接受",
|
||||
"accept-terms": "我明白并接受使用此平台的风险",
|
||||
"account": "帐户",
|
||||
"account-address-warning": "Do not send tokens directly to your account address.",
|
||||
"account-address-warning": "千万不要将币种直接传送至帐户地址。",
|
||||
"account-details-tip-desc": "当您进行首次存款时,我们将为您设置一个Mango账户。您的钱包中至少需要0.0035 SOL才能支付创建帐户的押金。",
|
||||
"account-details-tip-title": "帐户细节",
|
||||
"account-equity": "帐户余额",
|
||||
"account-equity-chart-title": "帐户余额",
|
||||
"account-health": "帐户健康",
|
||||
"account-health-tip-desc": "为了避免被清算,您必须将帐户健康度保持在0%以上。为了提高健康度,请减少借贷或存入资产。",
|
||||
"account-health-tip-title": "帐户健康",
|
||||
"account-name": "帐户标签",
|
||||
|
@ -21,10 +22,11 @@
|
|||
"active-alerts": "活动警报",
|
||||
"add-more-sol": "为了避免交易出错请给被连结的钱包多存入一点SOL。",
|
||||
"add-name": "加标签",
|
||||
"alert-health": "Alert when health is below",
|
||||
"alerts": "警报",
|
||||
"alert-health": "健康度低于此度发警报",
|
||||
"alert-info": "健康度在{{health}}%以下时发电子邮件",
|
||||
"alerts-disclaimer": "请别全靠警报来保护资产。我们无法保证会准时发出。",
|
||||
"alerts-max": "You've reached the maximum number of active alerts.",
|
||||
"alerts-max": "您以达到活动警报数量限制。",
|
||||
"all-assets": "所有资产",
|
||||
"amount": "数量",
|
||||
"approximate-time": "大概时间",
|
||||
|
@ -130,9 +132,9 @@
|
|||
"est-slippage": "预计下滑",
|
||||
"estimated-liq-price": "预计清算价格",
|
||||
"explorer": "浏览器",
|
||||
"export-data": "Export CSV",
|
||||
"export-data-empty": "No data to export",
|
||||
"export-data-success": "CSV exported successfully",
|
||||
"export-data": "导出CSV",
|
||||
"export-data-empty": "无资料可导出",
|
||||
"export-data-success": "CSV导出成功",
|
||||
"fee": "费率",
|
||||
"fee-discount": "费率折扣",
|
||||
"first-deposit-desc": "创建Mango帐户最少需要0.035 SOL。",
|
||||
|
@ -143,7 +145,7 @@
|
|||
"health-check": "帐户健康检查",
|
||||
"health-ratio": "健康比率",
|
||||
"hide-all": "在导航栏中隐藏全部",
|
||||
"hide-dust": "Hide dust",
|
||||
"hide-dust": "隐藏尘土",
|
||||
"high": "高",
|
||||
"history": "历史",
|
||||
"history-empty": "没有历史",
|
||||
|
@ -177,6 +179,7 @@
|
|||
"lets-go": "前往",
|
||||
"leverage": "杠杆",
|
||||
"leverage-too-high": "杠杆太高。请减少取款数量",
|
||||
"liabilities": "债务",
|
||||
"light": "明亮",
|
||||
"limit": "限价",
|
||||
"limit-order": "限价",
|
||||
|
@ -189,6 +192,7 @@
|
|||
"low": "低",
|
||||
"maint-health": "维持健康度",
|
||||
"make-trade": "下订单",
|
||||
"maker": "挂单者",
|
||||
"maker-fee": "挂单费率",
|
||||
"mango": "Mango",
|
||||
"mango-accounts": "Mango帐户",
|
||||
|
@ -223,8 +227,8 @@
|
|||
"name-your-account": "给帐户标签",
|
||||
"net": "净",
|
||||
"net-balance": "净余额",
|
||||
"net-interest-value": "Net Interest Value",
|
||||
"net-interest-value-desc": "Calculated at the time it was earned/paid. This might be useful at tax time.",
|
||||
"net-interest-value": "利息净价值",
|
||||
"net-interest-value-desc": "利息是以获取/付出时价值来计算的。纳税时这也许会有用。",
|
||||
"new": "新子帐户",
|
||||
"new-account": "新子帐户",
|
||||
"new-alert": "创建警报",
|
||||
|
@ -255,8 +259,8 @@
|
|||
"orderbook": "订单簿",
|
||||
"orderbook-animation": "订单动画",
|
||||
"orders": "订单",
|
||||
"performance-insights": "Performance Insights",
|
||||
"performance": "表现",
|
||||
"performance-insights": "表现分析",
|
||||
"period-progress": "期间进度",
|
||||
"perp": "Perp",
|
||||
"perp-fees": "Mango永续合约费率",
|
||||
|
@ -297,6 +301,7 @@
|
|||
"repay-full": "归还100%借贷",
|
||||
"repay-partial": "归还{{percentage}}%借贷",
|
||||
"reposition": "推动以重新定位",
|
||||
"reset": "重置",
|
||||
"rpc-endpoint": "RPC终点",
|
||||
"save": "保存",
|
||||
"save-name": "保存标签",
|
||||
|
@ -322,6 +327,7 @@
|
|||
"size": "数量",
|
||||
"slippage-warning": "此订单也许会遭受大量滑点!使用限价止损或限价止盈可能比较适合。",
|
||||
"spanish": "Español",
|
||||
"spot": "现货",
|
||||
"spread": "点差",
|
||||
"stats": "统计",
|
||||
"stop-limit": "限价止损",
|
||||
|
@ -332,6 +338,7 @@
|
|||
"swap": "换币",
|
||||
"take-profit": "止盈",
|
||||
"take-profit-limit": "限价止盈",
|
||||
"taker": "吃单者",
|
||||
"taker-fee": "吃单费率",
|
||||
"target-period-length": "目标期间长度",
|
||||
"themes-tip-desc": "Mango,黑暗或明亮(看您偏向)。",
|
||||
|
@ -382,7 +389,7 @@
|
|||
"type": "类型",
|
||||
"unrealized-pnl": "未实现盈亏",
|
||||
"unsettled": "未结清",
|
||||
"unsettled-balance": "未结清余额",
|
||||
"unsettled-balance": "未实现盈亏",
|
||||
"unsettled-balances": "未结清余额",
|
||||
"unsettled-positions": "未结清持仓",
|
||||
"use-explorer-one": "使用",
|
||||
|
@ -394,6 +401,7 @@
|
|||
"v3-welcome": "欢迎到Mango V3",
|
||||
"value": "价值",
|
||||
"view-all-trades": "在帐户页面查看所以交易",
|
||||
"view-counterparty": "查看交易对方",
|
||||
"view-transaction": "查看交易",
|
||||
"wallet": "钱包",
|
||||
"wallet-connected": "已连结钱包",
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"set-delegate": "Set Delegate",
|
||||
"delegate-your-account": "Delegate Your Account",
|
||||
"delegated-account": "Delegated Account",
|
||||
"info": "Grant control to another account to use Mango on your behalf.",
|
||||
"public-key": "Delegate Public Key",
|
||||
"delegate-updated": "Delegate Updated",
|
||||
"set-error": "Could not set Delegate",
|
||||
"invalid-key": "Invalid public key"
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
{
|
||||
"24h-vol": "24h成交量",
|
||||
"24h-volume": "24h成交量",
|
||||
"ata-deposit": "押金",
|
||||
"ata-deposit-details": "{{cost}} SOL为 {{count}} ATA帐户",
|
||||
"ata-deposit-details_plural": "{{cost}} SOL为 {{count}} ATA帐户",
|
||||
"ath": "历史高价",
|
||||
"atl": "历史低价",
|
||||
"bal": "余额:",
|
||||
"best": "最佳",
|
||||
"best-swap": "最佳换币",
|
||||
"change-percent": "变动%",
|
||||
"chart-not-available": "无法显示图表",
|
||||
"cheaper": "低于",
|
||||
"fees-paid-to": "费用缴给{{feeRecipient}}",
|
||||
|
@ -14,6 +18,7 @@
|
|||
"got-it": "明白",
|
||||
"heres-how": "了解更多",
|
||||
"input-info-unavailable": "获取付出币种资料时出错",
|
||||
"insights-not-available": "获取时市场分析出错",
|
||||
"jupiter-error": "Jupiter出错,请更改输入",
|
||||
"market-cap": "总市值",
|
||||
"market-cap-rank": "总市值排名",
|
||||
|
@ -21,6 +26,7 @@
|
|||
"minimum-received": "最好获得",
|
||||
"more-expensive": "高于",
|
||||
"need-ata-account": "您必有一个关联币种帐户(ATA)。",
|
||||
"no-tokens-found": "找不到币种...",
|
||||
"other-routes": "{{numberOfRoutes}}条其他路线",
|
||||
"output-info-unavailable": "获取收到币种资料时出错",
|
||||
"pay": "付出",
|
||||
|
@ -42,8 +48,10 @@
|
|||
"swapping": "正在交易...",
|
||||
"to": "到",
|
||||
"token-supply": "流通供应量",
|
||||
"top-ten": "前十名持有者",
|
||||
"transaction-fee": "交易费用",
|
||||
"unavailable": "无资料",
|
||||
"worst": "最差",
|
||||
"you-pay": "您付出",
|
||||
"you-receive": "您收到"
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
{
|
||||
"anchor-slider": "定滑快",
|
||||
"edit-all-prices": "調整所有價格",
|
||||
"great": "很好",
|
||||
"in-testing-warning": "在試驗中! (風險自負): 遇到問題請在discord #dev-ui頻道上報到。",
|
||||
"init-weighted-assets": "初始加權資產價值",
|
||||
"init-weighted-liabilities": "初始加權借貸價值",
|
||||
"initial-health": "初始健康度",
|
||||
"joke-get-party-started": "派對剛才開始嘍...",
|
||||
"joke-hit-em-with": "加油!硬著頭皮!",
|
||||
"joke-insert-coin": "糟糕!別放棄。必須保持百折不撓的精神喔!",
|
||||
"joke-liquidated": "您遭受清算了!",
|
||||
"joke-liquidator-activity": "清算者在醒起來...",
|
||||
"joke-liquidators-closing": "左右為難...",
|
||||
"joke-liquidators-spotted-you": "有點焦慮不安...",
|
||||
"joke-live-a-little": "仍有利可圖!",
|
||||
"joke-looking-good": "都井井有條",
|
||||
"joke-mangoes-are-ripe": "芒果熟了等您摘一摘...",
|
||||
"joke-rethink-positions": "烏雲密布...",
|
||||
"joke-sun-shining": "您冒個險吧。市場上有機可乘...",
|
||||
"joke-throw-some-money": "未雨綢繆很重要...",
|
||||
"joke-zero-borrows-risk": "皇天不負苦心人",
|
||||
"liq-price": "清算價格",
|
||||
"maint-weighted-assets": "維持加權資產價值",
|
||||
"maint-weighted-liabilities": "維持加權借貸價值",
|
||||
"maintenance-health": "維持健康度",
|
||||
"new-positions-openable": "可擴大當前持倉",
|
||||
"no": "不行",
|
||||
"ok": "OK",
|
||||
"percent-move-liquidation": "多大漲落導致清算",
|
||||
"perp-entry": "永續合約入點",
|
||||
"poor": "不好",
|
||||
"rekt": "糟糕了",
|
||||
"risk-calculator": "風險計算器",
|
||||
"scenario-balances": "模擬餘額",
|
||||
"scenario-details": "模擬細節",
|
||||
"scenario-maint-health": "模擬維持健康度:",
|
||||
"simulate-orders-cancelled": "架設取消掛單",
|
||||
"single-asset-liq": "單個資產虛擬清算價格會假設所有其他資產價格不變",
|
||||
"spot-val-perp-val": "現貨價直+合約餘額",
|
||||
"tooltip-anchor-slider": "將目前資產價格定為滑快起點(0%)",
|
||||
"tooltip-init-health": "為了擴大當前持倉,初始健康度必須高於0%。",
|
||||
"tooltip-maint-health": "為了避免清算,維持健康度必須高於0%。",
|
||||
"very-poor": "很不好",
|
||||
"yes": "行"
|
||||
}
|
|
@ -4,11 +4,12 @@
|
|||
"accept": "接受",
|
||||
"accept-terms": "我明白並接受使用此平台的風險",
|
||||
"account": "帳戶",
|
||||
"account-address-warning": "Do not send tokens directly to your account address.",
|
||||
"account-address-warning": "千萬不要將幣種直接傳送至帳戶地址。",
|
||||
"account-details-tip-desc": "當您進行首次存款時,我們將為您設置一個Mango賬戶。您的錢包中至少需要0.0035 SOL才能支付創建帳戶的押金。",
|
||||
"account-details-tip-title": "帳戶細節",
|
||||
"account-equity": "帳戶餘額",
|
||||
"account-equity-chart-title": "帳戶餘額",
|
||||
"account-health": "帳戶健康",
|
||||
"account-health-tip-desc": "為了避免被清算,您必須將帳戶健康度保持在0%以上。為了提高健康度,請減少借貸或存入資產。",
|
||||
"account-health-tip-title": "帳戶健康",
|
||||
"account-name": "帳戶標籤",
|
||||
|
@ -21,10 +22,11 @@
|
|||
"active-alerts": "活動警報",
|
||||
"add-more-sol": "為了避免交易出錯請給被連結的錢包多存入一點SOL。",
|
||||
"add-name": "加標籤",
|
||||
"alert-health": "Alert when health is below",
|
||||
"alerts": "警報",
|
||||
"alert-health": "健康度低於此度發警報",
|
||||
"alert-info": "健康度在{{health}}%以下時發電子郵件",
|
||||
"alerts-disclaimer": "請別全靠警報來保護資產。我們無法保證會準時發出。",
|
||||
"alerts-max": "You've reached the maximum number of active alerts.",
|
||||
"alerts-max": "您以達到活動警報數量限制。",
|
||||
"all-assets": "所有資產",
|
||||
"amount": "數量",
|
||||
"approximate-time": "大概時間",
|
||||
|
@ -130,9 +132,9 @@
|
|||
"est-slippage": "預計下滑",
|
||||
"estimated-liq-price": "預計清算價格",
|
||||
"explorer": "瀏覽器",
|
||||
"export-data": "Export CSV",
|
||||
"export-data-empty": "No data to export",
|
||||
"export-data-success": "CSV exported successfully",
|
||||
"export-data": "導出CSV",
|
||||
"export-data-empty": "無資料可導出",
|
||||
"export-data-success": "CSV導出成功",
|
||||
"fee": "費率",
|
||||
"fee-discount": "費率折扣",
|
||||
"first-deposit-desc": "創建Mango帳戶最少需要0.035 SOL。",
|
||||
|
@ -143,7 +145,7 @@
|
|||
"health-check": "帳戶健康檢查",
|
||||
"health-ratio": "健康比率",
|
||||
"hide-all": "在導航欄中隱藏全部",
|
||||
"hide-dust": "Hide dust",
|
||||
"hide-dust": "隱藏塵土",
|
||||
"high": "高",
|
||||
"history": "歷史",
|
||||
"history-empty": "沒有歷史",
|
||||
|
@ -177,6 +179,7 @@
|
|||
"lets-go": "前往",
|
||||
"leverage": "槓桿",
|
||||
"leverage-too-high": "槓桿太高。請減少取款數量",
|
||||
"liabilities": "債務",
|
||||
"light": "明亮",
|
||||
"limit": "限價",
|
||||
"limit-order": "限價",
|
||||
|
@ -189,6 +192,7 @@
|
|||
"low": "低",
|
||||
"maint-health": "維持健康度",
|
||||
"make-trade": "下訂單",
|
||||
"maker": "掛單者",
|
||||
"maker-fee": "掛單費率",
|
||||
"mango": "Mango",
|
||||
"mango-accounts": "Mango帳戶",
|
||||
|
@ -196,7 +200,7 @@
|
|||
"margin-available": "可用保證金",
|
||||
"market": "市場",
|
||||
"market-close": "市價平倉",
|
||||
"market-data": "Market Data",
|
||||
"market-data": "市場現況",
|
||||
"market-details": "市場細節",
|
||||
"market-order": "市價",
|
||||
"markets": "市場",
|
||||
|
@ -223,8 +227,8 @@
|
|||
"name-your-account": "給帳戶標籤",
|
||||
"net": "淨",
|
||||
"net-balance": "淨餘額",
|
||||
"net-interest-value": "Net Interest Value",
|
||||
"net-interest-value-desc": "Calculated at the time it was earned/paid. This might be useful at tax time.",
|
||||
"net-interest-value": "利息淨價值",
|
||||
"net-interest-value-desc": "利息是以獲取/付出時價值來計算的。納稅時這也許會有用。",
|
||||
"new": "新子帳戶",
|
||||
"new-account": "新子帳戶",
|
||||
"new-alert": "創建警報",
|
||||
|
@ -255,8 +259,8 @@
|
|||
"orderbook": "掛單簿",
|
||||
"orderbook-animation": "訂單動畫",
|
||||
"orders": "訂單",
|
||||
"performance-insights": "Performance Insights",
|
||||
"performance": "表現",
|
||||
"performance-insights": "表現分析",
|
||||
"period-progress": "期間進度",
|
||||
"perp": "Perp",
|
||||
"perp-fees": "Mango永續合約費率",
|
||||
|
@ -265,9 +269,9 @@
|
|||
"perp-positions-tip-title": "永續合約當前持倉細節",
|
||||
"perpetual-futures": "永續合約",
|
||||
"perps": "永續合約",
|
||||
"pnl-error": "結清盈虧出錯了",
|
||||
"pnl-help": "結清會更新USDC餘額來處理尚未結清的盈虧量。",
|
||||
"pnl-success": "已結清盈虧",
|
||||
"pnl-error": "實現盈虧出錯了",
|
||||
"pnl-help": "實現會更新USDC餘額來處理尚未實現的盈虧。",
|
||||
"pnl-success": "實現盈虧成功",
|
||||
"portfolio": "資產組合",
|
||||
"position": "當前持倉",
|
||||
"position-size": "當前持倉數量",
|
||||
|
@ -288,7 +292,7 @@
|
|||
"recent": "最近",
|
||||
"recent-trades": "最近成交",
|
||||
"redeem-failure": "收穫MNGO獎勵出錯了",
|
||||
"redeem-pnl": "結清",
|
||||
"redeem-pnl": "實現盈虧",
|
||||
"redeem-success": "已收穫MNGO獎勵了",
|
||||
"refresh": "更新",
|
||||
"refresh-data": "更新資料",
|
||||
|
@ -297,6 +301,7 @@
|
|||
"repay-full": "歸還100%借貸",
|
||||
"repay-partial": "歸還{{percentage}}%借貸",
|
||||
"reposition": "推動以重新定位",
|
||||
"reset": "重置",
|
||||
"rpc-endpoint": "RPC終點",
|
||||
"save": "保存",
|
||||
"save-name": "保存標籤",
|
||||
|
@ -322,6 +327,7 @@
|
|||
"size": "數量",
|
||||
"slippage-warning": "此訂單也許會遭受大量滑點!使用限價止損或限價止盈可能比較適合。",
|
||||
"spanish": "Español",
|
||||
"spot": "現貨",
|
||||
"spread": "點差",
|
||||
"stats": "統計",
|
||||
"stop-limit": "限價止損",
|
||||
|
@ -332,6 +338,7 @@
|
|||
"swap": "換幣",
|
||||
"take-profit": "止盈",
|
||||
"take-profit-limit": "限價止盈",
|
||||
"taker": "吃單者",
|
||||
"taker-fee": "吃單費率",
|
||||
"target-period-length": "目標期間長度",
|
||||
"themes-tip-desc": "Mango,黑暗或明亮(看您偏向)。",
|
||||
|
@ -382,7 +389,7 @@
|
|||
"type": "類型",
|
||||
"unrealized-pnl": "未實現盈虧",
|
||||
"unsettled": "未結清",
|
||||
"unsettled-balance": "未結清餘額",
|
||||
"unsettled-balance": "未實現盈虧",
|
||||
"unsettled-balances": "未結清餘額",
|
||||
"unsettled-positions": "未結清持倉",
|
||||
"use-explorer-one": "使用",
|
||||
|
@ -394,6 +401,7 @@
|
|||
"v3-welcome": "歡迎到Mango V3",
|
||||
"value": "價值",
|
||||
"view-all-trades": "在帳戶頁面查看所以交易",
|
||||
"view-counterparty": "查看交易對方",
|
||||
"view-transaction": "查看交易",
|
||||
"wallet": "錢包",
|
||||
"wallet-connected": "已連結錢包",
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"set-delegate": "Set Delegate",
|
||||
"delegate-your-account": "Delegate Your Account",
|
||||
"delegated-account": "Delegated Account",
|
||||
"info": "Grant control to another account to use Mango on your behalf.",
|
||||
"public-key": "Delegate Public Key",
|
||||
"delegate-updated": "Delegate Updated",
|
||||
"set-error": "Could not set Delegate",
|
||||
"invalid-key": "Invalid public key"
|
||||
}
|
|
@ -1,11 +1,15 @@
|
|||
{
|
||||
"24h-vol": "24h成交量",
|
||||
"24h-volume": "24h成交量",
|
||||
"ata-deposit": "押金",
|
||||
"ata-deposit-details": "{{cost}} SOL為 {{count}} ATA帳戶",
|
||||
"ata-deposit-details_plural": "{{cost}} SOL為 {{count}} ATA帳戶",
|
||||
"ath": "歷史高價",
|
||||
"atl": "歷史低價",
|
||||
"bal": "餘額:",
|
||||
"best": "最佳",
|
||||
"best-swap": "最佳換幣",
|
||||
"change-percent": "變動%",
|
||||
"chart-not-available": "無法顯示圖表",
|
||||
"cheaper": "低於",
|
||||
"fees-paid-to": "費用繳給{{feeRecipient}}",
|
||||
|
@ -14,6 +18,7 @@
|
|||
"got-it": "明白",
|
||||
"heres-how": "了解更多",
|
||||
"input-info-unavailable": "獲取付出幣種資料時出錯",
|
||||
"insights-not-available": "獲取時市場分析出錯",
|
||||
"jupiter-error": "Jupiter出錯,請更改輸入",
|
||||
"market-cap": "總市值",
|
||||
"market-cap-rank": "總市值排名",
|
||||
|
@ -21,6 +26,7 @@
|
|||
"minimum-received": "最好獲得",
|
||||
"more-expensive": "高於",
|
||||
"need-ata-account": "您必有一個關聯幣種帳戶(ATA)。",
|
||||
"no-tokens-found": "找不到幣種...",
|
||||
"other-routes": "{{numberOfRoutes}}條其他路線",
|
||||
"output-info-unavailable": "獲取收到幣種資料時出錯",
|
||||
"pay": "付出",
|
||||
|
@ -42,8 +48,10 @@
|
|||
"swapping": "正在換幣...",
|
||||
"to": "到",
|
||||
"token-supply": "流通供應量",
|
||||
"top-ten": "前十名持有者",
|
||||
"transaction-fee": "交易費用",
|
||||
"unavailable": "無資料",
|
||||
"worst": "最差",
|
||||
"you-pay": "您付出",
|
||||
"you-receive": "您收到"
|
||||
}
|
|
@ -16,6 +16,9 @@ export const mangoGroupConfigSelector = (state: MangoStore) =>
|
|||
export const mangoCacheSelector = (state: MangoStore) =>
|
||||
state.selectedMangoGroup.cache
|
||||
|
||||
export const mangoClientSelector = (state: MangoStore) =>
|
||||
state.connection.client
|
||||
|
||||
export const actionsSelector = (state: MangoStore) => state.actions
|
||||
|
||||
export const marketsSelector = (state: MangoStore) =>
|
||||
|
|
|
@ -21,6 +21,7 @@ import {
|
|||
getMultipleAccounts,
|
||||
PerpMarketLayout,
|
||||
msrmMints,
|
||||
MangoAccountLayout,
|
||||
} from '@blockworks-foundation/mango-client'
|
||||
import { AccountInfo, Commitment, Connection, PublicKey } from '@solana/web3.js'
|
||||
import { EndpointInfo, WalletAdapter } from '../@types/types'
|
||||
|
@ -158,6 +159,7 @@ export interface MangoStore extends State {
|
|||
cache: MangoCache | null
|
||||
}
|
||||
mangoAccounts: MangoAccount[]
|
||||
referrerPk: PublicKey | null
|
||||
selectedMangoAccount: {
|
||||
current: MangoAccount | null
|
||||
initialLoad: boolean
|
||||
|
@ -195,8 +197,11 @@ export interface MangoStore extends State {
|
|||
settings: {
|
||||
uiLocked: boolean
|
||||
}
|
||||
tradeHistory: any[]
|
||||
set: (x: any) => void
|
||||
tradeHistory: {
|
||||
spot: any[]
|
||||
perp: any[]
|
||||
}
|
||||
set: (x: (x: MangoStore) => void) => void
|
||||
actions: {
|
||||
fetchAllMangoAccounts: () => Promise<void>
|
||||
fetchMangoGroup: () => Promise<void>
|
||||
|
@ -267,6 +272,7 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
},
|
||||
mangoGroups: [],
|
||||
mangoAccounts: [],
|
||||
referrerPk: null,
|
||||
selectedMangoAccount: {
|
||||
current: null,
|
||||
initialLoad: true,
|
||||
|
@ -300,7 +306,10 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
submitting: false,
|
||||
success: '',
|
||||
},
|
||||
tradeHistory: [],
|
||||
tradeHistory: {
|
||||
spot: [],
|
||||
perp: [],
|
||||
},
|
||||
set: (fn) => set(produce(fn)),
|
||||
actions: {
|
||||
async fetchWalletTokens() {
|
||||
|
@ -371,16 +380,35 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
const wallet = get().wallet.current
|
||||
const actions = get().actions
|
||||
|
||||
const delegateFilter = [
|
||||
{
|
||||
memcmp: {
|
||||
offset: MangoAccountLayout.offsetOf('delegate'),
|
||||
bytes: wallet?.publicKey.toBase58(),
|
||||
},
|
||||
},
|
||||
]
|
||||
const accountSorter = (a, b) =>
|
||||
a.publicKey.toBase58() > b.publicKey.toBase58() ? 1 : -1
|
||||
|
||||
if (!wallet?.publicKey || !mangoGroup) return
|
||||
return mangoClient
|
||||
.getMangoAccountsForOwner(mangoGroup, wallet?.publicKey, true)
|
||||
.then((mangoAccounts) => {
|
||||
if (mangoAccounts.length > 0) {
|
||||
|
||||
return Promise.all([
|
||||
mangoClient.getMangoAccountsForOwner(
|
||||
mangoGroup,
|
||||
wallet?.publicKey,
|
||||
true
|
||||
),
|
||||
mangoClient.getAllMangoAccounts(mangoGroup, delegateFilter, false),
|
||||
])
|
||||
.then((values) => {
|
||||
const [mangoAccounts, delegatedAccounts] = values
|
||||
console.log(mangoAccounts.length, delegatedAccounts.length)
|
||||
if (mangoAccounts.length + delegatedAccounts.length > 0) {
|
||||
const sortedAccounts = mangoAccounts
|
||||
.slice()
|
||||
.sort((a, b) =>
|
||||
a.publicKey.toBase58() > b.publicKey.toBase58() ? 1 : -1
|
||||
)
|
||||
.sort(accountSorter)
|
||||
.concat(delegatedAccounts.sort(accountSorter))
|
||||
|
||||
set((state) => {
|
||||
state.selectedMangoAccount.initialLoad = false
|
||||
|
@ -435,6 +463,7 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
state.selectedMangoGroup.current = mangoGroup
|
||||
})
|
||||
})
|
||||
|
||||
const allMarketConfigs = getAllMarkets(mangoGroupConfig)
|
||||
const allMarketPks = allMarketConfigs.map((m) => m.publicKey)
|
||||
const allBidsAndAsksPks = allMarketConfigs
|
||||
|
@ -530,7 +559,7 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
const perpHistory = jsonPerpHistory?.data || []
|
||||
|
||||
set((state) => {
|
||||
state.tradeHistory = [...state.tradeHistory, ...perpHistory]
|
||||
state.tradeHistory.perp = perpHistory
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
|
@ -553,11 +582,10 @@ const useMangoStore = create<MangoStore>((set, get) => {
|
|||
})
|
||||
)
|
||||
.then((serumTradeHistory) => {
|
||||
console.log('serum Trade History', serumTradeHistory)
|
||||
|
||||
set((state) => {
|
||||
state.tradeHistory = [
|
||||
...serumTradeHistory,
|
||||
...state.tradeHistory,
|
||||
]
|
||||
state.tradeHistory.spot = serumTradeHistory
|
||||
})
|
||||
})
|
||||
.catch((e) => {
|
||||
|
|
|
@ -79,16 +79,24 @@ h3 {
|
|||
}
|
||||
|
||||
p {
|
||||
@apply text-th-fgd-3 mb-2.5;
|
||||
@apply text-sm text-th-fgd-3 mb-2;
|
||||
}
|
||||
|
||||
a {
|
||||
@apply text-th-primary transition-all duration-300 hover:text-th-primary-dark;
|
||||
@apply text-th-primary hover:text-th-primary-dark;
|
||||
}
|
||||
|
||||
li {
|
||||
@apply text-sm text-th-fgd-3 mb-2;
|
||||
}
|
||||
|
||||
tbody {
|
||||
@apply border-t border-th-bkg-4;
|
||||
}
|
||||
|
||||
button {
|
||||
transition: all 0.3s ease;
|
||||
@apply font-semibold rounded-md tracking-wider;
|
||||
@apply rounded-md tracking-wider;
|
||||
}
|
||||
|
||||
button.transition-none {
|
||||
|
|
|
@ -31,7 +31,7 @@ module.exports = {
|
|||
red: { DEFAULT: '#CC2929', dark: '#AA2222', muted: '#eba9a9' },
|
||||
green: { DEFAULT: '#5EBF4D', dark: '#4BA53B', muted: '#bfe5b8' },
|
||||
'bkg-1': '#f7f7f7',
|
||||
'bkg-2': '#FFFFFF',
|
||||
'bkg-2': '#FDFDFD',
|
||||
'bkg-3': '#F0F0F0',
|
||||
'bkg-4': '#E6E6E6',
|
||||
'fgd-1': '#061f23',
|
||||
|
@ -51,8 +51,8 @@ module.exports = {
|
|||
'bkg-2': '#1B1B1F',
|
||||
'bkg-3': '#27272B',
|
||||
'bkg-4': '#38383D',
|
||||
'fgd-1': '#E1E1E1',
|
||||
'fgd-2': '#D1D1D1',
|
||||
'fgd-1': '#D1D1D1',
|
||||
'fgd-2': '#C8C8C8',
|
||||
'fgd-3': '#B3B3B3',
|
||||
'fgd-4': '#878787',
|
||||
},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { I80F48 } from '@blockworks-foundation/mango-client/lib/src/fixednum'
|
||||
import { I80F48 } from '@blockworks-foundation/mango-client'
|
||||
import { TOKEN_MINTS } from '@project-serum/serum'
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import BN from 'bn.js'
|
||||
|
@ -312,3 +312,10 @@ export function getIsDocumentHidden() {
|
|||
export const numberCompactFormatter = Intl.NumberFormat('en', {
|
||||
notation: 'compact',
|
||||
})
|
||||
|
||||
export function patchInternalMarketName(marketName: string) {
|
||||
if (marketName.includes('/USDC')) {
|
||||
marketName = marketName.replace('/USDC', '-SPOT')
|
||||
}
|
||||
return marketName
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ export async function deposit({
|
|||
const wallet = useMangoStore.getState().wallet.current
|
||||
const tokenIndex = mangoGroup.getTokenIndex(fromTokenAcc.mint)
|
||||
const mangoClient = useMangoStore.getState().connection.client
|
||||
const referrer = useMangoStore.getState().referrerPk
|
||||
console.log('referrerPk', referrer)
|
||||
|
||||
if (mangoAccount) {
|
||||
return await mangoClient.deposit(
|
||||
|
@ -35,6 +37,7 @@ export async function deposit({
|
|||
wallet.publicKey,
|
||||
false
|
||||
)
|
||||
console.log('in deposit and create, referrer is', referrer)
|
||||
return await mangoClient.createMangoAccountAndDeposit(
|
||||
mangoGroup,
|
||||
wallet,
|
||||
|
@ -44,7 +47,8 @@ export async function deposit({
|
|||
fromTokenAcc.publicKey,
|
||||
Number(amount),
|
||||
existingAccounts.length,
|
||||
accountName
|
||||
accountName,
|
||||
referrer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { PhantomWalletAdapter } from './phantom'
|
||||
import { SolflareExtensionWalletAdapter } from './solflare-extension'
|
||||
import { SolflareWalletAdapter } from './solflare'
|
||||
import { SolletExtensionAdapter } from './sollet-extension'
|
||||
import { SlopeWalletAdapter } from './slope'
|
||||
import { BitpieWalletAdapter } from './bitpie'
|
||||
|
@ -18,7 +18,7 @@ export const WALLET_PROVIDERS = [
|
|||
name: 'Solflare',
|
||||
url: 'https://solflare.com',
|
||||
icon: `${ASSET_URL}/solflare.svg`,
|
||||
adapter: SolflareExtensionWalletAdapter,
|
||||
adapter: SolflareWalletAdapter,
|
||||
},
|
||||
{
|
||||
name: 'Sollet.io',
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
import EventEmitter from 'eventemitter3'
|
||||
import { PublicKey, Transaction } from '@solana/web3.js'
|
||||
import { notify } from '../../utils/notifications'
|
||||
import { DEFAULT_PUBLIC_KEY, WalletAdapter } from '../../@types/types'
|
||||
|
||||
type SolflareExtensionEvent = 'disconnect' | 'connect'
|
||||
type SolflareExtensionRequestMethod =
|
||||
| 'connect'
|
||||
| 'disconnect'
|
||||
| 'signTransaction'
|
||||
| 'signAllTransactions'
|
||||
|
||||
interface SolflareExtensionProvider {
|
||||
publicKey?: PublicKey
|
||||
isConnected?: boolean
|
||||
autoApprove?: boolean
|
||||
signTransaction: (transaction: Transaction) => Promise<Transaction>
|
||||
signAllTransactions: (transactions: Transaction[]) => Promise<Transaction[]>
|
||||
connect: () => Promise<void>
|
||||
disconnect: () => Promise<void>
|
||||
on: (event: SolflareExtensionEvent, handler: (args: any) => void) => void
|
||||
off: (event: SolflareExtensionEvent, handler: (args: any) => void) => void
|
||||
request: (method: SolflareExtensionRequestMethod, params: any) => Promise<any>
|
||||
}
|
||||
|
||||
export class SolflareExtensionWalletAdapter
|
||||
extends EventEmitter
|
||||
implements WalletAdapter
|
||||
{
|
||||
constructor() {
|
||||
super()
|
||||
this.connect = this.connect.bind(this)
|
||||
}
|
||||
|
||||
private get _provider(): SolflareExtensionProvider | undefined {
|
||||
if ((window as any)?.solflare?.isSolflare) {
|
||||
return (window as any).solflare
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
private _handleConnect = (...args) => {
|
||||
this.emit('connect', ...args)
|
||||
}
|
||||
|
||||
private _handleDisconnect = (...args) => {
|
||||
this._provider?.off('connect', this._handleConnect)
|
||||
this._provider?.off('disconnect', this._handleDisconnect)
|
||||
this.emit('disconnect', ...args)
|
||||
}
|
||||
|
||||
get connected() {
|
||||
return this._provider?.isConnected || false
|
||||
}
|
||||
|
||||
get autoApprove() {
|
||||
return this._provider?.autoApprove || false
|
||||
}
|
||||
|
||||
async signAllTransactions(
|
||||
transactions: Transaction[]
|
||||
): Promise<Transaction[]> {
|
||||
if (!this._provider) {
|
||||
return transactions
|
||||
}
|
||||
|
||||
return this._provider.signAllTransactions(transactions)
|
||||
}
|
||||
|
||||
get publicKey() {
|
||||
const instance = this._provider?.publicKey
|
||||
|
||||
if (instance) {
|
||||
return new PublicKey(instance.toString())
|
||||
}
|
||||
|
||||
return DEFAULT_PUBLIC_KEY
|
||||
}
|
||||
|
||||
async signTransaction(transaction: Transaction) {
|
||||
if (!this._provider) {
|
||||
return transaction
|
||||
}
|
||||
|
||||
return this._provider.signTransaction(transaction)
|
||||
}
|
||||
|
||||
async connect() {
|
||||
if (!this._provider) {
|
||||
notify({
|
||||
title: 'Solflare Extension Error',
|
||||
type: 'error',
|
||||
description:
|
||||
'Please install the Solflare Extension and then reload this page.',
|
||||
})
|
||||
return
|
||||
}
|
||||
this._provider?.on('connect', this._handleConnect)
|
||||
this._provider?.on('disconnect', this._handleDisconnect)
|
||||
return this._provider?.connect()
|
||||
}
|
||||
|
||||
async disconnect() {
|
||||
if (this._provider) {
|
||||
this._provider.disconnect()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
import Solflare from '@solflare-wallet/sdk'
|
||||
|
||||
export function SolflareWalletAdapter(_, network) {
|
||||
return new Solflare({ network })
|
||||
}
|
Loading…
Reference in New Issue