merge main

This commit is contained in:
saml33 2021-07-29 12:30:25 +10:00
commit e278b9d07f
31 changed files with 877 additions and 520 deletions

View File

@ -0,0 +1,116 @@
import { FunctionComponent, useState } from 'react'
import useMangoStore from '../stores/useMangoStore'
import {
ExclamationCircleIcon,
InformationCircleIcon,
} from '@heroicons/react/outline'
import Input from './Input'
import Button from './Button'
import Modal from './Modal'
import { ElementTitle } from './styles'
import Tooltip from './Tooltip'
import useConnection from '../hooks/useConnection'
import { PublicKey } from '@solana/web3.js'
// import { addMarginAccountInfo } from '../utils/mango'
import { notify } from '../utils/notifications'
interface AccountNameModalProps {
accountName?: string
isOpen: boolean
onClose?: (x?) => void
}
const AccountNameModal: FunctionComponent<AccountNameModalProps> = ({
accountName,
isOpen,
onClose,
}) => {
const [name, setName] = useState(accountName || '')
const [invalidNameMessage, setInvalidNameMessage] = useState('')
const wallet = useMangoStore.getState().wallet.current
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const actions = useMangoStore((s) => s.actions)
const { connection, programId } = useConnection()
const submitName = async () => {
// addMarginAccountInfo(
// connection,
// new PublicKey(programId),
// selectedMangoGroup,
// mangoAccount,
// wallet,
// name
// )
// .then(() => {
// actions.fetchMarginAccounts()
// onClose()
// })
// .catch((err) => {
// console.warn('Error setting account name:', err)
// notify({
// message: 'Could not set account name',
// description: `${err}`,
// txid: err.txid,
// type: 'error',
// })
// })
}
const validateNameInput = () => {
if (name.length >= 33) {
setInvalidNameMessage('Account name must be 32 characters or less')
}
if (name.length === 0) {
setInvalidNameMessage('Enter an account name')
}
}
const onChangeNameInput = (name) => {
setName(name)
if (invalidNameMessage) {
setInvalidNameMessage('')
}
}
return (
<Modal onClose={onClose} isOpen={isOpen}>
<Modal.Header>
<div className="flex items-center">
<ElementTitle noMarignBottom>Name your Account</ElementTitle>
</div>
</Modal.Header>
<div className="flex items-center justify-center text-th-fgd-3 pb-4">
Edit the public nickname for your account
<Tooltip content="Account names are stored on-chain">
<InformationCircleIcon className="h-5 w-5 ml-2 text-th-primary" />
</Tooltip>
</div>
<div className="pb-2 text-th-fgd-1">Account Name</div>
<Input
type="text"
className={`border border-th-fgd-4 flex-grow`}
error={!!invalidNameMessage}
placeholder="e.g. Calypso"
value={name}
onBlur={validateNameInput}
onChange={(e) => onChangeNameInput(e.target.value)}
/>
{invalidNameMessage ? (
<div className="flex items-center pt-1.5 text-th-red">
<ExclamationCircleIcon className="h-4 w-4 mr-1.5" />
{invalidNameMessage}
</div>
) : null}
<Button
onClick={() => submitName()}
disabled={name.length >= 33}
className="mt-4 w-full"
>
Save Name
</Button>
</Modal>
)
}
export default AccountNameModal

View File

@ -11,7 +11,7 @@ import {
MangoAccount,
MangoCache,
MangoGroup,
ZERO_I80F48,
// ZERO_I80F48,
} from '@blockworks-foundation/mango-client'
import { abbreviateAddress } from '../utils'
import useLocalStorageState from '../hooks/useLocalStorageState'
@ -187,17 +187,17 @@ const AccountInfo = ({
}) => {
const accountEquity = mangoAccount.computeValue(mangoGroup, mangoCache)
const leverage = accountEquity.gt(ZERO_I80F48)
? mangoAccount
.getLiabsVal(mangoGroup, mangoCache)
.div(accountEquity)
.toFixed(2)
: '0.00'
// const leverage = accountEquity.gt(ZERO_I80F48)
// ? mangoAccount
// .getLiabsVal(mangoGroup, mangoCache)
// .div(accountEquity)
// .toFixed(2)
// : '0.00'
return (
<div className="text-th-fgd-3 text-xs">
${accountEquity.toFixed(2)}
<span className="px-1.5 text-th-fgd-4">|</span>
{/* <span className="px-1.5 text-th-fgd-4">|</span>
<span
className={
parseFloat(leverage) > 4
@ -208,7 +208,7 @@ const AccountInfo = ({
}
>
{leverage}x
</span>
</span> */}
</div>
)
}

View File

