Merge branch 'main' into onboarding-redesign

This commit is contained in:
saml33 2022-11-02 16:24:54 +00:00
commit bdbad18b9f
38 changed files with 492 additions and 172 deletions

View File

@ -44,7 +44,7 @@ const MangoAccountsList = ({
s.mangoAccount.lastUpdatedAt = new Date().toISOString()
})
setLastAccountViewed(acc.publicKey.toString())
actions.fetchSerumOpenOrders(acc)
actions.fetchOpenOrders(acc)
} catch (e) {
console.warn('Error selecting account', e)
}

View File

@ -15,27 +15,34 @@ const rehydrateStore = async () => {
}
const HydrateStore = () => {
const actions = mangoStore((s) => s.actions)
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
const jupiterTokens = mangoStore((s) => s.jupiterTokens)
useInterval(() => {
rehydrateStore()
}, 5000)
useEffect(() => {
const actions = mangoStore.getState().actions
actions.fetchGroup().then(() => {
const fetchData = async () => {
await actions.fetchGroup()
actions.fetchJupiterTokens()
})
actions.fetchCoingeckoPrices()
}
fetchData()
}, [])
useEffect(() => {
if (jupiterTokens.length) {
actions.fetchCoingeckoPrices()
}
}, [jupiterTokens])
// watch selected Mango Account for changes
useEffect(() => {
const connection = mangoStore.getState().connection
const client = mangoStore.getState().client
if (!mangoAccount) return
console.log('mangoAccount.publicKey', mangoAccount.publicKey.toString())
const subscriptionId = connection.onAccountChange(
mangoAccount.publicKey,
@ -64,7 +71,6 @@ const HydrateStore = () => {
decodedMangoAccount
)
await newMangoAccount.reloadAccountData(client)
console.log('WEBSOCKET ma:', newMangoAccount)
// newMangoAccount.spotOpenOrdersAccounts =
// mangoAccount.spotOpenOrdersAccounts
@ -111,7 +117,7 @@ const ReadOnlyMangoAccount = () => {
state.mangoAccount.initialLoad = false
})
} catch (error) {
console.log('error', error)
console.error('error', error)
}
}

View File

@ -5,6 +5,7 @@ import WithdrawModal from '../modals/WithdrawModal'
import {
ArrowDownTrayIcon,
ArrowUpTrayIcon,
DocumentDuplicateIcon,
EllipsisHorizontalIcon,
PencilIcon,
TrashIcon,
@ -14,6 +15,8 @@ import IconDropMenu from '../shared/IconDropMenu'
import CloseAccountModal from '../modals/CloseAccountModal'
import AccountNameModal from '../modals/AccountNameModal'
import mangoStore from '@store/mangoStore'
import { copyToClipboard } from 'utils'
import { notify } from 'utils/notifications'
const AccountActions = () => {
const { t } = useTranslation(['common', 'close-account'])
@ -23,9 +26,17 @@ const AccountActions = () => {
const [showEditAccountModal, setShowEditAccountModal] = useState(false)
const [showWithdrawModal, setShowWithdrawModal] = useState(false)
const handleCopyAddress = (address: string) => {
copyToClipboard(address)
notify({
title: t('copy-address-success'),
type: 'success',
})
}
return (
<>
<div className="flex space-x-3">
<div className="flex items-center space-x-2 md:space-x-3">
<Button
className="flex items-center"
disabled={!mangoAccount}
@ -49,12 +60,22 @@ const AccountActions = () => {
icon={<EllipsisHorizontalIcon className="h-5 w-5" />}
large
>
<LinkButton
className="whitespace-nowrap"
disabled={!mangoAccount}
onClick={() =>
handleCopyAddress(mangoAccount!.publicKey.toString())
}
>
<DocumentDuplicateIcon className="h-4 w-4" />
<span className="ml-2">{t('copy-address')}</span>
</LinkButton>
<LinkButton
className="whitespace-nowrap"
disabled={!mangoAccount}
onClick={() => setShowEditAccountModal(true)}
>
<PencilIcon className="h-5 w-5" />
<PencilIcon className="h-4 w-4" />
<span className="ml-2">{t('edit-account')}</span>
</LinkButton>
<LinkButton
@ -62,7 +83,7 @@ const AccountActions = () => {
disabled={!mangoAccount}
onClick={() => setShowCloseAccountModal(true)}
>
<TrashIcon className="h-5 w-5" />
<TrashIcon className="h-4 w-4" />
<span className="ml-2">{t('close-account')}</span>
</LinkButton>
</IconDropMenu>

View File

@ -62,10 +62,13 @@ const CreateAccountForm = ({
const newAccount = mangoAccounts.find(
(acc) => acc.accountNum === newAccountNum
)
set((s) => {
s.mangoAccount.current = newAccount
s.mangoAccounts = mangoAccounts
})
if (newAccount) {
await newAccount.reloadAccountData(client)
set((s) => {
s.mangoAccount.current = newAccount
s.mangoAccounts = mangoAccounts
})
}
setLoading(false)
notify({
title: t('new-account-success'),

View File

@ -229,7 +229,7 @@ function BorrowModal({ isOpen, onClose, token }: ModalCombinedProps) {
placeholder="0.00"
value={inputAmount}
onValueChange={(e: NumberFormatValues) =>
setInputAmount(Number(e.value) ? e.value : '')
setInputAmount(!Number.isNaN(Number(e.value)) ? e.value : '')
}
isAllowed={withValueLimit}
/>

View File

@ -2,14 +2,25 @@ import { ModalProps } from '../../types/modal'
import Modal from '../shared/Modal'
import CreateAccountForm from '@components/account/CreateAccountForm'
import mangoStore from '@store/mangoStore'
import { useRouter } from 'next/router'
const CreateAccountModal = ({ isOpen, onClose }: ModalProps) => {
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
const router = useRouter()
const { asPath } = useRouter()
const handleClose = () => {
if (asPath !== '/') {
router.push('/')
}
onClose()
}
return (
<Modal isOpen={isOpen} onClose={onClose}>
<div className="flex min-h-[264px] flex-col items-center justify-center">
<CreateAccountForm
customClose={onClose}
customClose={handleClose}
isFirstAccount={!mangoAccount}
/>
</div>

View File

@ -98,7 +98,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
selectedToken === 'SOL' ? tokenMax.maxAmount - 0.05 : tokenMax.maxAmount
setInputAmount(max.toString())
setSizePercentage('100')
}, [tokenMax])
}, [tokenMax, selectedToken])
const handleSizePercentage = useCallback(
(percentage: string) => {
@ -113,7 +113,7 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
setInputAmount(amount.toString())
},
[tokenMax]
[tokenMax, selectedToken]
)
useEffect(() => {
@ -294,9 +294,9 @@ function DepositModal({ isOpen, onClose, token }: ModalCombinedProps) {
className="w-full rounded-lg rounded-l-none border border-th-bkg-4 bg-th-bkg-1 p-3 text-right font-mono text-xl tracking-wider text-th-fgd-1 focus:outline-none"
placeholder="0.00"
value={inputAmount}
onValueChange={(e: NumberFormatValues) =>
setInputAmount(Number(e.value) ? e.value : '')
}
onValueChange={(e: NumberFormatValues) => {
setInputAmount(!Number.isNaN(Number(e.value)) ? e.value : '')
}}
isAllowed={withValueLimit}
/>
</div>

View File

@ -19,7 +19,7 @@ const EditProfileModal = ({ isOpen, onClose }: ModalProps) => {
onEditProfileImage={() => setShowEditProfilePic(true)}
/>
<EnterBottomExitBottom
className="absolute bottom-0 left-0 z-20 h-full w-full overflow-auto bg-th-bkg-1 p-6"
className="absolute bottom-0 left-0 z-20 h-full w-full overflow-auto rounded-lg bg-th-bkg-1 p-6"
show={showEditProfilePic}
>
<EditNftProfilePic onClose={() => setShowEditProfilePic(false)} />

View File

@ -16,13 +16,12 @@ import Modal from '@components/shared/Modal'
import { formatFixedDecimals } from 'utils/numbers'
import CreateAccountForm from '@components/account/CreateAccountForm'
import { EnterRightExitLeft } from '@components/shared/Transitions'
import { useRouter } from 'next/router'
const MangoAccountsListModal = ({
// mangoAccount,
isOpen,
onClose,
}: {
// mangoAccount: MangoAccount | undefined
isOpen: boolean
onClose: () => void
}) => {
@ -34,6 +33,8 @@ const MangoAccountsListModal = ({
const loading = mangoStore((s) => s.mangoAccount.initialLoad)
const [showNewAccountForm, setShowNewAccountForm] = useState(false)
const [, setLastAccountViewed] = useLocalStorageStringState(LAST_ACCOUNT_KEY)
const router = useRouter()
const { asPath } = useRouter()
const handleSelectMangoAccount = async (acc: MangoAccount) => {
const set = mangoStore.getState().set
@ -45,7 +46,7 @@ const MangoAccountsListModal = ({
})
try {
const reloadedMangoAccount = await retryFn(() => acc.reload(client))
actions.fetchSerumOpenOrders(reloadedMangoAccount)
actions.fetchOpenOrders(reloadedMangoAccount)
set((s) => {
s.mangoAccount.current = reloadedMangoAccount
s.mangoAccount.lastUpdatedAt = new Date().toISOString()
@ -58,6 +59,13 @@ const MangoAccountsListModal = ({
}
}
const handleClose = () => {
if (asPath !== '/') {
router.push('/')
}
onClose()
}
return (
<Modal isOpen={isOpen} onClose={onClose}>
<div className="inline-block w-full transform overflow-x-hidden">
@ -144,7 +152,7 @@ const MangoAccountsListModal = ({
show={showNewAccountForm}
>
<CreateAccountForm
customClose={() => setShowNewAccountForm(false)}
customClose={handleClose}
handleBack={() => setShowNewAccountForm(false)}
/>
</EnterRightExitLeft>

View File

@ -237,7 +237,9 @@ function WithdrawModal({ isOpen, onClose, token }: ModalCombinedProps) {
placeholder="0.00"
value={inputAmount}
onValueChange={(e: NumberFormatValues) =>
setInputAmount(Number(e.value) ? e.value : '')
setInputAmount(
!Number.isNaN(Number(e.value)) ? e.value : ''
)
}
isAllowed={withValueLimit}
/>

View File

@ -138,7 +138,7 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
<div className="mb-3 flex w-full flex-col items-center sm:mt-3 sm:flex-row sm:justify-between">
<button
onClick={onClose}
className={`absolute left-2 top-2 z-50 text-th-fgd-2 focus:outline-none md:hover:text-th-primary`}
className={`absolute left-2 top-3 z-50 text-th-fgd-4 focus:outline-none md:hover:text-th-primary`}
>
<ArrowLeftIcon className={`h-5 w-5`} />
</button>
@ -148,7 +148,7 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
{t('save')}
</Button>
{profile.profile_image_url ? (
<LinkButton className="text-xs" onClick={removeProfileImage}>
<LinkButton className="text-sm" onClick={removeProfileImage}>
{t('profile:remove')}
</LinkButton>
) : null}
@ -156,10 +156,10 @@ const EditNftProfilePic = ({ onClose }: { onClose: () => void }) => {
</div>
{nfts.length > 0 ? (
<div className="flex flex-col items-center">
<div className="mb-4 grid w-full grid-flow-row grid-cols-3 gap-4">
<div className="mb-4 grid w-full grid-flow-row grid-cols-3 gap-3">
{nfts.map((n) => (
<button
className={`default-transitions col-span-1 flex items-center justify-center rounded-md border bg-th-bkg-3 py-3 sm:py-4 md:hover:bg-th-bkg-4 ${
className={`default-transition col-span-1 flex items-center justify-center rounded-md border bg-th-bkg-2 py-3 sm:py-4 md:hover:bg-th-bkg-3 ${
selectedProfile === n.image
? 'border-th-primary'
: 'border-th-bkg-3'

View File

@ -84,7 +84,7 @@ const EditProfileForm = ({
const messageString = JSON.stringify({
profile_name: name,
trader_category: profile?.trader_category,
profile_image_url: profile?.profile_image_url,
profile_image_url: profile?.profile_image_url || '',
})
const message = new TextEncoder().encode(messageString)
const signature = await signMessage(message)

View File

@ -27,10 +27,6 @@ const HealthImpact = ({
const group = mangoStore.getState().group
if (!group || !mangoAccount) return 0
const uiTokenAmount = isDeposit ? uiAmount : uiAmount * -1
console.log('uiAmount')
console.log('uiTokenAmount', uiTokenAmount)
const projectedHealth =
mangoAccount.simHealthRatioWithTokenPositionUiChanges(
group,

View File

@ -1,9 +1,10 @@
import { Fragment, useEffect, useMemo, useState } from 'react'
import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'
import {
CheckCircleIcon,
ArrowTopRightOnSquareIcon,
InformationCircleIcon,
XCircleIcon,
XMarkIcon,
} from '@heroicons/react/20/solid'
import mangoStore, { CLUSTER } from '@store/mangoStore'
import { Notification, notify } from '../../utils/notifications'
@ -13,12 +14,16 @@ import { TokenInstructions } from '@project-serum/serum'
import {
CLIENT_TX_TIMEOUT,
NOTIFICATION_POSITION_KEY,
PREFERRED_EXPLORER_KEY,
} from '../../utils/constants'
import useLocalStorageState from 'hooks/useLocalStorageState'
import { EXPLORERS } from 'pages/settings'
import { useTranslation } from 'next-i18next'
const setMangoStore = mangoStore.getState().set
const NotificationList = () => {
const { t } = useTranslation()
const notifications = mangoStore((s) => s.notifications)
const walletTokens = mangoStore((s) => s.wallet.tokens)
const notEnoughSoLMessage = 'Not enough SOL'
@ -56,6 +61,16 @@ const NotificationList = () => {
}
}, [notifications, walletTokens])
const clearAll = useCallback(() => {
setMangoStore((s) => {
const newNotifications = s.notifications.map((n) => ({
...n,
show: false,
}))
s.notifications = newNotifications
})
}, [notifications])
const reversedNotifications = [...notifications].reverse()
const position: string = useMemo(() => {
@ -78,8 +93,17 @@ const NotificationList = () => {
return (
<div
className={`pointer-events-none fixed z-50 w-full space-y-2 p-4 text-th-fgd-1 md:w-auto md:p-6 ${position}`}
className={`pointer-events-none fixed z-50 flex w-full flex-col items-end space-y-2 p-4 text-th-fgd-1 md:w-auto md:p-6 ${position}`}
>
{notifications.filter((n) => n.show).length > 1 ? (
<button
className="default-transition pointer-events-auto flex items-center rounded bg-th-bkg-3 px-2 py-1 text-xs text-th-fgd-3 md:hover:bg-th-bkg-4"
onClick={clearAll}
>
<XMarkIcon className="mr-1 h-3.5 w-3.5" />
{t('clear-all')}
</button>
) : null}
{reversedNotifications.map((n) => (
<Notification key={n.id} notification={n} />
))}
@ -92,6 +116,10 @@ const Notification = ({ notification }: { notification: Notification }) => {
NOTIFICATION_POSITION_KEY,
'bottom-left'
)
const [preferredExplorer] = useLocalStorageState(
PREFERRED_EXPLORER_KEY,
EXPLORERS[0]
)
const { type, title, description, txid, show, id } = notification
// overwrite the title if of the error message if it is a time out error
@ -231,12 +259,7 @@ const Notification = ({ notification }: { notification: Notification }) => {
) : null}
{txid ? (
<a
href={
'https://explorer.solana.com/tx/' +
txid +
'?cluster=' +
CLUSTER
}
href={preferredExplorer.url + txid + '?cluster=' + CLUSTER}
className="default-transition mt-1 flex items-center text-xs text-th-fgd-3 hover:text-th-fgd-2"
target="_blank"
rel="noreferrer"

View File

@ -1,8 +1,9 @@
import React, { FunctionComponent } from 'react'
import { useTranslation } from 'next-i18next'
import { PerpOrderSide } from '@blockworks-foundation/mango-v4'
type SideBadgeProps = {
side: string
side: string | PerpOrderSide
}
const SideBadge: FunctionComponent<SideBadgeProps> = ({ side }) => {
@ -11,13 +12,17 @@ const SideBadge: FunctionComponent<SideBadgeProps> = ({ side }) => {
return (
<div
className={`inline-block rounded uppercase ${
side === 'buy' || side === 'long'
side === 'buy' || side === 'long' || side === PerpOrderSide.bid
? 'border border-th-green text-th-green'
: 'border border-th-red text-th-red'
}
-my-0.5 px-1.5 py-0.5 text-xs uppercase`}
>
{t(side)}
{typeof side === 'string'
? t(side)
: side === PerpOrderSide.bid
? 'Buy'
: 'Sell'}
</div>
)
}

View File

@ -274,7 +274,7 @@ const SwapForm = () => {
decimalScale={inputTokenInfo?.decimals || 6}
name="amountIn"
id="amountIn"
className="w-full rounded-lg rounded-l-none border border-th-bkg-4 bg-th-bkg-1 p-3 text-right font-mono text-xl font-bold text-th-fgd-1 focus:outline-none"
className="w-full rounded-lg rounded-l-none border border-th-bkg-4 bg-th-bkg-1 p-3 text-right font-mono text-base font-bold text-th-fgd-1 focus:outline-none lg:text-lg xl:text-xl"
placeholder="0.00"
value={amountInFormValue}
onValueChange={handleAmountInChange}
@ -313,7 +313,7 @@ const SwapForm = () => {
type="output"
/>
</div>
<div className="flex h-[54px] w-full items-center justify-end rounded-r-lg border border-th-bkg-4 bg-th-bkg-3 text-right text-xl font-bold text-th-fgd-3">
<div className="flex h-[54px] w-full items-center justify-end rounded-r-lg border border-th-bkg-4 bg-th-bkg-3 text-right text-lg font-bold text-th-fgd-3 xl:text-xl">
{loadingSwapDetails ? (
<div className="w-full">
<SheenLoader className="flex flex-1 rounded-l-none">
@ -321,9 +321,24 @@ const SwapForm = () => {
</SheenLoader>
</div>
) : (
<span className="p-3 font-mono">
{amountOut ? numberFormat.format(amountOut.toNumber()) : ''}
</span>
// <span className="p-3 font-mono">
// {amountOut ? numberFormat.format(amountOut.toNumber()) : ''}
// </span>
<NumberFormat
inputMode="decimal"
thousandSeparator=","
allowNegative={false}
isNumericString={true}
decimalScale={inputTokenInfo?.decimals || 6}
name="amountIn"
id="amountIn"
className="w-full bg-th-bkg-1 p-3 text-right font-mono text-base font-bold text-th-fgd-1 focus:outline-none lg:text-lg xl:text-xl"
placeholder="0.00"
disabled
value={
amountOut ? numberFormat.format(amountOut.toNumber()) : ''
}
/>
)}
</div>
</div>

View File

@ -1,4 +1,4 @@
import Swap from './SwapForm'
import SwapForm from './SwapForm'
import mangoStore from '@store/mangoStore'
import SwapOnboardingTour from '@components/tours/SwapOnboardingTour'
import { useWallet } from '@solana/wallet-adapter-react'
@ -15,7 +15,7 @@ const SwapPage = () => {
return (
<>
<div className="grid grid-cols-12">
<div className="col-span-12 border-th-bkg-3 md:col-span-6 md:border-b lg:col-span-8">
<div className="col-span-12 border-th-bkg-3 md:col-span-6 md:border-b lg:col-span-7 xl:col-span-8">
{inputTokenInfo?.extensions?.coingeckoId &&
outputTokenInfo?.extensions?.coingeckoId ? (
<SwapTokenChart
@ -24,8 +24,8 @@ const SwapPage = () => {
/>
) : null}
</div>
<div className="col-span-12 mt-2 space-y-6 border-th-bkg-3 md:col-span-6 md:mt-0 md:border-b lg:col-span-4">
<Swap />
<div className="col-span-12 mt-2 space-y-6 border-th-bkg-3 md:col-span-6 md:mt-0 md:border-b lg:col-span-5 xl:col-span-4">
<SwapForm />
</div>
<div className="col-span-12">
<SwapInfoTabs />

View File

@ -44,7 +44,7 @@ const CustomTooltip = ({
await actions.fetchTourSettings(publicKey.toString())
}
} catch (e) {
console.log(e)
console.error(e)
} finally {
if (customOnClose) {
customOnClose()

View File

@ -94,7 +94,7 @@ const AdvancedTradeForm = () => {
if (info.source !== 'event') return
set((s) => {
s.tradeForm.price = e.value
if (s.tradeForm.baseSize && Number(e.value)) {
if (s.tradeForm.baseSize && !Number.isNaN(Number(e.value))) {
s.tradeForm.quoteSize = (
parseFloat(e.value) * parseFloat(s.tradeForm.baseSize)
).toString()
@ -111,7 +111,7 @@ const AdvancedTradeForm = () => {
set((s) => {
s.tradeForm.baseSize = e.value
if (s.tradeForm.price && Number(e.value)) {
if (s.tradeForm.price && !Number.isNaN(Number(e.value))) {
s.tradeForm.quoteSize = (
parseFloat(s.tradeForm.price) * parseFloat(e.value)
).toString()
@ -228,7 +228,7 @@ const AdvancedTradeForm = () => {
10
)
actions.reloadMangoAccount()
actions.fetchSerumOpenOrders()
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',
@ -257,7 +257,7 @@ const AdvancedTradeForm = () => {
undefined
)
actions.reloadMangoAccount()
actions.fetchSerumOpenOrders()
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',

View File

@ -1,4 +1,10 @@
import { Serum3Market, Serum3Side } from '@blockworks-foundation/mango-v4'
import {
Bank,
PerpMarket,
PerpOrder,
Serum3Market,
Serum3Side,
} from '@blockworks-foundation/mango-v4'
import { IconButton } from '@components/shared/Button'
import Loading from '@components/shared/Loading'
import SideBadge from '@components/shared/SideBadge'
@ -8,6 +14,7 @@ import { Order } from '@project-serum/serum/lib/market'
import { useWallet } from '@solana/wallet-adapter-react'
import { PublicKey } from '@solana/web3.js'
import mangoStore from '@store/mangoStore'
import Decimal from 'decimal.js'
import { useViewport } from 'hooks/useViewport'
import { useTranslation } from 'next-i18next'
import { useCallback, useState } from 'react'
@ -18,13 +25,13 @@ import MarketLogos from './MarketLogos'
const OpenOrders = () => {
const { t } = useTranslation(['common', 'trade'])
const { connected } = useWallet()
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
const [cancelId, setCancelId] = useState<string>('')
const { width } = useViewport()
const showTableView = width ? width > breakpoints.md : false
const handleCancelOrder = useCallback(
const handleCancelSerumOrder = useCallback(
async (o: Order) => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
@ -43,7 +50,7 @@ const OpenOrders = () => {
o.side === 'buy' ? Serum3Side.bid : Serum3Side.ask,
o.orderId
)
actions.fetchSerumOpenOrders()
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',
@ -65,7 +72,47 @@ const OpenOrders = () => {
[t]
)
return connected ? (
const handleCancelPerpOrder = useCallback(
async (o: PerpOrder) => {
const client = mangoStore.getState().client
const group = mangoStore.getState().group
const mangoAccount = mangoStore.getState().mangoAccount.current
const selectedMarket = mangoStore.getState().selectedMarket.current
const actions = mangoStore.getState().actions
if (!group || !mangoAccount) return
setCancelId(o.orderId.toString())
try {
if (selectedMarket instanceof Serum3Market) {
const tx = await client.perpCancelOrder(
group,
mangoAccount,
o.perpMarketIndex,
o.orderId
)
actions.fetchOpenOrders()
notify({
type: 'success',
title: 'Transaction successful',
txid: tx,
})
}
} catch (e: any) {
console.error('Error canceling', e)
notify({
title: t('trade:cancel-order-error'),
description: e.message,
txid: e.txid,
type: 'error',
})
} finally {
setCancelId('')
}
},
[t]
)
return mangoAccount ? (
Object.values(openOrders).flat().length ? (
showTableView ? (
<table>
@ -83,13 +130,22 @@ const OpenOrders = () => {
{Object.entries(openOrders)
.map(([marketPk, orders]) => {
return orders.map((o) => {
const group = mangoStore.getState().group
const serumMarket = group?.getSerum3MarketByExternalMarket(
new PublicKey(marketPk)
)
const quoteSymbol = group?.getFirstBankByTokenIndex(
serumMarket!.quoteTokenIndex
).name
const group = mangoStore.getState().group!
let market: PerpMarket | Serum3Market
let quoteSymbol
if (o instanceof PerpOrder) {
market = group.getPerpMarketByMarketIndex(o.perpMarketIndex)
quoteSymbol = group.getFirstBankByTokenIndex(
market.settleTokenIndex
).name
} else {
market = group.getSerum3MarketByExternalMarket(
new PublicKey(marketPk)
)
quoteSymbol = group.getFirstBankByTokenIndex(
market!.quoteTokenIndex
).name
}
return (
<tr
key={`${o.side}${o.size}${o.price}`}
@ -97,8 +153,8 @@ const OpenOrders = () => {
>
<td>
<div className="flex items-center">
<MarketLogos market={serumMarket!} />
{serumMarket?.name}
<MarketLogos market={market!} />
{market?.name}
</div>
</td>
<td className="text-right">
@ -127,7 +183,11 @@ const OpenOrders = () => {
<Tooltip content={t('cancel')}>
<IconButton
disabled={cancelId === o.orderId.toString()}
onClick={() => handleCancelOrder(o)}
onClick={() =>
o instanceof PerpOrder
? handleCancelPerpOrder(o)
: handleCancelSerumOrder(o)
}
size="small"
>
{cancelId === o.orderId.toString() ? (
@ -173,7 +233,7 @@ const OpenOrders = () => {
o.side === 'buy' ? 'text-th-green' : 'text-th-red'
}`}
>
{o.side}
<SideBadge side={o.side} />
</span>{' '}
<span className="font-mono">
{o.size.toLocaleString(undefined, {
@ -193,7 +253,11 @@ const OpenOrders = () => {
<span>{formatFixedDecimals(o.size * o.price, true)}</span>
<IconButton
disabled={cancelId === o.orderId.toString()}
onClick={() => handleCancelOrder(o)}
onClick={() =>
o instanceof PerpOrder
? handleCancelPerpOrder(o)
: handleCancelSerumOrder(o)
}
>
{cancelId === o.orderId.toString() ? (
<Loading className="h-4 w-4" />

View File

@ -158,18 +158,30 @@ const groupBy = (
return sortedGroups
}
const hasOpenOrderForPriceGroup = (
openOrderPrices: number[],
price: string,
grouping: number
) => {
return !!openOrderPrices.find((ooPrice) => {
return (
ooPrice >= parseFloat(price) && ooPrice < parseFloat(price) + grouping
)
})
}
const depth = 40
const Orderbook = () => {
const { t } = useTranslation(['common', 'trade'])
const selectedMarket = mangoStore((s) => s.selectedMarket.current)
// const [openOrderPrices, setOpenOrderPrices] = useState<any[]>([])
const [isScrolled, setIsScrolled] = useState(false)
const [orderbookData, setOrderbookData] = useState<any | null>(null)
const [grouping, setGrouping] = useState(0.01)
const [showBuys, setShowBuys] = useState(true)
const [showSells, setShowSells] = useState(true)
const [userOpenOrderPrices, setUserOpenOrderPrices] = useState<number[]>([])
const currentOrderbookData = useRef<any>(null)
const nextOrderbookData = useRef<any>(null)
@ -224,8 +236,6 @@ const Orderbook = () => {
useInterval(() => {
const orderbook = mangoStore.getState().selectedMarket.orderbook
console.log('orderbook', orderbook)
const group = mangoStore.getState().group
if (!market || !group) return
@ -235,17 +245,19 @@ const Orderbook = () => {
previousGrouping !== grouping)
) {
// check if user has open orders so we can highlight them on orderbook
// const openOrders = mangoStore.getState().mangoAccount.openOrders
// const newOpenOrderPrices = openOrders?.length
// ? openOrders
// .filter(({ market }) =>
// market.account.publicKey.equals(marketConfig.publicKey)
// )
// .map(({ order }) => order.price)
// : []
// if (!isEqual(newOpenOrderPrices, openOrderPrices)) {
// setOpenOrderPrices(newOpenOrderPrices)
// }
const openOrders = mangoStore.getState().mangoAccount.openOrders
const marketPk =
selectedMarket && selectedMarket instanceof PerpMarket
? selectedMarket.publicKey
: selectedMarket?.serumMarketExternal
const newUserOpenOrderPrices =
marketPk && openOrders[marketPk.toString()]?.length
? openOrders[marketPk.toString()]?.map((order) => order.price)
: []
if (!isEqual(newUserOpenOrderPrices, userOpenOrderPrices)) {
setUserOpenOrderPrices(newUserOpenOrderPrices)
}
// updated orderbook data
const bids = groupBy(orderbook?.bids, market!, grouping, true) || []
@ -480,11 +492,11 @@ const Orderbook = () => {
<MemoizedOrderbookRow
minOrderSize={market.minOrderSize}
tickSize={market.tickSize}
// hasOpenOrder={hasOpenOrderForPriceGroup(
// openOrderPrices,
// price,
// grouping
// )}
hasOpenOrder={hasOpenOrderForPriceGroup(
userOpenOrderPrices,
orderbookData?.asks[index].price,
grouping
)}
key={orderbookData?.asks[index].price}
price={orderbookData?.asks[index].price}
size={orderbookData?.asks[index].size}
@ -523,11 +535,11 @@ const Orderbook = () => {
<MemoizedOrderbookRow
minOrderSize={market.minOrderSize}
tickSize={market.tickSize}
// hasOpenOrder={hasOpenOrderForPriceGroup(
// openOrderPrices,
// price,
// grouping
// )}
hasOpenOrder={hasOpenOrderForPriceGroup(
userOpenOrderPrices,
orderbookData?.bids[index].price,
grouping
)}
price={orderbookData?.bids[index].price}
size={orderbookData?.bids[index].size}
side="buy"
@ -552,7 +564,7 @@ const OrderbookRow = ({
size,
sizePercent,
// invert,
// hasOpenOrder,
hasOpenOrder,
minOrderSize,
cumulativeSizePercent,
tickSize,
@ -563,7 +575,7 @@ const OrderbookRow = ({
size: number
sizePercent: number
cumulativeSizePercent: number
// hasOpenOrder: boolean
hasOpenOrder: boolean
// invert: boolean
grouping: number
minOrderSize: number
@ -623,6 +635,10 @@ const OrderbookRow = ({
if (!minOrderSize) return null
if (hasOpenOrder) {
console.log('HAS OPEN ORDER')
}
return (
<div
className={`relative flex h-[24px] cursor-pointer justify-between border-b border-b-th-bkg-1 text-sm`}
@ -635,7 +651,7 @@ const OrderbookRow = ({
<div
style={{ fontFeatureSettings: 'zero 1' }}
className={`z-10 w-full text-right font-mono text-xs ${
/*hasOpenOrder*/ false ? 'text-th-primary' : ''
hasOpenOrder ? 'text-th-primary' : ''
}`}
// onClick={handleSizeClick}
>

View File

@ -0,0 +1,59 @@
import SideBadge from '@components/shared/SideBadge'
import mangoStore from '@store/mangoStore'
import { useTranslation } from 'next-i18next'
import MarketLogos from './MarketLogos'
import PerpSideBadge from './PerpSideBadge'
const PerpPositions = () => {
const { t } = useTranslation(['common', 'trade'])
const group = mangoStore((s) => s.group)
const perpPositions = mangoStore((s) => s.mangoAccount.perpPositions)
if (!group) return null
return (
<div>
<table>
<thead>
<tr>
<th className="text-left">{t('market')}</th>
<th className="text-right">{t('trade:side')}</th>
<th className="text-right">{t('trade:size')}</th>
<th className="text-right">{t('value')}</th>
</tr>
</thead>
<tbody>
{Object.entries(perpPositions).map(([mkt, position]) => {
const market = group.getPerpMarketByMarketIndex(
position.marketIndex
)
const basePosition = position.getBasePositionUi(market)
return (
<tr key={`${position.marketIndex}`} className="my-1 p-2">
<td>
<div className="flex items-center">
<MarketLogos market={market!} />
{market?.name}
</div>
</td>
<td className="text-right">
<PerpSideBadge basePosition={basePosition} />
</td>
<td className="text-right">
<div className="">{basePosition}</div>
</td>
<td className="text-right">
<div className="">
${Math.abs(basePosition * market._uiPrice).toFixed(2)}
</div>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
)
}
export default PerpPositions

View File

@ -0,0 +1,13 @@
import SideBadge from '@components/shared/SideBadge'
const PerpSideBadge = ({ basePosition }: { basePosition: number }) => (
<>
{basePosition !== 0 ? (
<SideBadge side={basePosition > 0 ? 'long' : 'short'} />
) : (
'--'
)}
</>
)
export default PerpSideBadge

View File

@ -5,11 +5,12 @@ import SwapTradeBalances from '../shared/SwapTradeBalances'
import UnsettledTrades from './UnsettledTrades'
import mangoStore from '@store/mangoStore'
import { useUnsettledSpotBalances } from 'hooks/useUnsettledSpotBalances'
import PerpPositions from './PerpPositions'
const TradeInfoTabs = () => {
const [selectedTab, setSelectedTab] = useState('balances')
const openOrders = mangoStore((s) => s.mangoAccount.openOrders)
const mangoAccount = mangoStore((s) => s.mangoAccount.current)
const perpPositions = mangoStore((s) => s.mangoAccount.perpPositions)
const unsettledSpotBalances = useUnsettledSpotBalances()
const tabsWithCount: [string, number][] = useMemo(() => {
@ -17,8 +18,9 @@ const TradeInfoTabs = () => {
['balances', 0],
['trade:orders', Object.values(openOrders).flat().length],
['trade:unsettled', Object.values(unsettledSpotBalances).flat().length],
['Positions', perpPositions.length],
]
}, [openOrders, mangoAccount])
}, [openOrders, perpPositions, unsettledSpotBalances])
return (
<div className="hide-scroll h-full overflow-y-scroll pb-5">
@ -35,6 +37,7 @@ const TradeInfoTabs = () => {
{selectedTab === 'trade:unsettled' ? (
<UnsettledTrades unsettledSpotBalances={unsettledSpotBalances} />
) : null}
{selectedTab === 'Positions' ? <PerpPositions /> : null}
</div>
)
}

View File

@ -39,7 +39,7 @@ const UnsettledTrades = ({
mangoAccount,
new PublicKey(mktAddress)
)
actions.fetchSerumOpenOrders()
actions.fetchOpenOrders()
actions.reloadMangoAccount()
notify({
type: 'success',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -23,12 +23,15 @@
"borrow-value": "Borrow Value",
"buy": "Buy",
"cancel": "Cancel",
"clear-all": "Clear All",
"close-account": "Close Account",
"close-account-desc": "Are you sure? Closing your account is irreversible.",
"closing-account": "Closing your account...",
"collateral-value": "Collateral Value",
"connect": "Connect",
"connect-helper": "Connect to get started",
"copy-address": "Copy Address",
"copy-address-success": "Copied Mango Account address",
"create-account": "Create Account",
"creating-account": "Creating Account...",
"cumulative-interest-value": "Cumulative Interest Earned",

View File

@ -1,6 +1,6 @@
{
"browse-profiles": "Browse",
"choose-profile": "Choose an NFT",
"choose-profile": "Profile Image",
"connect-view-profile": "Connect your wallet to view your profile",
"day-trader": "Day Trader",
"degen": "Degen",

View File

@ -23,12 +23,15 @@
"borrow-value": "Borrow Value",
"buy": "Buy",
"cancel": "Cancel",
"clear-all": "Clear All",
"close-account": "Close Account",
"close-account-desc": "Are you sure? Closing your account is irreversible.",
"closing-account": "Closing your account...",
"collateral-value": "Collateral Value",
"connect": "Connect",
"connect-helper": "Connect to get started",
"copy-address": "Copy Address",
"copy-address-success": "Copied Mango Account address",
"create-account": "Create Account",
"creating-account": "Creating Account...",
"cumulative-interest-value": "Cumulative Interest Earned",

View File

@ -1,6 +1,6 @@
{
"browse-profiles": "Browse",
"choose-profile": "Choose an NFT",
"choose-profile": "Profile Image",
"connect-view-profile": "Connect your wallet to view your profile",
"day-trader": "Day Trader",
"degen": "Degen",

View File

@ -23,12 +23,15 @@
"borrow-value": "Borrow Value",
"buy": "Buy",
"cancel": "Cancel",
"clear-all": "Clear All",
"close-account": "Close Account",
"close-account-desc": "Are you sure? Closing your account is irreversible.",
"closing-account": "Closing your account...",
"collateral-value": "Collateral Value",
"connect": "Connect",
"connect-helper": "Connect to get started",
"copy-address": "Copy Address",
"copy-address-success": "Copied Mango Account address",
"create-account": "Create Account",
"creating-account": "Creating Account...",
"cumulative-interest-value": "Cumulative Interest Value",

View File

@ -23,12 +23,15 @@
"borrow-value": "Borrow Value",
"buy": "Buy",
"cancel": "Cancel",
"clear-all": "Clear All",
"close-account": "Close Account",
"close-account-desc": "Are you sure? Closing your account is irreversible.",
"closing-account": "Closing your account...",
"collateral-value": "Collateral Value",
"connect": "Connect",
"connect-helper": "Connect to get started",
"copy-address": "Copy Address",
"copy-address-success": "Copied Mango Account address",
"create-account": "Create Account",
"creating-account": "Creating Account...",
"cumulative-interest-value": "Cumulative Interest Earned",

View File

@ -23,12 +23,15 @@
"borrow-value": "Borrow Value",
"buy": "Buy",
"cancel": "Cancel",
"clear-all": "Clear All",
"close-account": "Close Account",
"close-account-desc": "Are you sure? Closing your account is irreversible.",
"closing-account": "Closing your account...",
"collateral-value": "Collateral Value",
"connect": "Connect",
"connect-helper": "Connect to get started",
"copy-address": "Copy Address",
"copy-address-success": "Copied Mango Account address",
"create-account": "Create Account",
"creating-account": "Creating Account...",
"cumulative-interest-value": "Cumulative Interest Earned",

View File

@ -14,6 +14,8 @@ import {
Serum3Market,
MANGO_V4_ID,
Bank,
PerpOrder,
PerpPosition,
} from '@blockworks-foundation/mango-v4'
import EmptyWallet from '../utils/wallet'
@ -25,7 +27,6 @@ import {
} from '../utils/tokens'
import { Token } from '../types/jupiter'
import {
COINGECKO_IDS,
DEFAULT_MARKET_NAME,
INPUT_TOKEN_DEFAULT,
LAST_ACCOUNT_KEY,
@ -35,6 +36,7 @@ import { retryFn } from '../utils'
import { Orderbook, SpotBalances } from 'types'
import spotBalancesUpdater from './spotBalancesUpdater'
import { PerpMarket } from '@blockworks-foundation/mango-v4/'
import perpPositionsUpdater from './perpPositionsUpdater'
const GROUP = new PublicKey('DLdcpC6AsAJ9xeKMR3WhHrN5sM5o7GVVXQhQ5vwisTtz')
@ -178,7 +180,8 @@ export type MangoStore = {
lastUpdatedAt: string
lastSlot: number
openOrderAccounts: OpenOrders[]
openOrders: Record<string, Order[]>
openOrders: Record<string, Order[] | PerpOrder[]>
perpPositions: PerpPosition[]
spotBalances: SpotBalances
stats: {
interestTotals: { data: TotalInterestDataItem[]; loading: boolean }
@ -249,7 +252,7 @@ export type MangoStore = {
reloadMangoAccount: () => Promise<void>
fetchMangoAccounts: (wallet: Wallet) => Promise<void>
fetchNfts: (connection: Connection, walletPk: PublicKey) => void
fetchSerumOpenOrders: (ma?: MangoAccount) => Promise<void>
fetchOpenOrders: (ma?: MangoAccount) => Promise<void>
fetchProfileDetails: (walletPk: string) => void
fetchSwapHistory: (mangoAccountPk: string) => Promise<void>
fetchTourSettings: (walletPk: string) => void
@ -284,6 +287,7 @@ const mangoStore = create<MangoStore>()(
lastUpdatedAt: '',
openOrderAccounts: [],
openOrders: {},
perpPositions: [],
spotBalances: {},
stats: {
interestTotals: { data: [], loading: false },
@ -479,23 +483,32 @@ const mangoStore = create<MangoStore>()(
state.coingeckoPrices.loading = true
})
try {
const promises: any = []
for (const asset of COINGECKO_IDS) {
promises.push(
fetch(
`https://api.coingecko.com/api/v3/coins/${asset.id}/market_chart?vs_currency=usd&days=1`
).then((res) => res.json())
)
}
const jupiterTokens = mangoStore.getState().jupiterTokens
if (jupiterTokens.length) {
const coingeckoIds = jupiterTokens.map((token) => ({
id: token.extensions?.coingeckoId,
symbol: token.symbol,
}))
const promises: any = []
for (const token of coingeckoIds) {
if (token.id) {
promises.push(
fetch(
`https://api.coingecko.com/api/v3/coins/${token.id}/market_chart?vs_currency=usd&days=1`
).then((res) => res.json())
)
}
}
const data = await Promise.all(promises)
for (let i = 0; i < data.length; i++) {
data[i].symbol = COINGECKO_IDS[i].symbol
const data = await Promise.all(promises)
for (let i = 0; i < data.length; i++) {
data[i].symbol = coingeckoIds[i].symbol
}
set((state) => {
state.coingeckoPrices.data = data
state.coingeckoPrices.loading = false
})
}
set((state) => {
state.coingeckoPrices.data = data
state.coingeckoPrices.loading = false
})
} catch (e) {
console.warn('Unable to load Coingecko prices')
set((state) => {
@ -604,7 +617,7 @@ const mangoStore = create<MangoStore>()(
}
if (newSelectedMangoAccount) {
await actions.fetchSerumOpenOrders(newSelectedMangoAccount)
await actions.fetchOpenOrders(newSelectedMangoAccount)
}
set((state) => {
@ -639,7 +652,7 @@ const mangoStore = create<MangoStore>()(
}
return []
},
fetchSerumOpenOrders: async (providedMangoAccount) => {
fetchOpenOrders: async (providedMangoAccount) => {
const set = get().set
const client = get().client
const group = await client.getGroup(GROUP)
@ -647,11 +660,12 @@ const mangoStore = create<MangoStore>()(
providedMangoAccount || get().mangoAccount.current
if (!mangoAccount) return
console.log('mangoAccount', mangoAccount)
try {
let openOrders: Record<string, Order[]> = {}
for (const serum3Orders of mangoAccount.serum3) {
let openOrders: Record<string, Order[] | PerpOrder[]> = {}
let serumOpenOrderAccounts: OpenOrders[] = []
for (const serum3Orders of mangoAccount.serum3Active()) {
if (serum3Orders.marketIndex === 65535) continue
const market = group.getSerum3MarketByMarketIndex(
serum3Orders.marketIndex
@ -665,14 +679,30 @@ const mangoStore = create<MangoStore>()(
openOrders[market.serumMarketExternal.toString()] = orders
}
}
if (Object.keys(openOrders).length) {
const serumOpenOrderAccounts =
if (
mangoAccount.serum3Active().length &&
Object.keys(openOrders).length
) {
serumOpenOrderAccounts =
await mangoAccount.loadSerum3OpenOrdersAccounts(client)
set((s) => {
s.mangoAccount.openOrders = openOrders
s.mangoAccount.openOrderAccounts = serumOpenOrderAccounts
})
}
for (const perpOrder of mangoAccount.perpOrdersActive()) {
const market = group.getPerpMarketByMarketIndex(
perpOrder.orderMarket
)
const orders = await mangoAccount.loadPerpOpenOrdersForMarket(
client,
group,
perpOrder.orderMarket
)
openOrders[market.publicKey.toString()] = orders
}
set((s) => {
s.mangoAccount.openOrders = openOrders
s.mangoAccount.openOrderAccounts = serumOpenOrderAccounts
})
} catch (e) {
console.error('Failed loading open orders ', e)
}
@ -829,7 +859,7 @@ const mangoStore = create<MangoStore>()(
})
} catch (e) {
notify({ type: 'error', title: 'Failed to load profile details' })
console.log(e)
console.error(e)
set((state) => {
state.profile.loadDetails = false
})
@ -863,6 +893,10 @@ const mangoStore = create<MangoStore>()(
)
mangoStore.subscribe((state) => state.mangoAccount.current, spotBalancesUpdater)
mangoStore.subscribe(
(state) => state.mangoAccount.current,
perpPositionsUpdater
)
const getDefaultSelectedMarket = (markets: Serum3Market[]): Serum3Market => {
return markets.find((m) => m.name === DEFAULT_MARKET_NAME) || markets[0]

View File

@ -0,0 +1,31 @@
import {
Group,
MangoAccount,
PerpMarket,
PerpPosition,
toUiI80F48,
} from '@blockworks-foundation/mango-v4'
import mangoStore from './mangoStore'
const perpPositionsUpdater = (_newState: any, _prevState: any) => {
const mangoAccount = mangoStore.getState().mangoAccount.current
const group = mangoStore.getState().group
const set = mangoStore.getState().set
if (!mangoAccount || !group) return
const positions: PerpPosition[] = []
for (const perpMarket of mangoAccount.perpActive()) {
const position = mangoAccount.getPerpPosition(perpMarket.marketIndex)
if (position) {
positions.push(position)
}
}
set((s) => {
s.mangoAccount.perpPositions = positions
})
}
export default perpPositionsUpdater

View File

@ -30,25 +30,6 @@ export const PROFILE_CATEGORIES = [
'yolo',
]
export const COINGECKO_IDS = [
{ id: 'bitcoin', symbol: 'BTC' },
{ id: 'ethereum', symbol: 'ETH' },
{ id: 'solana', symbol: 'SOL' },
{ id: 'mango-markets', symbol: 'MNGO' },
// { id: 'binancecoin', symbol: 'BNB' },
// { id: 'serum', symbol: 'SRM' },
{ id: 'raydium', symbol: 'RAY' },
// { id: 'ftx-token', symbol: 'FTT' },
// { id: 'avalanche-2', symbol: 'AVAX' },
// { id: 'terra-luna', symbol: 'LUNA' },
// { id: 'cope', symbol: 'COPE' },
// { id: 'cardano', symbol: 'ADA' },
{ id: 'msol', symbol: 'MSOL' },
{ id: 'usd-coin', symbol: 'USDC' },
{ id: 'tether', symbol: 'USDT' },
// { id: 'stepn', symbol: 'GMT' },
]
const baseUrl = 'https://event-history-api-candles.herokuapp.com'
export const CHART_DATA_FEED = `${baseUrl}/tv`

View File

@ -34,3 +34,12 @@ export function isEqual(obj1: any, obj2: any, keys: Array<string>) {
}
return true
}
export const copyToClipboard = (copyThis: string) => {
const el = document.createElement('textarea')
el.value = copyThis.toString()
document.body.appendChild(el)
el.select()
document.execCommand('copy')
document.body.removeChild(el)
}

View File

@ -30,9 +30,9 @@
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.10.5", "@babel/runtime@^7.12.5", "@babel/runtime@^7.17.2":
version "7.20.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.0.tgz#824a9ef325ffde6f78056059db3168c08785e24a"
integrity sha512-NDYdls71fTXoU8TZHfbBWg7DiZfNzClcKui/+kyi6ppD2L1qnWW3VV6CjtaBXSUGGhiTWJ6ereOIkUvenif66Q==
version "7.20.1"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9"
integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==
dependencies:
regenerator-runtime "^0.13.10"
@ -50,9 +50,9 @@
dependencies:
regenerator-runtime "^0.13.4"
"@blockworks-foundation/mango-v4@git+https://mschneider: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@github.com/blockworks-foundation/mango-v4.git#main":
"@blockworks-foundation/mango-v4@https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#main":
version "0.0.1-beta.5"
resolved "git+https://mschneider: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@github.com/blockworks-foundation/mango-v4.git#6ea338df6dfbb14fb238d3157fbd6f4c5d5a1709"
resolved "https://tylersssss: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@github.com/blockworks-foundation/mango-v4.git#62b1944b2ff249cfc138c3e2bf266f871d32182f"
dependencies:
"@project-serum/anchor" "^0.25.0"
"@project-serum/serum" "^0.13.65"
@ -1766,9 +1766,9 @@
integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
"@types/node@*", "@types/node@>=13.7.0":
version "18.11.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.7.tgz#8ccef136f240770c1379d50100796a6952f01f94"
integrity sha512-LhFTglglr63mNXUSRYD8A+ZAIu5sFqNJ4Y2fPuY7UlrySJH87rRRlhtVmMHplmfk5WkoJGmDjE9oiTfyX94CpQ==
version "18.11.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4"
integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==
"@types/node@17.0.23":
version "17.0.23"
@ -4569,9 +4569,9 @@ jmespath@^0.15.0:
integrity sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w==
joi@^17.4.0:
version "17.6.4"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.6.4.tgz#4d9536a059ef0762c718ae98673016b3ec151abd"
integrity sha512-tPzkTJHZQjSFCc842QpdVpOZ9LI2txApboNUbW70qgnRB14Lzl+oWQOPdF2N4yqyiY14wBGe8lc7f/2hZxbGmw==
version "17.7.0"
resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3"
integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg==
dependencies:
"@hapi/hoek" "^9.0.0"
"@hapi/topo" "^5.0.0"
@ -6512,7 +6512,12 @@ tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.4.0:
tslib@^2.0.3, tslib@^2.1.0:
version "2.4.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e"
integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==
tslib@^2.3.0, tslib@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==