@ -7,11 +7,15 @@ import { InformationCircleIcon } from '@heroicons/react/outline'
import Tooltip from './Tooltip'
import { sleep } from '../utils'
import { Market } from '@project-serum/serum'
import { ZERO_I80F48 } from '@blockworks-foundation/mango-client'
import {
getTokenBySymbol,
ZERO_I80F48,
} from '@blockworks-foundation/mango-client'
const BalancesTable = () => {
const balances = useBalances()
const actions = useMangoStore((s) => s.actions)
const mangoGroupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
async function handleSettleAll() {
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
@ -117,53 +121,59 @@ const BalancesTable = () => {
</Tr>
</Thead>
<Tbody>
{balances.map((balance, index) => (
<Tr
key={`${index}`}
className={`border-b border-th-bkg-3
{balances.map((balance, index) => {
const tokenConfig = getTokenBySymbol(
mangoGroupConfig,
balance.symbol.toUpperCase()
)
return (
<Tr
key={`${index}`}
className={`border-b border-th-bkg-3
${index % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
`}
>
<Td
className={`flex items-center px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${balance.symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<Td
className={`flex items-center px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${balance.symbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
{balance.symbol}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.marginDeposits.toFixed()}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.borrows.toFixed()}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.orders}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.unsettled}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.net.toFixed()}
</Td>
</Tr>
))}
{balance.symbol}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.marginDeposits.toFixed(tokenConfig.decimals)}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.borrows.toFixed(tokenConfig.decimals)}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.orders}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.unsettled}
</Td>
<Td
className={`px-4 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{balance.net.toFixed(tokenConfig.decimals)}
</Td>
</Tr>
)
})}
</Tbody>
</Table>
</div>

View File

@ -44,8 +44,8 @@ const ConnectWalletButton = () => {
{connected && wallet?.publicKey ? (
<Menu>
<div className="relative h-full">
<Menu.Button className="bg-th-fgd-4 flex items-center justify-center rounded-full w-9 h-9 text-th-fgd-2 focus:outline-none hover:bg-th-bkg-3 hover:text-th-fgd-3">
<ProfileIcon className="fill-current h-5 w-5" />
<Menu.Button className="bg-th-fgd-4 flex items-center justify-center rounded-full w-10 h-10 text-th-fgd-2 focus:outline-none hover:bg-th-bkg-3 hover:text-th-fgd-3">
<ProfileIcon className="fill-current h-6 w-6" />
</Menu.Button>
<Menu.Items className="bg-th-bkg-1 mt-2 p-1 absolute right-0 shadow-lg outline-none rounded-md w-48 z-20">
<Menu.Item>

View File

@ -1,11 +1,7 @@
import { StyledMarketInfoLabel } from './MarketHeader'
const DayHighLow = () => {
return (
<div className="pr-6">
<StyledMarketInfoLabel className="text-center text-th-fgd-3">
24h Range
</StyledMarketInfoLabel>
<div className="text-center text-th-fgd-3 tiny-text">24h Range</div>
<div className="flex items-center">
<div className="pr-2 text-th-fgd-1 text-xs">$XX.XX</div>
<div className="h-1.5 flex rounded bg-th-bkg-3 w-24">

View File

@ -5,27 +5,13 @@ import { DotsHorizontalIcon } from '@heroicons/react/outline'
import FloatingElement from './FloatingElement'
import { ElementTitle } from './styles'
import useMangoStore from '../stores/useMangoStore'
import {
abbreviateAddress,
divideBnToNumber,
i80f48ToPercent,
tokenPrecision,
} from '../utils/index'
import DepositModal from './DepositModal'
import WithdrawModal from './WithdrawModal'
// import BorrowModal from './BorrowModal'
import Button from './Button'
import Tooltip from './Tooltip'
import AccountsModal from './AccountsModal'
export default function MarginBalances() {
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const selectedMangoGroupConfig = useMangoStore(
(s) => s.selectedMangoGroup.config
)
const selectedMangoGroupCache = useMangoStore(
(s) => s.selectedMangoGroup.cache
)
const selectedMangoAccount = useMangoStore(
(s) => s.selectedMangoAccount.current
)
@ -58,24 +44,10 @@ export default function MarginBalances() {
return (
<>
<FloatingElement>
<div className="flex justify-between pb-5">
<div className="w-8 h-8" />
<div className="flex flex-col items-center">
<ElementTitle noMarignBottom>Mango Account</ElementTitle>
{selectedMangoAccount ? (
<Link href={'/account'}>
<a className="pt-1 text-th-fgd-3 text-xs underline hover:no-underline">
{selectedMangoAccount?.publicKey.toString()}
</a>
</Link>
) : connected ? (
<div className="pt-1 text-th-fgd-3">
Deposit funds to get started
</div>
) : null}
</div>
<Menu>
<div className="relative h-full">
<div className="flex justify-center">
<ElementTitle noMarignBottom>Mango Account</ElementTitle>
<div className="absolute right-0 pr-4">
<Menu>
<Menu.Button
className="flex items-center justify-center rounded-full bg-th-bkg-3 w-8 h-8 hover:text-th-primary focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
disabled={!connected}
@ -118,10 +90,23 @@ export default function MarginBalances() {
</button>
</Menu.Item> */}
</Menu.Items>
</div>
</Menu>
</Menu>
</div>
</div>
<div className={`flex justify-center items-center`}>
<div className="flex justify-center mt-2">
{selectedMangoAccount ? (
<Link href={'/account'}>
<a className="pt-1 text-th-fgd-3 text-xs underline hover:no-underline">
{selectedMangoAccount?.publicKey.toString()}
</a>
</Link>
) : connected ? (
<div className="pt-1 text-th-fgd-3">
Deposit funds to get started
</div>
) : null}
</div>
<div className="flex justify-center items-center mt-2">
<Button
onClick={() => setShowDepositModal(true)}
className="w-1/2"
@ -149,10 +134,6 @@ export default function MarginBalances() {
onClose={handleCloseWithdraw}
/>
)}
{/* {showBorrowModal && (
<BorrowModal isOpen={showBorrowModal} onClose={handleCloseBorrow} />
)} */}
{showAccountsModal ? (
<AccountsModal
onClose={handleCloseAccounts}

View File

@ -19,39 +19,35 @@ export default function MarginInfo() {
? mangoAccount.computeValue(mangoGroup, mangoCache)
: ZERO_I80F48
const leverage =
mangoAccount && equity.gt(ZERO_I80F48)
? mangoAccount.getLiabsVal(mangoGroup, mangoCache).div(equity)
: 0.0
// const leverage =
// mangoAccount && equity.gt(ZERO_I80F48)
// ? mangoAccount.getLiabsVal(mangoGroup, mangoCache).div(equity)
// : 0.0
return (
<FloatingElement showConnect>
<div className={!connected && 'filter blur-sm'}>
<div className={!connected ? 'filter blur-sm' : undefined}>
<ElementTitle>Account</ElementTitle>
<div>
<div>
<div className={`flex justify-between pt-2 pb-2`}>
<div className="flex justify-between pt-2 pb-2">
<Tooltip content="Account value">
<div
className={`cursor-help font-normal text-th-fgd-3 border-b border-th-fgd-3 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
<div className="cursor-help font-normal text-th-fgd-3 border-b border-th-fgd-3 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3">
Equity
</div>
</Tooltip>
<div className={`text-th-fgd-1`}>${equity.toFixed(2)}</div>
<div className="text-th-fgd-1">${equity.toFixed(2)}</div>
</div>
<div className={`flex justify-between pt-2 pb-2`}>
{/* <div className="flex justify-between pt-2 pb-2">
<div className="font-normal text-th-fgd-3 leading-4">
Leverage
</div>
<div className={`text-th-fgd-1`}>{leverage.toFixed(2)}x</div>
</div>
<div className="text-th-fgd-1">{leverage.toFixed(2)}x</div>
</div> */}
<div className={`flex justify-between pt-2 pb-2`}>
<Tooltip content="Leverage">
<div className="font-normal text-th-fgd-3 leading-4">
Total Assets Value
</div>
</Tooltip>
<div className="font-normal text-th-fgd-3 leading-4">
Total Assets Value
</div>
<div className={`text-th-fgd-1`}>
$
{mangoAccount
@ -60,11 +56,9 @@ export default function MarginInfo() {
</div>
</div>
<div className={`flex justify-between pt-2 pb-2`}>
<Tooltip content="Leverage">
<div className="font-normal text-th-fgd-3 leading-4">
Total Liabilities Value
</div>
</Tooltip>
<div className="font-normal text-th-fgd-3 leading-4">
Total Liabilities Value
</div>
<div className={`text-th-fgd-1`}>
$
{mangoAccount
@ -73,14 +67,36 @@ export default function MarginInfo() {
</div>
</div>
<div className={`flex justify-between pt-2 pb-2`}>
<div className="font-normal text-th-fgd-3 leading-4">
Maint Health
</div>
<div className={`text-th-fgd-1`}>
{mangoAccount
? mangoAccount
.getHealth(mangoGroup, mangoCache, 'Maint')
.toFixed(2)
: 0}
</div>
</div>
<div className={`flex justify-between pt-2 pb-2`}>
<div className="font-normal text-th-fgd-3 leading-4">
Init Health
</div>
<div className={`text-th-fgd-1`}>
{mangoAccount
? mangoAccount
.getHealth(mangoGroup, mangoCache, 'Init')
.toFixed(2)
: 0}
</div>
</div>
{/* <div className="flex justify-between pt-2 pb-2">
<Tooltip content="Must be above 0% to borrow funds">
<div
className={`cursor-help font-normal text-th-fgd-3 border-b border-th-fgd-3 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
<div className="cursor-help font-normal text-th-fgd-3 border-b border-th-fgd-3 border-dashed border-opacity-20 leading-4 default-transition hover:border-th-bkg-2 hover:text-th-fgd-3">
Init Ratio
</div>
</Tooltip>
<div className={`text-th-fgd-1`}>
<div className="text-th-fgd-1">
{mangoAccount
? mangoAccount
.getHealthRatio(mangoGroup, mangoCache, 'Init')
@ -88,7 +104,7 @@ export default function MarginInfo() {
: 100.0}
%
</div>
</div>
</div> */}
</div>
<div className="border border-th-bkg-3 mt-4 p-4 rounded">
<div className="flex flex-col">

View File

@ -1,5 +1,4 @@
import React, { useCallback, useMemo, useState } from 'react'
import styled from '@emotion/styled'
import useMangoStore from '../stores/useMangoStore'
import usePrevious from '../hooks/usePrevious'
import useInterval from '../hooks/useInterval'
@ -9,10 +8,6 @@ import ManualRefresh from './ManualRefresh'
import useOraclePrice from '../hooks/useOraclePrice'
import DayHighLow from './DayHighLow'
export const StyledMarketInfoLabel = styled.div`
font-size: 0.7rem;
`
const MarketHeader = () => {
const oraclePrice = useOraclePrice()
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
@ -75,7 +70,7 @@ const MarketHeader = () => {
return (
<div
className={`flex items-end sm:items-center justify-between pt-4 px-6 md:px-6`}
className={`flex items-end sm:items-center justify-between pt-4 px-6 md:pb-1 md:pt-8 md:px-6`}
>
<div className="flex flex-col sm:flex-row sm:items-center">
<div className="pb-3 sm:pb-0 pr-8">
@ -99,17 +94,13 @@ const MarketHeader = () => {
</div>
<div className="flex items-center">
<div className="pr-6">
<StyledMarketInfoLabel className="text-th-fgd-3">
Oracle price
</StyledMarketInfoLabel>
<div className="text-th-fgd-3 tiny-text">Oracle price</div>
<div className="font-semibold text-th-fgd-1 text-xs">
{oraclePrice ? oraclePrice.toFixed(2) : '--'}
</div>
</div>
<div className="pr-4">
<StyledMarketInfoLabel className="text-th-fgd-3">
24h Change
</StyledMarketInfoLabel>
<div className="text-th-fgd-3 tiny-text">24h Change</div>
{ohlcv && !loading ? (
<div
className={`font-semibold text-xs ${
@ -128,15 +119,15 @@ const MarketHeader = () => {
)}
</div>
<div className="pr-6">
<StyledMarketInfoLabel className="text-th-fgd-3">
24h Vol
</StyledMarketInfoLabel>
<div className="text-th-fgd-3 tiny-text">24h Vol</div>
<div className="font-semibold text-th-fgd-1 text-xs">
{ohlcv && !loading && volume ? (
volume !== '--' ? (
<>
{volume.toFixed(2)}
<span className="ml-1 text-th-fgd-3">{baseSymbol}</span>
<span className="ml-1 text-th-fgd-3 tiny-text">
{baseSymbol}
</span>
</>
) : (
volume
@ -150,17 +141,13 @@ const MarketHeader = () => {
{selectedMarketName.includes('PERP') ? (
<>
<div className="pr-6">
<StyledMarketInfoLabel className="text-th-fgd-3">
Funding (8h Ave)
</StyledMarketInfoLabel>
<div className="text-th-fgd-3 tiny-text">Funding (8h Ave)</div>
<div className="font-semibold text-th-fgd-1 text-xs">
0.001%
</div>
</div>
<div className="pr-6">
<StyledMarketInfoLabel className="text-th-fgd-3">
Open Interest
</StyledMarketInfoLabel>
<div className="text-th-fgd-3 tiny-text">Open Interest</div>
<div className="font-semibold text-th-fgd-1 text-xs">$XXXm</div>
</div>
</>

View File

@ -19,6 +19,7 @@ import {
StepMonoIcon,
SushiMonoIcon,
UniMonoIcon,
UsdtMonoIcon,
} from './icons'
const symbolIcons = {
@ -38,6 +39,7 @@ const symbolIcons = {
StepMonoIcon,
SushiMonoIcon,
UniMonoIcon,
UsdtMonoIcon,
}
export default function MarketMenuItem({ menuTitle = '', linksArray = [] }) {

View File

@ -125,8 +125,10 @@ export default function MarketPosition() {
<span className="text-th-fgd-2">{symbol}</span>
</div>
</div>
<div className="pb-2">
<div className="text-th-fgd-3 tiny-text">Deposits</div>
<div className="pb-3">
<div className="pb-0.5 text-th-fgd-3 text-xs">
Deposits
</div>
<div className={`text-th-fgd-1`}>
{selectedMangoAccount
? selectedMangoAccount
@ -141,8 +143,10 @@ export default function MarketPosition() {
: (0).toFixed(tokenPrecision[symbol])}
</div>
</div>
<div className="pb-2">
<div className="text-th-fgd-3 tiny-text">Borrows</div>
<div className="pb-3">
<div className="pb-0.5 text-th-fgd-3 text-xs">
Borrows
</div>
<div className={`text-th-fgd-1`}>
{selectedMangoAccount
? selectedMangoAccount
@ -160,7 +164,7 @@ export default function MarketPosition() {
{/* <div className="w-1/4">
<Tooltip content="Maximum available with leverage">
<div
className={`cursor-help font-normal pb-0.5 text-th-fgd-3 tiny-text default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
className={`cursor-help font-normal pb-0.5 text-th-fgd-3 text-xs default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
Available
</div>
@ -170,7 +174,7 @@ export default function MarketPosition() {
<div>
<Tooltip content="Deposit APY and Borrow APR">
<div
className={`cursor-help font-normal pb-0.5 text-th-fgd-3 tiny-text default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
className={`cursor-help font-normal pb-0.5 text-th-fgd-3 text-xs default-transition hover:border-th-bkg-2 hover:text-th-fgd-3`}
>
Interest Rates
</div>

View File

@ -6,6 +6,15 @@ import { LinkButton } from './Button'
import MarketsModal from './MarketsModal'
import useLocalStorageState from '../hooks/useLocalStorageState'
const StyledMarketSelectWrapper = styled.div`
-ms-overflow-style: none;
scrollbar-width: none;
::-webkit-scrollbar {
display: none;
}
`
const StyledMarketTypeToggleWrapper = styled.div`
background: rgba(255, 255, 255, 0.12);
`
@ -44,7 +53,7 @@ const MarketSelect = () => {
return (
<>
<div className="bg-th-bkg-3 flex h-10">
<StyledMarketSelectWrapper className="bg-th-bkg-3 flex h-10">
<StyledMarketTypeToggleWrapper className="flex items-center pl-6 md:pl-9 pr-1">
<LinkButton
className="font-normal text-th-fgd-2 text-xs"
@ -67,7 +76,7 @@ const MarketSelect = () => {
))}
</div>
</div>
</div>
</StyledMarketSelectWrapper>
{showMarketsModal ? (
<MarketsModal
isOpen={showMarketsModal}

View File

@ -1,5 +1,8 @@
import React, { FunctionComponent, useEffect, useState } from 'react'
import { ExclamationCircleIcon } from '@heroicons/react/outline'
import React, { FunctionComponent, useEffect, useMemo, useState } from 'react'
import {
ExclamationCircleIcon,
InformationCircleIcon,
} from '@heroicons/react/outline'
// import {
// nativeToUi,
// sleep,
@ -18,6 +21,7 @@ import {
import Loading from './Loading'
import Button from './Button'
import Slider from './Slider'
import Tooltip from './Tooltip'
import { notify } from '../utils/notifications'
import useMangoGroupConfig from '../hooks/useMangoGroupConfig'
import { deposit } from '../utils/mango'
@ -36,6 +40,9 @@ const NewAccount: FunctionComponent<NewAccountProps> = ({
const [invalidAmountMessage, setInvalidAmountMessage] = useState('')
const [sliderPercentage, setSliderPercentage] = useState(0)
const [maxButtonTransition, setMaxButtonTransition] = useState(false)
const [showNewAccountName, setShowNewAccountName] = useState(true)
const [invalidNameMessage, setInvalidNameMessage] = useState('')
const [name, setName] = useState('')
const walletTokens = useMangoStore((s) => s.wallet.tokens)
const actions = useMangoStore((s) => s.actions)
@ -116,6 +123,22 @@ const NewAccount: FunctionComponent<NewAccountProps> = ({
validateAmountInput(amount)
}
const validateNameInput = () => {
if (name.length >= 33) {
setInvalidNameMessage('Account name must be 32 characters or less')
}
if (name.length === 0) {
setInvalidNameMessage('Enter an account name')
}
}
const onChangeNameInput = (name) => {
setName(name)
if (invalidNameMessage) {
setInvalidNameMessage('')
}
}
// turn off slider transition for dragging slider handle interaction
useEffect(() => {
if (maxButtonTransition) {
@ -125,64 +148,103 @@ const NewAccount: FunctionComponent<NewAccountProps> = ({
return (
<>
<ElementTitle noMarignBottom>Create Margin Account</ElementTitle>
<div className="text-th-fgd-3 text-center pb-4 pt-2">
Make a deposit to initialize a new margin account
</div>
<AccountSelect
accounts={walletTokens}
selectedAccount={selectedAccount}
onSelectAccount={handleAccountSelect}
/>
<div className="flex justify-between pb-2 pt-4">
<div className={`text-th-fgd-1`}>Amount</div>
<div
className="text-th-fgd-1 underline cursor-pointer default-transition hover:text-th-primary hover:no-underline"
onClick={setMaxForSelectedAccount}
>
Max
</div>
</div>
<div className="flex">
<Input
type="number"
min="0"
className={`border border-th-fgd-4 flex-grow pr-11`}
placeholder="0.00"
error={!!invalidAmountMessage}
onBlur={(e) => validateAmountInput(e.target.value)}
value={inputAmount}
onChange={(e) => onChangeAmountInput(e.target.value)}
suffix={symbol}
/>
</div>
{invalidAmountMessage ? (
<div className="flex items-center pt-1.5 text-th-red">
<ExclamationCircleIcon className="h-4 w-4 mr-1.5" />
{invalidAmountMessage}
</div>
) : null}
<div className="pt-3 pb-4">
<Slider
value={sliderPercentage}
onChange={(v) => onChangeSlider(v)}
step={1}
maxButtonTransition={maxButtonTransition}
/>
</div>
<div className={`pt-8 flex justify-center`}>
<Button
disabled={inputAmount <= 0 || inputAmount > selectedAccount.uiBalance}
onClick={handleNewAccountDeposit}
className="w-full"
>
<div className={`flex items-center justify-center`}>
{submitting && <Loading className="-ml-1 mr-3" />}
Create Account
<ElementTitle noMarignBottom>New Account</ElementTitle>
{/* {showNewAccountName ? (
<>
<div className="flex items-center justify-center text-th-fgd-3 pb-4 pt-2">
Create a public nickname for your account
<Tooltip content="Account names are stored on-chain">
<InformationCircleIcon className="h-5 w-5 ml-2 text-th-primary" />
</Tooltip>
</div>
</Button>
</div>
<div className="pb-2 text-th-fgd-1">
Account Name <span className="text-th-fgd-3">(Optional)</span>
</div>
<Input
type="text"
className={`border border-th-fgd-4 flex-grow`}
error={!!invalidNameMessage}
placeholder="e.g. Calypso"
value={name}
onBlur={validateNameInput}
onChange={(e) => onChangeNameInput(e.target.value)}
/>
{invalidNameMessage ? (
<div className="flex items-center pt-1.5 text-th-red">
<ExclamationCircleIcon className="h-4 w-4 mr-1.5" />
{invalidNameMessage}
</div>
) : null}
<Button
onClick={() => setShowNewAccountName(false)}
disabled={name.length >= 33}
className="mt-4 w-full"
>
Next
</Button>
</>
) : ( */}
<>
<div className="text-th-fgd-3 text-center pb-4 pt-2">
Make a deposit to initialize your new account
</div>
<AccountSelect
accounts={walletTokens}
selectedAccount={selectedAccount}
onSelectAccount={handleAccountSelect}
/>
<div className="flex justify-between pb-2 pt-4">
<div className={`text-th-fgd-1`}>Amount</div>
<div
className="text-th-fgd-1 underline cursor-pointer default-transition hover:text-th-primary hover:no-underline"
onClick={setMaxForSelectedAccount}
>
Max
</div>
</div>
<div className="flex">
<Input
type="number"
min="0"
className={`border border-th-fgd-4 flex-grow pr-11`}
placeholder="0.00"
error={!!invalidAmountMessage}
onBlur={(e) => validateAmountInput(e.target.value)}
value={inputAmount}
onChange={(e) => onChangeAmountInput(e.target.value)}
suffix={symbol}
/>
</div>
{invalidAmountMessage ? (
<div className="flex items-center pt-1.5 text-th-red">
<ExclamationCircleIcon className="h-4 w-4 mr-1.5" />
{invalidAmountMessage}
</div>
) : null}
<div className="pt-3 pb-4">
<Slider
value={sliderPercentage}
onChange={(v) => onChangeSlider(v)}
step={1}
maxButtonTransition={maxButtonTransition}
/>
</div>
<div className={`pt-8 flex justify-center`}>
<Button
disabled={
inputAmount <= 0 || inputAmount > selectedAccount.uiBalance
}
onClick={handleNewAccountDeposit}
className="w-full"
>
<div className={`flex items-center justify-center`}>
{submitting && <Loading className="-ml-1 mr-3" />}
Create New Account
</div>
</Button>
</div>
</>
{/* )} */}
</>
)
}

View File

@ -188,20 +188,11 @@ const OpenOrdersTable = () => {
</Td>
<Td className={`px-4 py-1 whitespace-nowrap text-left`}>
<div className={`flex justify-end`}>
{/* Todo: support order modification */}
{/* <Button
onClick={() =>
console.log('trigger modify order modal')
}
className={`text-xs pt-0 pb-0 h-8 pl-3 pr-3`}
>
Modify
</Button> */}
<Button
onClick={() =>
handleCancelOrder(order, market.account)
}
className={`ml-3 text-xs pt-0 pb-0 h-8 pl-3 pr-3`}
className="ml-3 text-xs pt-0 pb-0 h-8 pl-3 pr-3"
>
{cancelId + '' === order.orderId + '' ? (
<Loading />

View File

@ -1,19 +1,30 @@
import useMangoStore from '../stores/useMangoStore'
import useMangoStore, { mangoClient } from '../stores/useMangoStore'
import { Table, Thead, Tbody, Tr, Th, Td } from 'react-super-responsive-table'
import {
getMarketByPublicKey,
nativeI80F48ToUi,
nativeToUi,
PerpAccount,
PerpMarket,
ZERO_I80F48,
} from '@blockworks-foundation/mango-client'
import { useMemo } from 'react'
import Button from './Button'
import { notify } from '../utils/notifications'
import { QUOTE_INDEX } from '@blockworks-foundation/mango-client/lib/src/MangoGroup'
import BN from 'bn.js'
import SideBadge from './SideBadge'
import { useState } from 'react'
import Loading from './Loading'
const PositionsTable = () => {
const actions = useMangoStore((s) => s.actions)
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
const allMarkets = useMangoStore((s) => s.selectedMangoGroup.markets)
const [settlingPerpAcc, setSettlingPerpAcc] = useState(null)
const perpMarkets = useMemo(
() =>
mangoGroup
@ -23,128 +34,187 @@ const PositionsTable = () => {
: [],
[mangoGroup]
)
const perpAccounts = useMemo(
() =>
mangoAccount
? groupConfig.perpMarkets.map(
(m) => mangoAccount.perpAccounts[m.marketIndex]
)
: [],
[mangoAccount]
const perpAccounts = mangoAccount
? groupConfig.perpMarkets.map((m) => {
return {
perpAccount: mangoAccount.perpAccounts[m.marketIndex],
marketIndex: m.marketIndex,
}
})
: []
const filteredPerpAccounts = perpAccounts.filter(
({ perpAccount }) =>
!(
perpAccount.quotePosition.eq(ZERO_I80F48) &&
perpAccount.basePosition.eq(new BN(0))
)
)
console.log({ perpMarkets, perpAccounts })
const handleSettlePnl = async (
perpMarket: PerpMarket,
perpAccount: PerpAccount
) => {
const mangoAccount = useMangoStore.getState().selectedMangoAccount.current
const mangoGroup = useMangoStore.getState().selectedMangoGroup.current
const wallet = useMangoStore.getState().wallet.current
const marketIndex = mangoGroup.getPerpMarketIndex(perpMarket.publicKey)
setSettlingPerpAcc(perpAccount)
try {
const txid = await mangoClient.settlePnl(
mangoGroup,
mangoAccount,
perpMarket,
mangoGroup.rootBankAccounts[QUOTE_INDEX],
mangoCache.priceCache[marketIndex].price,
wallet
)
actions.fetchMangoAccounts()
notify({
title: 'Successfully settled PNL',
description: '',
txid,
})
} catch (e) {
console.log('Error settling PNL: ', `${e}`, `${perpAccount}`)
notify({
title: 'Error settling PNL',
description: e.message,
txid: e.txid,
type: 'error',
})
} finally {
setSettlingPerpAcc(null)
}
}
return (
<div className={`flex flex-col py-4`}>
<div className={`-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8`}>
<div className={`align-middle inline-block min-w-full sm:px-6 lg:px-8`}>
{perpAccounts.length ? (
<div
className={`overflow-hidden border-b border-th-bkg-2 sm:rounded-md`}
>
<Table className={`min-w-full divide-y divide-th-bkg-2`}>
<div className="flex flex-col py-4">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="align-middle inline-block min-w-full sm:px-6 lg:px-8">
{filteredPerpAccounts.length ? (
<div className="overflow-hidden border-b border-th-bkg-2 sm:rounded-m">
<Table className="min-w-full divide-y divide-th-bkg-2">
<Thead>
<Tr className="text-th-fgd-3">
<Th
scope="col"
className={`px-6 py-2 text-left font-normal`}
>
Market
<Th scope="col" className="px-6 py-2 text-left font-normal">
Perp Market
</Th>
<Th
scope="col"
className={`px-6 py-2 text-left font-normal`}
>
<Th scope="col" className="px-2 py-2 text-left font-normal">
Side
</Th>
<Th scope="col" className="px-2 py-2 text-left font-normal">
Base Position
</Th>
<Th
scope="col"
className={`px-6 py-2 text-left font-normal`}
>
<Th scope="col" className="px-2 py-2 text-left font-normal">
Quote Position
</Th>
<Th
scope="col"
className={`px-6 py-2 text-left font-normal`}
>
<Th scope="col" className="px-2 py-2 text-left font-normal">
Unrealized PnL
</Th>
<Th
scope="col"
className={`px-6 py-2 text-left font-normal`}
>
<Th scope="col" className="px-2 py-2 text-left font-normal">
Health
</Th>
<Th scope="col" className={`relative px-6 py-2.5`}>
<span className={`sr-only`}>Edit</span>
</Th>
</Tr>
</Thead>
<Tbody>
{perpAccounts.map((acc, index) => {
const market = perpMarkets[index]
const marketConfig = getMarketByPublicKey(
groupConfig,
market.perpMarket
)
{filteredPerpAccounts.map(
({ perpAccount, marketIndex }, index) => {
const perpMarketInfo = perpMarkets[marketIndex]
const marketConfig = getMarketByPublicKey(
groupConfig,
perpMarketInfo.perpMarket
)
const marketCache =
mangoCache.perpMarketCache[marketConfig.marketIndex]
const price =
mangoCache.priceCache[marketConfig.marketIndex].price
const perpMarket = allMarkets[
marketConfig.publicKey.toString()
] as PerpMarket
const marketCache =
mangoCache.perpMarketCache[marketIndex]
const price = mangoCache.priceCache[marketIndex].price
const perpMarket = allMarkets[
marketConfig.publicKey.toString()
] as PerpMarket
console.log('perp account: ', perpAccount)
console.log('perp market: ', perpMarket)
return (
<Tr
key={`${index}`}
className={`border-b border-th-bkg-3
return (
<Tr
key={`${marketIndex}`}
className={`border-b border-th-bkg-3
${index % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
`}
>
<Td
className={`px-6 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{marketConfig.name}
</Td>
<Td
className={`px-6 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{perpMarket.baseLotsToNumber(acc.basePosition)}
</Td>
<Td
className={`px-6 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{nativeI80F48ToUi(
acc.quotePosition,
marketConfig.quoteDecimals
).toFixed()}
</Td>
<Td
className={`px-6 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
$
{nativeI80F48ToUi(
acc.getPnl(market, price),
marketConfig.quoteDecimals
).toFixed()}
</Td>
<Td
className={`px-6 py-2.5 whitespace-nowrap text-sm text-th-fgd-1`}
>
{acc
.getHealth(
market,
price,
market.maintAssetWeight,
market.maintLiabWeight,
marketCache.longFunding,
marketCache.shortFunding
)
.toFixed(3)}
</Td>
</Tr>
)
})}
<Td className="px-6 py-1 whitespace-nowrap text-sm text-th-fgd-1">
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${marketConfig.baseSymbol.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{marketConfig.name}</div>
</div>
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
<SideBadge
side={
perpAccount.basePosition.gt(new BN(0))
? 'long'
: 'short'
}
/>
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
{perpMarket.baseLotsToNumber(
perpAccount.basePosition
)}
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
{nativeI80F48ToUi(
perpAccount.quotePosition,
marketConfig.quoteDecimals
).toFixed()}
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
$
{nativeI80F48ToUi(
perpAccount.getPnl(perpMarketInfo, price),
marketConfig.quoteDecimals
).toFixed()}
</Td>
<Td className="px-2 py-1 whitespace-nowrap text-sm text-th-fgd-1">
{perpAccount
.getHealth(
perpMarketInfo,
price,
perpMarketInfo.maintAssetWeight,
perpMarketInfo.maintLiabWeight,
marketCache.longFunding,
marketCache.shortFunding
)
.toFixed(3)}
</Td>
<Td className="px-6 py-1 whitespace-nowrap text-sm text-th-fgd-1">
<div className="flex justify-end">
<Button
onClick={() =>
handleSettlePnl(perpMarket, perpAccount)
}
className="ml-3 text-xs pt-0 pb-0 h-8 pl-3 pr-3"
>
{settlingPerpAcc == perpAccount ? (
<Loading />
) : (
<span>Settle PNL</span>
)}
</Button>
</div>
</Td>
</Tr>
)
}
)}
</Tbody>
</Table>
</div>

View File

@ -1,15 +1,24 @@
import { useState } from 'react'
import { useCallback, useState } from 'react'
import { MenuIcon, XIcon } from '@heroicons/react/outline'
import { abbreviateAddress } from '../utils/index'
import MenuItem from './MenuItem'
import ThemeSwitch from './ThemeSwitch'
import useMangoStore from '../stores/useMangoStore'
import ConnectWalletButton from './ConnectWalletButton'
import NavDropMenu from './NavDropMenu'
import AccountsModal from './AccountsModal'
import Button from './Button'
const TopBar = () => {
const connected = useMangoStore((s) => s.wallet.connected)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
const wallet = useMangoStore((s) => s.wallet.current)
const [showMenu, setShowMenu] = useState(false)
const [showAccountsModal, setShowAccountsModal] = useState(false)
const handleCloseAccounts = useCallback(() => {
setShowAccountsModal(false)
}, [])
return (
<>
@ -47,8 +56,21 @@ const TopBar = () => {
<div className={`pl-2`}>
<ThemeSwitch />
</div>
{mangoAccount ? (
<div className="pl-3">
<Button
className="pb-1 pt-1 pl-2 pr-2 text-xs"
onClick={() => setShowAccountsModal(true)}
>
<div className="font-normal text-th-primary tiny-text">
Account
</div>
{abbreviateAddress(mangoAccount.publicKey)}
</Button>
</div>
) : null}
<div className="flex">
<div className="hidden md:block pl-4">
<div className="hidden md:block pl-3">
<ConnectWalletButton />
</div>
</div>
@ -106,6 +128,12 @@ const TopBar = () => {
</div>
</div>
</nav>
{showAccountsModal ? (
<AccountsModal
onClose={handleCloseAccounts}
isOpen={showAccountsModal}
/>
) : null}
</>
)
}

View File

@ -1,4 +1,5 @@
import { ArrowSmDownIcon } from '@heroicons/react/solid'
import BN from 'bn.js'
import useTradeHistory from '../hooks/useTradeHistory'
import Link from 'next/link'
import { useRouter } from 'next/router'
@ -14,8 +15,14 @@ const TradeHistoryTable = () => {
const { items, requestSort, sortConfig } = useSortableData(tradeHistory)
const marketConfig = useMangoStore((s) => s.selectedMarket.config)
const renderTradeDateTime = (timestamp) => {
const date = new Date(timestamp)
const renderTradeDateTime = (timestamp: BN | string) => {
let date
if (timestamp instanceof BN) {
date = new Date(timestamp.toNumber() * 1000)
} else {
date = new Date(timestamp)
}
return (
<>
<div>{date.toLocaleDateString()}</div>
@ -25,12 +32,12 @@ const TradeHistoryTable = () => {
}
return (
<div className={`flex flex-col py-4`}>
<div className={`-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8`}>
<div className={`align-middle inline-block min-w-full sm:px-6 lg:px-8`}>
<div className="flex flex-col py-4">
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="align-middle inline-block min-w-full sm:px-6 lg:px-8">
{tradeHistory && tradeHistory.length ? (
<div className={`shadow overflow-hidden border-b border-th-bkg-2`}>
<Table className={`min-w-full divide-y divide-th-bkg-2`}>
<div className="shadow overflow-hidden border-b border-th-bkg-2">
<Table className="min-w-full divide-y divide-th-bkg-2">
<Thead>
<Tr className="text-th-fgd-3 text-xs">
<Th
@ -53,10 +60,7 @@ const TradeHistoryTable = () => {
/>
</LinkButton>
</Th>
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
<Th scope="col" className="px-6 py-3 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('side')}
@ -73,10 +77,7 @@ const TradeHistoryTable = () => {
/>
</LinkButton>
</Th>
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
<Th scope="col" className="px-6 py-3 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('size')}
@ -93,10 +94,7 @@ const TradeHistoryTable = () => {
/>
</LinkButton>
</Th>
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
<Th scope="col" className="px-6 py-3 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('price')}
@ -113,10 +111,7 @@ const TradeHistoryTable = () => {
/>
</LinkButton>
</Th>
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
<Th scope="col" className="px-6 py-3 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('value')}
@ -133,10 +128,7 @@ const TradeHistoryTable = () => {
/>
</LinkButton>
</Th>
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
<Th scope="col" className="px-6 py-3 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('liquidity')}
@ -153,10 +145,7 @@ const TradeHistoryTable = () => {
/>
</LinkButton>
</Th>
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
<Th scope="col" className="px-6 py-3 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('feeCost')}
@ -173,15 +162,12 @@ const TradeHistoryTable = () => {
/>
</LinkButton>
</Th>
<Th
scope="col"
className={`px-6 py-3 text-left font-normal`}
>
<Th scope="col" className="px-6 py-3 text-left font-normal">
<LinkButton
className="flex items-center no-underline"
onClick={() => requestSort('loadTimestamp')}
>
Approx Date
Approx Time
<ArrowSmDownIcon
className={`default-transition flex-shrink-0 h-4 w-4 ml-1 ${
sortConfig?.key === 'loadTimestamp'
@ -203,57 +189,43 @@ const TradeHistoryTable = () => {
${index % 2 === 0 ? `bg-th-bkg-3` : `bg-th-bkg-2`}
`}
>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
<Td className="px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1">
<div className="flex items-center">
<img
alt=""
width="20"
height="20"
src={`/assets/icons/${trade.marketName
.split('/')[0]
.split(/-|\//)[0]
.toLowerCase()}.svg`}
className={`mr-2.5`}
/>
<div>{trade.marketName}</div>
</div>
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
<Td className="px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1">
<SideBadge side={trade.side} />
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
<Td className="px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1">
{trade.size}
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
<Td className="px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1">
{trade.price}
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
${(trade.price * trade.size).toFixed(2)}
<Td className="px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1">
${trade.value.toFixed(2)}
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
<Td className="px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1">
{trade.liquidity}
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
{trade.feeCost}
<Td className="px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1">
${trade.feeCost}
</Td>
<Td
className={`px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1`}
>
{trade.loadTimestamp
? renderTradeDateTime(trade.loadTimestamp)
<Td className="px-6 py-4 whitespace-nowrap text-sm text-th-fgd-1">
{trade.loadTimestamp || trade.timestamp
? renderTradeDateTime(
trade.loadTimestamp || trade.timestamp
)
: 'Recent'}
</Td>
</Tr>
@ -262,18 +234,11 @@ const TradeHistoryTable = () => {
</Table>
</div>
) : (
<div
className={`w-full text-center py-6 bg-th-bkg-1 text-th-fgd-3 rounded-md`}
>
<div className="w-full text-center py-6 bg-th-bkg-1 text-th-fgd-3 rounded-md">
No {marketConfig.name} trade history.
{asPath === '/account' ? (
<Link href={'/'}>
<a
className={`inline-flex ml-2 py-0
`}
>
Make a trade
</a>
<a className="inline-flex ml-2 py-0">Make a trade</a>
</Link>
) : null}
</div>

View File

@ -32,6 +32,7 @@ export const defaultLayouts = {
],
lg: [
{ i: 'tvChart', x: 0, y: 0, w: 8, h: 29, minW: 2 },
{ i: 'depositWithdraw', x: 8, y: 0, w: 4, h: 6 },
{ i: 'marketPosition', x: 8, y: 0, w: 4, h: 15, minW: 2 },
{ i: 'marginInfo', x: 8, y: 1, w: 4, h: 14, minW: 2 },
{ i: 'orderbook', x: 0, y: 2, w: 4, h: 17, minW: 2 },
@ -41,6 +42,7 @@ export const defaultLayouts = {
],
md: [
{ i: 'tvChart', x: 0, y: 0, w: 8, h: 29, minW: 2 },
{ i: 'depositWithdraw', x: 8, y: 0, w: 4, h: 6 },
{ i: 'marketPosition', x: 8, y: 0, w: 4, h: 15, minW: 2 },
{ i: 'marginInfo', x: 8, y: 1, w: 4, h: 14, minW: 2 },
{ i: 'orderbook', x: 0, y: 2, w: 4, h: 17, minW: 2 },
@ -50,6 +52,7 @@ export const defaultLayouts = {
],
sm: [
{ i: 'tvChart', x: 0, y: 0, w: 12, h: 25, minW: 6 },
{ i: 'depositWithdraw', x: 0, y: 1, w: 6, h: 15 },
{ i: 'marketPosition', x: 0, y: 1, w: 6, h: 15, minW: 2 },
{ i: 'marginInfo', x: 6, y: 1, w: 6, h: 15, minW: 2 },
{ i: 'tradeForm', x: 0, y: 2, w: 12, h: 13, minW: 3 },
@ -86,11 +89,11 @@ const TradePageGrid = () => {
const [mounted, setMounted] = useState(false)
useEffect(() => setMounted(true), [])
if (!mounted) return null
// TODO enable savedLayouts
return (
<ResponsiveGridLayout
className="layout"
layouts={savedLayouts || defaultLayouts}
layouts={defaultLayouts}
breakpoints={{ xl: 1600, lg: 1200, md: 1110, sm: 768, xs: 0 }}
cols={{ xl: 12, lg: 12, md: 12, sm: 12, xs: 1 }}
rowHeight={15}

View File

@ -10,7 +10,7 @@ import TradeHistoryTable from './TradeHistoryTable'
const TABS = [
'Balances',
'Open Orders',
'Positions',
'Perp Positions',
/*'Fee Discounts'*/
'Trade History',
]
@ -72,7 +72,7 @@ const TabContent = ({ activeTab }) => {
return <BalancesTable />
case 'Trade History':
return <TradeHistoryTable />
case 'Positions':
case 'Perp Positions':
return <PositionsTable />
// case 'Fee Discounts':
// return <FeeDiscountsTable />

View File

@ -65,7 +65,7 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
const [maxButtonTransition, setMaxButtonTransition] = useState(false)
const actions = useMangoStore((s) => s.actions)
const selectedMangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const mangoGroup = useMangoStore((s) => s.selectedMangoGroup.current)
const selectedMangoAccount = useMangoStore(
(s) => s.selectedMangoAccount.current
)
@ -77,34 +77,33 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
() => tokens.find((t) => t.symbol === withdrawTokenSymbol),
[withdrawTokenSymbol, tokens]
)
const tokenIndex = selectedMangoGroup.getTokenIndex(token.mintKey)
const tokenIndex = mangoGroup.getTokenIndex(token.mintKey)
useEffect(() => {
if (!selectedMangoGroup || !selectedMangoAccount || !withdrawTokenSymbol)
return
if (!mangoGroup || !selectedMangoAccount || !withdrawTokenSymbol) return
const mintDecimals = selectedMangoGroup.tokens[tokenIndex].decimals
const mintDecimals = mangoGroup.tokens[tokenIndex].decimals
const deposits = selectedMangoAccount.getUiDeposit(
mangoCache.rootBankCache[tokenIndex],
selectedMangoGroup,
mangoGroup,
tokenIndex
)
const borrows = selectedMangoAccount.getUiBorrow(
mangoCache.rootBankCache[tokenIndex],
selectedMangoGroup,
mangoGroup,
tokenIndex
)
const maxValForSelectedAsset = getDepositsForSelectedAsset().mul(
selectedMangoGroup.getPrice(tokenIndex, mangoCache) || ONE_I80F48
mangoGroup.getPrice(tokenIndex, mangoCache) || ONE_I80F48
)
const currentAssetsVal = selectedMangoAccount
.getAssetsVal(selectedMangoGroup, mangoCache, 'Init')
.getAssetsVal(mangoGroup, mangoCache, 'Init')
.sub(maxValForSelectedAsset)
const currentLiabsVal = selectedMangoAccount.getLiabsVal(
selectedMangoGroup,
mangoGroup,
mangoCache,
'Init'
)
@ -117,7 +116,9 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
const amountToWithdraw = includeBorrow
? liabsAvail
.div(
selectedMangoGroup.getPrice(tokenIndex, mangoCache) || ONE_I80F48
(mangoGroup.getPrice(tokenIndex, mangoCache) || ONE_I80F48).mul(
mangoGroup.spotMarkets[tokenIndex].initLiabWeight
)
)
.add(getDepositsForSelectedAsset())
: getDepositsForSelectedAsset()
@ -152,18 +153,10 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
.div(I80F48.fromNumber(Math.pow(10, mintDecimals)))
.div(mangoCache.rootBankCache[tokenIndex].borrowIndex)
const assetsVal = simulation.getAssetsVal(
selectedMangoGroup,
mangoCache,
'Init'
)
const liabsVal = simulation.getLiabsVal(
selectedMangoGroup,
mangoCache,
'Init'
)
const assetsVal = simulation.getAssetsVal(mangoGroup, mangoCache, 'Init')
const liabsVal = simulation.getLiabsVal(mangoGroup, mangoCache, 'Init')
// const collateralRatio = simulation.getCollateralRatio(
// selectedMangoGroup,
// mangoGroup,
// prices
// )
// const leverage = 1 / Math.max(0, collateralRatio - 1)
@ -177,7 +170,7 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
inputAmount,
tokenIndex,
selectedMangoAccount,
selectedMangoGroup,
mangoGroup,
mangoCache,
])
@ -186,7 +179,7 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
withdraw({
amount: Number(inputAmount),
token: selectedMangoGroup.tokens[tokenIndex].mint,
token: mangoGroup.tokens[tokenIndex].mint,
allowBorrow: includeBorrow,
})
.then((txid: string) => {
@ -206,6 +199,7 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
console.error('Error withdrawing:', err)
notify({
title: 'Could not perform withdraw',
description: err.message,
txid: err.txid,
type: 'error',
})
@ -222,7 +216,7 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
const getDepositsForSelectedAsset = (): I80F48 => {
return selectedMangoAccount.getUiDeposit(
mangoCache.rootBankCache[tokenIndex],
selectedMangoGroup,
mangoGroup,
tokenIndex
)
}
@ -394,7 +388,7 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
{selectedMangoAccount
.getUiDeposit(
mangoCache.rootBankCache[tokenIndex],
selectedMangoGroup,
mangoGroup,
tokenIndex
)
.toFixed(tokenPrecision[withdrawTokenSymbol])}
@ -604,18 +598,10 @@ const WithdrawModal: FunctionComponent<WithdrawModalProps> = ({
</div>
<div className="flex justify-between pb-2">
<div className="text-th-fgd-4">Leverage</div>
{/* <div className="text-th-fgd-1">
{simulation.leverage.toFixed(2)}x
</div> */}
</div>
<div className="flex justify-between">
<div className="text-th-fgd-4">Collateral Ratio</div>
{/* <div className="text-th-fgd-1">
{simulation.collateralRatio * 100 < 200
? Math.floor(simulation.collateralRatio * 100)
: '>200'}
%
</div> */}
<div className="text-th-fgd-1">
{/* {simulation.leverage.toFixed(2)}x */}
X.XXx
</div>
</div>
{simulation.liabsVal > 0.05 ? (
<div className="flex justify-between pt-2">

View File

@ -570,3 +570,21 @@ export const UniMonoIcon = ({ className }) => {
</svg>
)
}
export const UsdtMonoIcon = ({ className }) => {
return (
<svg
className={`${className}`}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 32 32"
fill="currentColor"
>
<path d="M16.023 15.7763C17.3269 15.7763 17.9114 15.7414 18.0245 15.7329L18.0207 15.732C22.0208 15.5547 25.0047 14.8571 25.0047 14.0237C25.0047 13.1903 22.0199 12.4927 18.0207 12.3145V15.0391C17.9085 15.0494 17.2854 15.1013 16.0409 15.1013C15.0039 15.1013 14.2535 15.057 13.9914 15.0381V12.3126C9.98372 12.4917 6.99236 13.1884 6.99236 14.0237C6.99236 14.859 9.98372 15.5566 13.9914 15.7329C14.2497 15.7452 14.986 15.7763 16.023 15.7763Z" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M0.0244761 14.4741L5.8592 2.21826C5.8783 2.17698 5.90893 2.14211 5.94741 2.11786C5.98588 2.0936 6.03055 2.081 6.07603 2.08156H25.9258C25.971 2.08144 26.0154 2.09436 26.0535 2.11877C26.0916 2.14318 26.1219 2.17804 26.1407 2.2192L31.9755 14.475C31.9977 14.5206 32.0047 14.5721 31.9955 14.622C31.9864 14.6719 31.9615 14.7175 31.9245 14.7522L16.1664 29.8514C16.1216 29.8943 16.062 29.9183 16 29.9183C15.9379 29.9183 15.8783 29.8943 15.8336 29.8514L0.075385 14.7513C0.0384323 14.7166 0.0135572 14.6709 0.00439729 14.6211C-0.00476259 14.5712 0.00227334 14.5197 0.0244761 14.4741ZM18.0245 9.59369V12.0326C22.5521 12.242 25.954 13.1385 25.954 14.2141C25.954 15.2898 22.5488 16.1873 18.0235 16.3957V24.214H13.9904V16.3985C9.45484 16.1892 6.04395 15.2917 6.04395 14.2151C6.04395 13.1385 9.45484 12.24 13.9904 12.0317V9.59369H8.41026V5.87451H23.6056V9.59369H18.0245Z"
/>
</svg>
)
}

View File

@ -1,8 +1,7 @@
import { Balances } from '../@types/types'
import { nativeToUi } from '@blockworks-foundation/mango-client'
import { nativeToUi, QUOTE_INDEX } from '@blockworks-foundation/mango-client'
import useMangoStore from '../stores/useMangoStore'
import { sumBy } from 'lodash'
import { QUOTE_INDEX } from '@blockworks-foundation/mango-client/lib/src/MangoGroup'
import { I80F48 } from '@blockworks-foundation/mango-client/lib/src/fixednum'
export function useBalances(): Balances[] {
@ -122,7 +121,7 @@ export function useBalances(): Balances[] {
.sub(quoteMeta.borrows)
.sub(I80F48.fromNumber(quoteInOrders))
return baseBalances.concat([
return [
{
market: null,
key: `${quoteMeta.symbol}${quoteMeta.symbol}`,
@ -133,5 +132,5 @@ export function useBalances(): Balances[] {
unsettled,
net,
},
])
].concat(baseBalances)
}

View File

@ -1,4 +1,4 @@
import { useEffect } from 'react'
import { useCallback, useEffect } from 'react'
import { AccountInfo } from '@solana/web3.js'
import useMangoStore, {
DEFAULT_CONNECTION,
@ -78,28 +78,30 @@ const useHydrateStore = () => {
}, [selectedMarket])
// fetch filled trades for selected market
useInterval(() => {
async function fetchFills() {
const market = useMangoStore.getState().selectedMarket.current
if (!market) {
return null
}
try {
const loadedFills = await selectedMarket.loadFills(
DEFAULT_CONNECTION,
10000
)
setMangoStore((state) => {
state.selectedMarket.fills = loadedFills
})
} catch (err) {
console.log('Error fetching fills:', err)
}
const fetchFills = useCallback(async () => {
if (!selectedMarket) {
return null
}
try {
const loadedFills = await selectedMarket.loadFills(
DEFAULT_CONNECTION,
10000
)
setMangoStore((state) => {
state.selectedMarket.fills = loadedFills
})
} catch (err) {
console.log('Error fetching fills:', err)
}
}, [selectedMarket, setMangoStore])
useInterval(() => {
fetchFills()
}, _SLOW_REFRESH_INTERVAL)
useEffect(() => {
fetchFills()
}, [fetchFills])
}
export default useHydrateStore

View File

@ -63,10 +63,9 @@ const useMangoStats = () => {
borrowInterest: rootBank
.getBorrowRate(mangoGroup)
.mul(I80F48.fromNumber(100)),
utilization:
totalDeposits > I80F48.fromNumber(0)
? totalBorrows.div(totalDeposits)
: I80F48.fromNumber(0),
utilization: totalDeposits.gt(I80F48.fromNumber(0))
? totalBorrows.div(totalDeposits)
: I80F48.fromNumber(0),
}
})
setLatestStats(latestStats)

View File

@ -1,3 +1,8 @@
import {
getMarketIndexBySymbol,
MangoGroup,
} from '@blockworks-foundation/mango-client'
import { PublicKey } from '@solana/web3.js'
import useMangoStore from '../stores/useMangoStore'
const byTimestamp = (a, b) => {
@ -6,14 +11,33 @@ const byTimestamp = (a, b) => {
)
}
const parsedPerpEvent = (event) => {
const parsedPerpEvent = (
mangoGroup: MangoGroup,
mangoAccountPk: PublicKey,
event
) => {
const mangoGroupConfig = useMangoStore.getState().selectedMangoGroup.config
const marketIndex = getMarketIndexBySymbol(
mangoGroupConfig,
event.marketName.split(/-|\//)[0]
)
const perpMarketInfo = mangoGroup.perpMarkets[marketIndex]
const maker = event.maker.equals(mangoAccountPk)
const orderId = maker ? event.makerOrderId : event.takerOrderId
const value = event.quantity * event.price
const feeRate = maker
? perpMarketInfo.makerFee.toNumber()
: perpMarketInfo.takerFee.toNumber()
return {
...event,
key: `${event.orderId}-${event.uuid}`,
liquidity: event.maker ? 'Maker' : 'Taker',
value: event.price * event.size,
key: orderId.toString(),
liquidity: maker ? 'Maker' : 'Taker',
size: event.quantity,
price: event.price,
value,
feeCost: (feeRate * value).toFixed(4),
side: event.side,
feeCost: 0.0,
}
}
@ -27,14 +51,18 @@ const parsedSerumEvent = (event) => {
}
}
const formatTradeHistory = (newTradeHistory) => {
const formatTradeHistory = (
selectedMangoGroup,
mangoAccountPk: PublicKey,
newTradeHistory
) => {
return newTradeHistory
.flat()
.map((trade) => {
if (trade.eventFlags) {
return parsedSerumEvent(trade)
} else {
return parsedPerpEvent(trade)
return parsedPerpEvent(selectedMangoGroup, mangoAccountPk, trade)
}
})
.sort(byTimestamp)
@ -73,11 +101,19 @@ export const useTradeHistory = () => {
)
const newTradeHistory = [...newFills, ...tradeHistory]
if (newFills.length > 0 && newTradeHistory.length !== allTrades.length) {
return formatTradeHistory(newTradeHistory)
return formatTradeHistory(
selectedMangoGroup,
mangoAccount.publicKey,
newTradeHistory
)
}
}
return formatTradeHistory(tradeHistory)
return formatTradeHistory(
selectedMangoGroup,
mangoAccount.publicKey,
tradeHistory
)
}
export default useTradeHistory

View File

@ -93,7 +93,6 @@ export default function useWallet() {
useEffect(() => {
if (!wallet) return
wallet.on('connect', async () => {
console.log('connected wallet')
sleep(250)
await actions.fetchMangoAccounts()
setMangoStore((state) => {

View File

@ -1,8 +1,10 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import {
CurrencyDollarIcon,
DuplicateIcon,
ExternalLinkIcon,
LinkIcon,
PencilIcon,
} from '@heroicons/react/outline'
import {
getTokenBySymbol,
@ -12,7 +14,7 @@ import {
} from '@blockworks-foundation/mango-client'
import useMangoStore from '../stores/useMangoStore'
import { useBalances } from '../hooks/useBalances'
import { abbreviateAddress } from '../utils'
import { abbreviateAddress, copyToClipboard } from '../utils'
import PageBodyContainer from '../components/PageBodyContainer'
import TopBar from '../components/TopBar'
import AccountAssets from '../components/account-page/AccountAssets'
@ -21,7 +23,10 @@ import AccountOrders from '../components/account-page/AccountOrders'
import AccountHistory from '../components/account-page/AccountHistory'
import AccountsModal from '../components/AccountsModal'
import AccountOverview from '../components/account-page/AccountOverview'
import AccountNameModal from '../components/AccountNameModal'
import Button from '../components/Button'
import EmptyState from '../components/EmptyState'
import { MangoAccount } from '@blockworks-foundation/mango-client'
const TABS = [
'Overview',
@ -33,12 +38,23 @@ const TABS = [
'Activity',
]
export function getMarginInfoString(marginAccount: MangoAccount) {
return marginAccount?.info
? String.fromCharCode(...marginAccount?.info).replaceAll(
String.fromCharCode(0),
''
)
: ''
}
export default function Account() {
const [activeTab, setActiveTab] = useState(TABS[0])
const [showAccountsModal, setShowAccountsModal] = useState(false)
const [portfolio, setPortfolio] = useState([])
const allMarkets = useMangoStore((s) => s.selectedMangoGroup.markets)
const balances = useBalances()
const [showNameModal, setShowNameModal] = useState(false)
const [isCopied, setIsCopied] = useState(false)
const connected = useMangoStore((s) => s.wallet.connected)
const groupConfig = useMangoStore((s) => s.selectedMangoGroup.config)
const mangoAccount = useMangoStore((s) => s.selectedMangoAccount.current)
@ -46,6 +62,9 @@ export default function Account() {
const mangoCache = useMangoStore((s) => s.selectedMangoGroup.cache)
const wallet = useMangoStore((s) => s.wallet.current)
const marginInfoString = getMarginInfoString(mangoAccount)
const [accountName, setAccountName] = useState(marginInfoString)
const handleTabChange = (tabName) => {
setActiveTab(tabName)
}
@ -124,42 +143,77 @@ export default function Account() {
})
setPortfolio(portfolio.sort((a, b) => b.value - a.value))
}, [perpAccounts])
const handleCopyPublicKey = (code) => {
setIsCopied(true)
copyToClipboard(code)
}
const handleCloseNameModal = useCallback(() => {
setShowNameModal(false)
}, [])
useEffect(() => {
if (isCopied) {
const timer = setTimeout(() => {
setIsCopied(false)
}, 1500)
return () => clearTimeout(timer)
}
}, [isCopied])
return (
<div className={`bg-th-bkg-1 text-th-fgd-1 transition-all`}>
<TopBar />
<PageBodyContainer>
<div className="flex flex-col sm:flex-row items-center justify-between pt-8 pb-3 sm:pb-6 md:pt-10">
<h1 className={`text-th-fgd-1 text-2xl font-semibold`}>Account</h1>
{mangoAccount ? (
<div className="divide-x divide-th-fgd-4 flex justify-center w-full pt-4 sm:pt-0 sm:justify-end">
<div className="pr-4 text-xs text-th-fgd-1">
<div className="pb-0.5 text-2xs text-th-fgd-3">Owner</div>
<a
className="default-transition flex items-center text-th-fgd-2"
href={`https://explorer.solana.com/address/${mangoAccount?.owner}`}
target="_blank"
rel="noopener noreferrer"
>
<span>{abbreviateAddress(mangoAccount?.owner)}</span>
<ExternalLinkIcon className={`h-3 w-3 ml-1`} />
</a>
</div>
<div className="pl-4 text-xs text-th-fgd-1">
<div className="pb-0.5 text-2xs text-th-fgd-3">
Margin Account
<>
<div className="flex flex-col sm:flex-row sm:items-end pb-4 md:pb-0">
<h1 className={`font-semibold mr-3 text-th-fgd-1 text-2xl`}>
{accountName ? accountName : 'Account'}
</h1>
<div className="flex items-center pb-0.5 text-th-fgd-3 ">
{abbreviateAddress(mangoAccount.publicKey)}
<DuplicateIcon
className="cursor-pointer default-transition h-4 w-4 ml-1.5 hover:text-th-fgd-1"
onClick={() => handleCopyPublicKey(mangoAccount.publicKey)}
/>
{isCopied ? (
<div className="ml-2 text-th-fgd-2 text-xs">Copied!</div>
) : null}
</div>
</div>
<div className="flex items-center">
{/* Re-instate when added to program code */}
{/* <Button
className="text-xs flex flex-grow items-center justify-center mr-2 pt-0 pb-0 h-8 pl-3 pr-3"
onClick={() => setShowNameModal(true)}
>
<div className="flex items-center">
<PencilIcon className="h-4 w-4 mr-1.5" />
{accountName ? 'Edit Name' : 'Add Name'}
</div>
</Button> */}
<a
className="default-transition flex items-center text-th-fgd-2"
className="border border-th-fgd-4 bg-th-bkg-2 default-transition flex flex-grow font-bold h-8 items-center justify-center pl-3 pr-3 rounded-md text-th-fgd-1 text-xs hover:bg-th-bkg-3 hover:text-th-fgd-1 focus:outline-none"
href={`https://explorer.solana.com/address/${mangoAccount?.publicKey}`}
target="_blank"
rel="noopener noreferrer"
>
<span>{abbreviateAddress(mangoAccount?.publicKey)}</span>
<ExternalLinkIcon className={`h-3 w-3 ml-1`} />
<span>Explorer</span>
<ExternalLinkIcon className={`h-4 w-4 ml-1.5`} />
</a>
<Button
className="text-xs flex flex-grow items-center justify-center ml-2 pt-0 pb-0 h-8 pl-3 pr-3"
onClick={() => setShowAccountsModal(true)}
>
<div className="flex items-center">
<CurrencyDollarIcon className="h-4 w-4 mr-1.5" />
Accounts
</div>
</Button>
</div>
</div>
</>
) : null}
</div>
<div className="bg-th-bkg-2 overflow-none p-6 rounded-lg">
@ -211,6 +265,13 @@ export default function Account() {
isOpen={showAccountsModal}
/>
) : null}
{showNameModal ? (
<AccountNameModal
accountName={accountName}
isOpen={showNameModal}
onClose={handleCloseNameModal}
/>
) : null}
</div>
)
}

View File

@ -0,0 +1,13 @@
<<<<<<< HEAD
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.023 15.7763C17.3269 15.7763 17.9114 15.7414 18.0245 15.7329L18.0207 15.732C22.0208 15.5547 25.0047 14.8571 25.0047 14.0237C25.0047 13.1903 22.0199 12.4927 18.0207 12.3145V15.0391C17.9085 15.0494 17.2854 15.1013 16.0409 15.1013C15.0039 15.1013 14.2535 15.057 13.9914 15.0381V12.3126C9.98372 12.4917 6.99236 13.1884 6.99236 14.0237C6.99236 14.859 9.98372 15.5566 13.9914 15.7329C14.2497 15.7452 14.986 15.7763 16.023 15.7763Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.0244761 14.4741L5.8592 2.21826C5.8783 2.17698 5.90893 2.14211 5.94741 2.11786C5.98588 2.0936 6.03055 2.081 6.07603 2.08156H25.9258C25.971 2.08144 26.0154 2.09436 26.0535 2.11877C26.0916 2.14318 26.1219 2.17804 26.1407 2.2192L31.9755 14.475C31.9977 14.5206 32.0047 14.5721 31.9955 14.622C31.9864 14.6719 31.9615 14.7175 31.9245 14.7522L16.1664 29.8514C16.1216 29.8943 16.062 29.9183 16 29.9183C15.9379 29.9183 15.8783 29.8943 15.8336 29.8514L0.075385 14.7513C0.0384323 14.7166 0.0135572 14.6709 0.00439729 14.6211C-0.00476259 14.5712 0.00227334 14.5197 0.0244761 14.4741ZM18.0245 9.59369V12.0326C22.5521 12.242 25.954 13.1385 25.954 14.2141C25.954 15.2898 22.5488 16.1873 18.0235 16.3957V24.214H13.9904V16.3985C9.45484 16.1892 6.04395 15.2917 6.04395 14.2151C6.04395 13.1385 9.45484 12.24 13.9904 12.0317V9.59369H8.41026V5.87451H23.6056V9.59369H18.0245Z" fill="white"/>
</svg>
=======
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32">
<g fill="none" fill-rule="evenodd">
<circle cx="16" cy="16" r="16" fill="#26A17B"/>
<path fill="#FFF" d="M17.922 17.383v-.002c-.11.008-.677.042-1.942.042-1.01 0-1.721-.03-1.971-.042v.003c-3.888-.171-6.79-.848-6.79-1.658 0-.809 2.902-1.486 6.79-1.66v2.644c.254.018.982.061 1.988.061 1.207 0 1.812-.05 1.925-.06v-2.643c3.88.173 6.775.85 6.775 1.658 0 .81-2.895 1.485-6.775 1.657m0-3.59v-2.366h5.414V7.819H8.595v3.608h5.414v2.365c-4.4.202-7.709 1.074-7.709 2.118 0 1.044 3.309 1.915 7.709 2.118v7.582h3.913v-7.584c4.393-.202 7.694-1.073 7.694-2.116 0-1.043-3.301-1.914-7.694-2.117"/>
</g>
</svg>
>>>>>>> 2e33486dbb113439af571877c0066425d0d88a20

View File

@ -1 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><g fill="none" fill-rule="evenodd"><circle cx="16" cy="16" r="16" fill="#26A17B"/><path fill="#FFF" d="M17.922 17.383v-.002c-.11.008-.677.042-1.942.042-1.01 0-1.721-.03-1.971-.042v.003c-3.888-.171-6.79-.848-6.79-1.658 0-.809 2.902-1.486 6.79-1.66v2.644c.254.018.982.061 1.988.061 1.207 0 1.812-.05 1.925-.06v-2.643c3.88.173 6.775.85 6.775 1.658 0 .81-2.895 1.485-6.775 1.657m0-3.59v-2.366h5.414V7.819H8.595v3.608h5.414v2.365c-4.4.202-7.709 1.074-7.709 2.118 0 1.044 3.309 1.915 7.709 2.118v7.582h3.913v-7.584c4.393-.202 7.694-1.073 7.694-2.116 0-1.043-3.301-1.914-7.694-2.117"/></g></svg>
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.8592 2.21826L0.0244761 14.4741C0.00227334 14.5197 -0.00476259 14.5712 0.00439729 14.6211C0.0135572 14.6709 0.0384323 14.7166 0.075385 14.7513L15.8336 29.8514C15.8783 29.8943 15.9379 29.9183 16 29.9183C16.062 29.9183 16.1216 29.8943 16.1664 29.8514L31.9245 14.7522C31.9615 14.7175 31.9864 14.6719 31.9955 14.622C32.0047 14.5721 31.9977 14.5206 31.9755 14.475L26.1407 2.2192C26.1219 2.17804 26.0916 2.14318 26.0535 2.11877C26.0154 2.09436 25.971 2.08144 25.9258 2.08156H6.07603C6.03055 2.081 5.98588 2.0936 5.94741 2.11786C5.90893 2.14211 5.8783 2.17698 5.8592 2.21826V2.21826Z" fill="#50AF95"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.0245 15.7329C17.9114 15.7414 17.3269 15.7763 16.023 15.7763C14.986 15.7763 14.2497 15.7452 13.9914 15.7329C9.98372 15.5566 6.99236 14.859 6.99236 14.0237C6.99236 13.1884 9.98372 12.4917 13.9914 12.3126V15.0381C14.2535 15.057 15.0039 15.1013 16.0409 15.1013C17.2854 15.1013 17.9085 15.0494 18.0207 15.0391V12.3145C22.0199 12.4927 25.0047 13.1903 25.0047 14.0237C25.0047 14.8571 22.0208 15.5547 18.0207 15.732L18.0245 15.7329ZM18.0245 12.0326V9.59369H23.6056V5.87451H8.41026V9.59369H13.9904V12.0317C9.45484 12.24 6.04395 13.1385 6.04395 14.2151C6.04395 15.2917 9.45484 16.1892 13.9904 16.3985V24.214H18.0235V16.3957C22.5488 16.1873 25.954 15.2898 25.954 14.2141C25.954 13.1385 22.5516 12.2409 18.0235 12.0317L18.0245 12.0326Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 651 B

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -37,7 +37,7 @@ export const ENDPOINTS: EndpointInfo[] = [
},
{
name: 'devnet',
url: 'https://api.devnet.solana.com',
url: 'https://mango.devnet.rpcpool.com',
websocket: 'https://api.devnet.solana.com',
custom: false,
},
@ -56,7 +56,7 @@ export const WEBSOCKET_CONNECTION = new Connection(
'processed' as Commitment
)
const DEFAULT_MANGO_GROUP_NAME = 'mango_test_v3.8'
const DEFAULT_MANGO_GROUP_NAME = 'devnet.0'
const DEFAULT_MANGO_GROUP_CONFIG = Config.ids().getGroup(
CLUSTER,
DEFAULT_MANGO_GROUP_NAME
@ -299,7 +299,7 @@ const useMangoStore = create<MangoStore>((set, get) => ({
const mangoCache = await mangoGroup.loadCache(DEFAULT_CONNECTION)
const allMarketConfigs = getAllMarkets(mangoGroupConfig)
const allMarketPks = allMarketConfigs.map((m) => m.publicKey)
console.log('mango group: ', mangoGroup)
const allMarketAccountInfos = await getMultipleAccounts(
DEFAULT_CONNECTION,
allMarketPks

View File

@ -79,7 +79,8 @@ button {
}
.tiny-text {
font-size: 0.7rem;
font-size: 0.65rem;
line-height: 1.5;
}
/* Chart */

View File

@ -994,8 +994,8 @@
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@blockworks-foundation/mango-client@git+https://github.com/blockworks-foundation/mango-client-v3.git":
version "3.0.1"
resolved "git+https://github.com/blockworks-foundation/mango-client-v3.git#e632d3fdc4cf7c70dbf5137de81265a5767af6c1"
version "3.0.2"
resolved "git+https://github.com/blockworks-foundation/mango-client-v3.git#34dc2dbb28c23a657855b9c4cf0b25a972673b80"
dependencies:
"@project-serum/serum" "^0.13.45"
"@project-serum/sol-wallet-adapter" "^0.2.0"
@ -1891,14 +1891,14 @@
integrity sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==
"@types/node@*":
version "16.4.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.0.tgz#2c219eaa3b8d1e4d04f4dd6e40bc68c7467d5272"
integrity sha512-HrJuE7Mlqcjj+00JqMWpZ3tY8w7EUd+S0U3L1+PQSWiXZbOgyQDvi+ogoUxaHApPJq5diKxYBQwA3iIlNcPqOg==
version "16.4.6"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.4.6.tgz#1734d119dfa8fede5606d35ae10f9fc9c84d889b"
integrity sha512-FKyawK3o5KL16AwbeFajen8G4K3mmqUrQsehn5wNKs8IzlKHE8TfnSmILXVMVziAEcnB23u1RCFU1NT6hSyr7Q==
"@types/node@^12.12.54":
version "12.20.16"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.16.tgz#1acf34f6456208f495dac0434dd540488d17f991"
integrity sha512-6CLxw83vQf6DKqXxMPwl8qpF8I7THFZuIwLt4TnNsumxkp1VsRZWT8txQxncT/Rl2UojTsFzWgDG4FRMwafrlA==
version "12.20.17"
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.17.tgz#ffd44c2801fc527a6fe6e86bc9b900261df1c87e"
integrity sha512-so8EHl4S6MmatPS0f9sE1ND94/ocbcEshW5OpyYthRqeRpiYyW2uXYTo/84kmfdfeNrDycARkvuiXl6nO40NGg==
"@types/node@^14.14.25":
version "14.17.3"
@ -7583,9 +7583,9 @@ regenerate@^1.4.0:
integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==
regenerator-runtime@^0.13.4:
version "0.13.7"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==
regenerator-transform@^0.14.2:
version "0.14.5"