+
Select a token to Boost!
@@ -210,8 +201,8 @@ const Stake = () => {
) : null}
>
) : (
-
-
+ <>
+
set((state) => {
@@ -225,13 +216,6 @@ const Stake = () => {
Boost! {selectedToken}
- {/*
-
- setShowTokenSelect(true)}
- tokenName={selectedToken}
- />
-
*/}
{selectedToken === 'USDC' ? (
{
}
/>
)}
-
+ >
)
) : (
@@ -259,7 +243,7 @@ const Stake = () => {
)}
- {selectedToken ? (
+ {/* {selectedToken ? (
)}
- ) : null}
+ ) : null} */}
>
)
}
diff --git a/components/StakeForm.tsx b/components/StakeForm.tsx
index 708005b..a80899b 100644
--- a/components/StakeForm.tsx
+++ b/components/StakeForm.tsx
@@ -34,7 +34,11 @@ import Link from 'next/link'
import LeverageSlider from './shared/LeverageSlider'
import useMangoGroup from 'hooks/useMangoGroup'
import FormatNumericValue from './shared/FormatNumericValue'
-import { getNextAccountNumber, stakeAndCreate } from 'utils/transactions'
+import {
+ getNextAccountNumber,
+ stakeAndCreate,
+ walletSwap,
+} from 'utils/transactions'
// import { MangoAccount } from '@blockworks-foundation/mango-v4'
import useBankRates from 'hooks/useBankRates'
import { Disclosure } from '@headlessui/react'
@@ -50,6 +54,12 @@ import {
JLP_BORROW_TOKEN,
LST_BORROW_TOKEN,
} from 'utils/constants'
+import {
+ HERO_TOKEN_BUTTON_CLASSES,
+ HERO_TOKEN_IMAGE_WRAPPER_CLASSES,
+} from './HeroTokenButton'
+import Image from 'next/image'
+import useQuoteRoutes from 'hooks/useQuoteRoutes'
const set = mangoStore.getState().set
@@ -98,6 +108,7 @@ export const walletBalanceForToken = (
function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
const { t } = useTranslation(['common', 'account'])
+ const [depositToken, setDepositToken] = useState(selectedToken)
const [inputAmount, setInputAmount] = useState('')
const [sizePercentage, setSizePercentage] = useState('')
const submitting = mangoStore((s) => s.submittingBoost)
@@ -105,7 +116,8 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
const [refreshingWalletTokens, setRefreshingWalletTokens] = useState(false)
const { maxSolDeposit } = useSolBalance()
const { ipAllowed } = useIpAddress()
-
+ const wallet = useWallet()
+ const connection = mangoStore((s) => s.connection)
const storedLeverage = mangoStore((s) => s.leverage)
const { usedTokens, totalTokens } = useMangoAccountAccounts()
const { jlpGroup, lstGroup } = useMangoGroup()
@@ -115,8 +127,10 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
leverage,
)
const leverageMax = useLeverageMax(selectedToken)
+ const { connected, publicKey } = useWallet()
+ const walletTokens = mangoStore((s) => s.wallet.tokens)
- const [stakeBank, borrowBank] = useMemo(() => {
+ const [stakeBank, borrowBank, depositBank] = useMemo(() => {
const stakeBank =
clientContext === 'jlp'
? jlpGroup?.banksMapByName.get(selectedToken)?.[0]
@@ -125,9 +139,37 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
clientContext === 'jlp'
? jlpGroup?.banksMapByName.get(JLP_BORROW_TOKEN)?.[0]
: lstGroup?.banksMapByName.get(LST_BORROW_TOKEN)?.[0]
- return [stakeBank, borrowBank]
- }, [selectedToken, jlpGroup, lstGroup, clientContext])
+ const backupBank =
+ clientContext === 'jlp'
+ ? jlpGroup?.banksMapByName.get('USDC')?.[0]
+ : lstGroup?.banksMapByName.get('SOL')?.[0]
+ const depositBank = depositToken !== selectedToken ? backupBank : stakeBank
+ return [stakeBank, borrowBank, depositBank]
+ }, [selectedToken, jlpGroup, lstGroup, clientContext, depositToken])
+ const isSwapMode = depositToken !== selectedToken
+ const {
+ bestRoute,
+ isFetching: fetchingRoute,
+ // refetch: refetchRoute,
+ } = useQuoteRoutes({
+ inputMint: depositBank?.mint.toString(),
+ outputMint: stakeBank?.mint.toString(),
+ amount: inputAmount,
+ slippage: 0.5,
+ swapMode: 'ExactIn',
+ wallet: publicKey?.toBase58(),
+ mangoAccount: undefined,
+ routingMode: 'ALL',
+ inDecimals: depositBank?.mintDecimals,
+ outDecimals: stakeBank?.mintDecimals,
+ enabled: () =>
+ !!(stakeBank?.mint && depositBank?.mint && inputAmount && isSwapMode),
+ })
+ const uiOutAmount =
+ bestRoute?.outAmount && stakeBank
+ ? toUiDecimals(bestRoute.outAmount, stakeBank.mintDecimals) * 0.999
+ : 0
const liquidationPrice = useMemo(() => {
let price
if (borrowBank?.name == 'SOL') {
@@ -154,47 +196,70 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
return hasTokenPosition ? false : usedTokens.length >= totalTokens.length
}, [stakeBank, usedTokens, totalTokens])
- const { connected, publicKey } = useWallet()
- const walletTokens = mangoStore((s) => s.wallet.tokens)
-
const tokenMax = useMemo(() => {
- return walletBalanceForToken(walletTokens, selectedToken, clientContext)
- }, [walletTokens, selectedToken, clientContext])
+ if (!depositBank) return { maxAmount: 0, maxDecimals: 0 }
+ return walletBalanceForToken(walletTokens, depositBank.name, clientContext)
+ }, [walletTokens, depositBank, clientContext])
const setMax = useCallback(() => {
- const max = floorToDecimal(tokenMax.maxAmount, 6)
+ if (!depositBank) return
+ let max = new Decimal(0)
+
+ if (depositBank.name === 'SOL') {
+ max = floorToDecimal(tokenMax.maxAmount - 0.01, depositBank.mintDecimals)
+ } else {
+ max = floorToDecimal(tokenMax.maxAmount, depositBank.mintDecimals)
+ }
setInputAmount(max.toFixed())
setSizePercentage('100')
- }, [tokenMax])
+ }, [depositBank, tokenMax])
const handleSizePercentage = useCallback(
(percentage: string) => {
- if (!stakeBank) return
+ if (!depositBank) return
setSizePercentage(percentage)
- const amount = floorToDecimal(
- new Decimal(percentage).div(100).mul(tokenMax.maxAmount),
- stakeBank.mintDecimals,
- )
+ let amount = new Decimal(0)
+ if (depositBank.name === 'SOL' && percentage === '100') {
+ amount = floorToDecimal(
+ new Decimal(percentage).div(100).mul(tokenMax.maxAmount),
+ depositBank.mintDecimals,
+ ).sub(0.01)
+ } else {
+ amount = floorToDecimal(
+ new Decimal(percentage).div(100).mul(tokenMax.maxAmount),
+ depositBank.mintDecimals,
+ )
+ }
setInputAmount(amount.toFixed())
},
- [tokenMax, stakeBank],
+ [tokenMax, depositBank],
)
const amountToBorrow = useMemo(() => {
+ const stakeAmount =
+ isSwapMode && bestRoute && stakeBank ? uiOutAmount : inputAmount
const borrowPrice = borrowBank?.uiPrice
const stakePrice = stakeBank?.uiPrice
- if (!borrowPrice || !stakePrice || !Number(inputAmount)) return 0
+ if (!borrowPrice || !stakePrice || !Number(stakeAmount)) return 0
if (clientContext === 'jlp') {
const borrowAmount =
- stakeBank?.uiPrice * Number(inputAmount) * (leverage - 1)
+ stakeBank?.uiPrice * Number(stakeAmount) * (leverage - 1)
return borrowAmount
} else {
const priceDifference = (stakePrice - borrowPrice) / borrowPrice
const borrowAmount =
- (1 + priceDifference) * Number(inputAmount) * (leverage - 1)
+ (1 + priceDifference) * Number(stakeAmount) * (leverage - 1)
return borrowAmount
}
- }, [leverage, borrowBank, stakeBank, inputAmount])
+ }, [
+ isSwapMode,
+ bestRoute,
+ stakeBank,
+ inputAmount,
+ borrowBank?.uiPrice,
+ clientContext,
+ leverage,
+ ])
const availableVaultBalance = useMemo(() => {
if (!borrowBank) return 0
@@ -235,17 +300,40 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
})
try {
// const newAccountfNum = getNextAccountNumber(mangoAccounts)
+ let stakeAmount = parseFloat(inputAmount)
+ if (!bestRoute && isSwapMode) {
+ notify({
+ title: 'No swap route found',
+ type: 'error',
+ })
+ }
notify({
title: 'Building transaction. This may take a moment.',
type: 'info',
})
+ if (isSwapMode && bestRoute) {
+ const { txid, outAmount } = await walletSwap(
+ bestRoute,
+ connection,
+ 0.5,
+ wallet,
+ client,
+ )
+ stakeAmount = toUiDecimals(outAmount, stakeBank.mintDecimals)
+ notify({
+ title: 'Transaction confirmed',
+ type: 'success',
+ txid: txid,
+ })
+ }
+
const { signature: tx, slot } = await stakeAndCreate(
client,
group,
mangoAccount,
amountToBorrow,
stakeBank.mint,
- parseFloat(inputAmount),
+ stakeAmount,
accNumber ?? 0,
)
notify({
@@ -281,9 +369,13 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
ipAllowed,
stakeBank,
publicKey,
- amountToBorrow,
- inputAmount,
clientContext,
+ inputAmount,
+ bestRoute,
+ isSwapMode,
+ amountToBorrow,
+ connection,
+ wallet,
])
const showInsufficientBalance =
@@ -318,8 +410,57 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
})
}, [selectedToken, clientContext])
+ const handleDepositTokenChange = (token: string) => {
+ setDepositToken(token)
+ setInputAmount('')
+ setSizePercentage('')
+ }
+
return (
<>
+
Token to deposit
+
+
+
+
)}
-
+
-
+
-
+
- {formatTokenSymbol(selectedToken)}
+ {formatTokenSymbol(depositToken)}
@@ -416,12 +557,30 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
}
/>
) : (
-
handleSizePercentage(p)}
- values={['10', '25', '50', '75', '100']}
- unit="%"
- />
+ <>
+ handleSizePercentage(p)}
+ values={['10', '25', '50', '75', '100']}
+ unit="%"
+ />
+ {depositToken !== selectedToken && stakeBank ? (
+
+ {fetchingRoute ? (
+
+
+
+ ) : (
+
+ )}
+
+ ) : null}
+ >
)}
{depositLimitExceeded ? (
@@ -435,7 +594,7 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
) : null}
-
+
{leverage}x
@@ -684,7 +843,9 @@ function StakeForm({ token: selectedToken, clientContext }: StakeFormProps) {
})}
) : ipAllowed ? (
- `Boost! ${inputAmount} ${formatTokenSymbol(selectedToken)}`
+ `Boost! ${
+ isSwapMode && uiOutAmount ? uiOutAmount : inputAmount
+ } ${formatTokenSymbol(selectedToken)}`
) : (
'Country not allowed'
)}
diff --git a/components/notifications/TransactionNotification.tsx b/components/notifications/TransactionNotification.tsx
index 41fca7f..5ce2ab6 100644
--- a/components/notifications/TransactionNotification.tsx
+++ b/components/notifications/TransactionNotification.tsx
@@ -112,13 +112,13 @@ const TransactionNotificationList = () => {
) : null}
{reversedNotifications.map((n) => (
-
+
))}
)
}
-const TransactionNotification = ({
+const TransactionNotificationComponent = ({
notification,
}: {
notification: TransactionNotification
diff --git a/components/shared/TokenLogo.tsx b/components/shared/TokenLogo.tsx
index c6682ac..7756aac 100644
--- a/components/shared/TokenLogo.tsx
+++ b/components/shared/TokenLogo.tsx
@@ -17,7 +17,6 @@ const TokenLogo = ({
if (!bank) return ''
const tokenSymbol = bank.name.toLowerCase()
const hasCustomIcon = CUSTOM_TOKEN_ICONS[tokenSymbol]
- console.log(tokenSymbol)
if (hasCustomIcon) return `/icons/${tokenSymbol}.svg`
let jupiterLogoURI
if (mangoTokens?.length) {
diff --git a/hooks/useAccountHistory.ts b/hooks/useAccountHistory.ts
index 633d871..a941b24 100644
--- a/hooks/useAccountHistory.ts
+++ b/hooks/useAccountHistory.ts
@@ -84,7 +84,7 @@ export default function useAccountHistory() {
cacheTime: 1000 * 60 * 5,
staleTime: 1000 * 60,
retry: 3,
- refetchOnWindowFocus: true,
+ refetchOnWindowFocus: false,
enabled: !!stakeAccounts,
},
)
diff --git a/hooks/useAnalytics.ts b/hooks/useAnalytics.ts
new file mode 100644
index 0000000..33a2b0f
--- /dev/null
+++ b/hooks/useAnalytics.ts
@@ -0,0 +1,60 @@
+import useMangoAccount from './useMangoAccount'
+import { useWallet } from '@solana/wallet-adapter-react'
+import { WHITE_LIST_API } from 'utils/constants'
+import { useCallback } from 'react'
+
+export default function useAnalytics() {
+ // const { group } = useMangoGroup()
+ const { mangoAccountAddress } = useMangoAccount()
+ const { publicKey } = useWallet()
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ // const ignoredMints = [
+ // 'mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So',
+ // 'So11111111111111111111111111111111111111112',
+ // 'J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn',
+ // 'bSo13r4TkiE4KumL71LsHTPpL2euBYLFx6h9HP3piy1',
+ // '7Q2afV64in6N6SeZsAAB81TJzwDoD6zpqmHkzi9Dcavn',
+ // '7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7ARj',
+ // ]
+ // const banks = useMemo(() => {
+ // return group && mangoAccount
+ // ? [...group.banksMapByMint.values()].filter(
+ // (x) =>
+ // x.length &&
+ // x[0] &&
+ // x[0].collateralFeePerDay > 0 &&
+ // !ignoredMints.includes(x[0].mint.toBase58()) &&
+ // mangoAccount.getTokenBalanceUi(x[0]) * x[0].uiPrice > 10000,
+ // )
+ // : []
+ // }, [group, ignoredMints, mangoAccount])
+
+ const sendAnalytics = useCallback(
+ async (data: object, tag: string) => {
+ if (publicKey?.toBase58() && tag && data && mangoAccountAddress) {
+ const enchantedData = JSON.stringify({
+ mangoAccountAddress: mangoAccountAddress,
+ ...data,
+ })
+
+ await fetch(`${WHITE_LIST_API}analytics/add`, {
+ method: 'POST',
+ headers: {
+ Accept: 'application/json',
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ wallet: publicKey.toBase58(),
+ data: enchantedData,
+ tag: tag,
+ }),
+ })
+ }
+ },
+ [mangoAccountAddress, publicKey],
+ )
+
+ return {
+ sendAnalytics,
+ }
+}
diff --git a/hooks/useJupiterSwapData.ts b/hooks/useJupiterSwapData.ts
new file mode 100644
index 0000000..1955413
--- /dev/null
+++ b/hooks/useJupiterSwapData.ts
@@ -0,0 +1,43 @@
+import { useMemo } from 'react'
+import useJupiterMints from 'hooks/useJupiterMints'
+import mangoStore from '@store/mangoStore'
+
+const useJupiterSwapData = () => {
+ const inputBank = mangoStore((s) => s.swap.inputBank)
+ const outputBank = mangoStore((s) => s.swap.outputBank)
+ const { mangoTokens } = useJupiterMints()
+
+ const [inputTokenInfo, outputTokenInfo] = useMemo(() => {
+ if (inputBank && outputBank) {
+ return [
+ mangoTokens?.find(
+ (item) => item?.address === inputBank.mint.toString() || '',
+ ),
+ mangoTokens?.find(
+ (item) => item?.address === outputBank.mint.toString() || '',
+ ),
+ ]
+ } else {
+ return []
+ }
+ }, [inputBank, outputBank, mangoTokens])
+
+ let inputCoingeckoId = inputTokenInfo?.extensions?.coingeckoId
+ let outputCoingeckoId = outputTokenInfo?.extensions?.coingeckoId
+
+ if (inputBank?.name.toLocaleLowerCase() === 'dai') {
+ inputCoingeckoId = 'dai'
+ }
+ if (outputBank?.name.toLocaleLowerCase() === 'dai') {
+ outputCoingeckoId = 'dai'
+ }
+
+ return {
+ inputTokenInfo,
+ inputCoingeckoId,
+ outputTokenInfo,
+ outputCoingeckoId,
+ }
+}
+
+export default useJupiterSwapData
diff --git a/hooks/useQuoteRoutes.ts b/hooks/useQuoteRoutes.ts
new file mode 100644
index 0000000..7cb050d
--- /dev/null
+++ b/hooks/useQuoteRoutes.ts
@@ -0,0 +1,665 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+import {
+ AddressLookupTableAccount,
+ Connection,
+ PublicKey,
+ TransactionInstruction,
+ TransactionMessage,
+ VersionedTransaction,
+} from '@solana/web3.js'
+import { useQuery } from '@tanstack/react-query'
+import Decimal from 'decimal.js'
+import { JupiterV6RouteInfo } from 'types/jupiter'
+import { MANGO_ROUTER_API_URL } from 'utils/constants'
+import { useMemo } from 'react'
+import { JUPITER_V6_QUOTE_API_MAINNET } from 'utils/constants'
+import { MangoAccount, toUiDecimals } from '@blockworks-foundation/mango-v4'
+import { findRaydiumPoolInfo, getSwapTransaction } from 'utils/swap/raydium'
+import mangoStore from '@store/mangoStore'
+import useAnalytics from './useAnalytics'
+
+type SwapModes = 'ExactIn' | 'ExactOut'
+
+type MultiRoutingMode = 'ALL' | 'ALL_AND_JUPITER_DIRECT'
+
+type JupiterRoutingMode = 'JUPITER_DIRECT' | 'JUPITER'
+
+type RaydiumRoutingMode = 'RAYDIUM'
+
+type MangoRoutingMode = 'MANGO'
+
+type RoutingMode =
+ | MultiRoutingMode
+ | JupiterRoutingMode
+ | RaydiumRoutingMode
+ | MangoRoutingMode
+
+type useQuoteRoutesPropTypes = {
+ inputMint: string | undefined
+ outputMint: string | undefined
+ amount: string
+ slippage: number
+ swapMode: SwapModes
+ wallet: string | undefined
+ mangoAccount: MangoAccount | undefined
+ routingMode: RoutingMode
+ inDecimals: number | undefined
+ outDecimals: number | undefined
+ enabled?: () => boolean
+}
+
+function isMultiRoutingMode(value: RoutingMode): value is MultiRoutingMode {
+ return ['ALL', 'ALL_AND_JUPITER_DIRECT'].includes(value)
+}
+
+function isRaydiumRoutingMode(value: RoutingMode): value is RaydiumRoutingMode {
+ return value === 'RAYDIUM'
+}
+
+const deserializeJupiterIxAndAlt = async (
+ connection: Connection,
+ swapTransaction: string,
+): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
+ const parsedSwapTransaction = VersionedTransaction.deserialize(
+ Buffer.from(swapTransaction, 'base64'),
+ )
+ const message = parsedSwapTransaction.message
+ // const lookups = message.addressTableLookups
+ const addressLookupTablesResponses = await Promise.all(
+ message.addressTableLookups.map((alt) =>
+ connection.getAddressLookupTable(alt.accountKey),
+ ),
+ )
+ const addressLookupTables: AddressLookupTableAccount[] =
+ addressLookupTablesResponses
+ .map((alt) => alt.value)
+ .filter((x): x is AddressLookupTableAccount => x !== null)
+
+ const decompiledMessage = TransactionMessage.decompile(message, {
+ addressLookupTableAccounts: addressLookupTables,
+ })
+
+ return [decompiledMessage.instructions, addressLookupTables]
+}
+
+const fetchJupiterTransaction = async (
+ connection: Connection,
+ selectedRoute: JupiterV6RouteInfo,
+ userPublicKey: PublicKey,
+ slippage: number,
+ inputMint: PublicKey,
+ outputMint: PublicKey,
+ origin?: 'mango' | 'jupiter' | 'raydium',
+): Promise<[TransactionInstruction[], AddressLookupTableAccount[]]> => {
+ // docs https://station.jup.ag/api-v6/post-swap
+ const transactions = await (
+ await fetch(
+ `${
+ origin === 'mango' ? MANGO_ROUTER_API_URL : JUPITER_V6_QUOTE_API_MAINNET
+ }/swap`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ // response from /quote api
+ quoteResponse: selectedRoute,
+ // user public key to be used for the swap
+ userPublicKey,
+ slippageBps: Math.ceil(slippage * 100),
+ wrapAndUnwrapSol: false,
+ }),
+ },
+ )
+ ).json()
+
+ const { swapTransaction } = transactions
+
+ const [ixs, alts] = await deserializeJupiterIxAndAlt(
+ connection,
+ swapTransaction,
+ )
+
+ const isSetupIx = (pk: PublicKey): boolean =>
+ pk.toString() === 'ComputeBudget111111111111111111111111111111' ||
+ pk.toString() === 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
+
+ const isDuplicateAta = (ix: TransactionInstruction): boolean => {
+ return (
+ ix.programId.toString() ===
+ 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL' &&
+ (ix.keys[3].pubkey.toString() === inputMint.toString() ||
+ ix.keys[3].pubkey.toString() === outputMint.toString())
+ )
+ }
+
+ //remove ATA and compute setup from swaps in margin trades
+ const filtered_jup_ixs = ixs
+ .filter((ix) => !isSetupIx(ix.programId))
+ .filter((ix) => !isDuplicateAta(ix))
+
+ return [filtered_jup_ixs, alts]
+}
+
+const fetchJupiterRoute = async (
+ inputMint: string | undefined,
+ outputMint: string | undefined,
+ amount = 0,
+ slippage = 50,
+ swapMode: SwapModes = 'ExactIn',
+ onlyDirectRoutes = true,
+ maxAccounts = 64,
+ connection: Connection,
+ wallet: string,
+ sendAnalytics?: (data: object, tag: string) => Promise
,
+) => {
+ return new Promise<{ bestRoute: JupiterV6RouteInfo }>(
+ // eslint-disable-next-line no-async-promise-executor
+ async (resolve, reject) => {
+ try {
+ if (!inputMint || !outputMint) return
+ const paramObj: {
+ inputMint: string
+ outputMint: string
+ amount: string
+ slippageBps: string
+ swapMode: string
+ onlyDirectRoutes: string
+ maxAccounts?: string
+ } = {
+ inputMint: inputMint.toString(),
+ outputMint: outputMint.toString(),
+ amount: amount.toString(),
+ slippageBps: Math.ceil(slippage * 100).toString(),
+ swapMode,
+ onlyDirectRoutes: `${onlyDirectRoutes}`,
+ }
+ //exact out is not supporting max account
+ if (swapMode === 'ExactIn') {
+ paramObj.maxAccounts = maxAccounts.toString()
+ }
+ const paramsString = new URLSearchParams(paramObj).toString()
+ const response = await fetch(
+ `${JUPITER_V6_QUOTE_API_MAINNET}/quote?${paramsString}`,
+ )
+ if (sendAnalytics) {
+ sendAnalytics(
+ {
+ url: `${JUPITER_V6_QUOTE_API_MAINNET}/quote?${paramsString}`,
+ },
+ 'fetchJupiterRoute',
+ )
+ }
+
+ const res: JupiterV6RouteInfo = await response.json()
+ if (res.error) {
+ throw res.error
+ }
+ const [ixes] = await fetchJupiterTransaction(
+ connection,
+ res,
+ new PublicKey(wallet),
+ slippage,
+ new PublicKey(inputMint),
+ new PublicKey(outputMint),
+ 'jupiter',
+ )
+
+ if (
+ maxAccounts !== 64 &&
+ [...ixes.flatMap((x) => x.keys.flatMap((k) => k.pubkey))].length >
+ maxAccounts
+ ) {
+ throw 'Max accounts exceeded'
+ }
+ resolve({
+ bestRoute: res,
+ })
+ } catch (e) {
+ if (sendAnalytics) {
+ sendAnalytics(
+ {
+ error: `${e}`,
+ },
+ 'fetchJupiterRouteError',
+ )
+ }
+ console.log('jupiter route error:', e)
+ reject(e)
+ }
+ },
+ )
+}
+
+const fetchRaydiumRoute = async (
+ inputMint: string | undefined,
+ outputMint: string | undefined,
+ amount = 0,
+ slippage = 50,
+ connection: Connection,
+ wallet: string,
+ isInWalletSwap: boolean,
+ sendAnalytics?: (data: object, tag: string) => Promise,
+) => {
+ return new Promise<{ bestRoute: JupiterV6RouteInfo }>(
+ // eslint-disable-next-line no-async-promise-executor
+ async (resolve, reject) => {
+ try {
+ if (sendAnalytics) {
+ sendAnalytics(
+ {
+ inputMint,
+ outputMint,
+ amount,
+ slippage,
+ },
+ 'fetchRaydiumRoute',
+ )
+ }
+
+ if (!inputMint || !outputMint) return
+
+ const poolKeys = await findRaydiumPoolInfo(
+ connection,
+ outputMint,
+ inputMint,
+ )
+
+ if (poolKeys) {
+ const resp = await getSwapTransaction(
+ connection,
+ outputMint,
+ amount,
+ poolKeys!,
+ slippage,
+ new PublicKey(wallet),
+ isInWalletSwap,
+ )
+ resolve(resp as unknown as { bestRoute: JupiterV6RouteInfo })
+ } else {
+ throw 'No route found'
+ }
+ } catch (e) {
+ if (sendAnalytics) {
+ sendAnalytics(
+ {
+ error: `${e}`,
+ },
+ 'raydiumRouteError',
+ )
+ }
+ console.log('raydium route error:', e)
+ reject(e)
+ }
+ },
+ )
+}
+
+const fetchMangoRoute = async (
+ inputMint = 'So11111111111111111111111111111111111111112',
+ outputMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
+ amount = 0,
+ slippage = 50,
+ swapMode = 'ExactIn',
+ sendAnalytics?: (data: object, tag: string) => Promise,
+) => {
+ return new Promise<{ bestRoute: JupiterV6RouteInfo }>(
+ // eslint-disable-next-line no-async-promise-executor
+ async (resolve, reject) => {
+ const timeout = setTimeout(() => {
+ reject('Request timed out')
+ }, 5000)
+
+ try {
+ const paramsString = new URLSearchParams({
+ inputMint: inputMint.toString(),
+ outputMint: outputMint.toString(),
+ amount: amount.toString(),
+ slippageBps: Math.ceil(slippage * 100).toString(),
+ mode: swapMode,
+ }).toString()
+
+ const response = await fetch(
+ `${MANGO_ROUTER_API_URL}/quote?${paramsString}`,
+ )
+ clearTimeout(timeout)
+ if (sendAnalytics) {
+ sendAnalytics(
+ {
+ url: `${MANGO_ROUTER_API_URL}/quote?${paramsString}`,
+ },
+ 'fetchMangoRoute',
+ )
+ }
+
+ if (response.status === 500) {
+ throw 'No route found'
+ }
+
+ const res = await response.json()
+
+ if (res.outAmount) {
+ resolve({
+ bestRoute: { ...res, origin: 'mango' },
+ })
+ } else {
+ reject('No route found')
+ }
+ } catch (e) {
+ clearTimeout(timeout)
+ if (sendAnalytics) {
+ sendAnalytics(
+ {
+ error: `${e}`,
+ },
+ 'mangoRouteError',
+ )
+ }
+ console.log('mango router error:', e)
+ reject(e)
+ }
+ },
+ )
+}
+
+export async function handleGetRoutes(
+ inputMint: string | undefined,
+ outputMint: string | undefined,
+ amount: number,
+ slippage: number,
+ swapMode: SwapModes,
+ wallet: string | undefined,
+ mangoAccount: MangoAccount | undefined,
+ routingMode: MultiRoutingMode | RaydiumRoutingMode,
+ connection: Connection,
+ sendAnalytics: ((data: object, tag: string) => Promise) | undefined,
+ inputTokenDecimals: number,
+): Promise<{ bestRoute: JupiterV6RouteInfo }>
+
+export async function handleGetRoutes(
+ inputMint: string | undefined,
+ outputMint: string | undefined,
+ amount: number,
+ slippage: number,
+ swapMode: SwapModes,
+ wallet: string | undefined,
+ mangoAccount: MangoAccount | undefined,
+ routingMode: JupiterRoutingMode | MangoRoutingMode,
+ connection: Connection,
+ sendAnalytics: ((data: object, tag: string) => Promise) | undefined,
+): Promise<{ bestRoute: JupiterV6RouteInfo }>
+
+export async function handleGetRoutes(
+ inputMint: string | undefined,
+ outputMint: string | undefined,
+ amount: number,
+ slippage: number,
+ swapMode: 'ExactIn',
+ wallet: string | undefined,
+ mangoAccount: MangoAccount | undefined,
+ routingMode: RaydiumRoutingMode,
+ connection: Connection,
+ sendAnalytics: ((data: object, tag: string) => Promise) | undefined,
+ inputTokenDecimals: number,
+): Promise<{ bestRoute: JupiterV6RouteInfo }>
+
+export async function handleGetRoutes(
+ inputMint: string | undefined,
+ outputMint: string | undefined,
+ amount = 0,
+ slippage = 50,
+ swapMode: SwapModes,
+ wallet: string | undefined,
+ mangoAccount: MangoAccount | undefined,
+ routingMode: RoutingMode = 'ALL',
+ connection: Connection,
+ sendAnalytics: ((data: object, tag: string) => Promise) | undefined,
+ inputTokenDecimals?: number,
+) {
+ try {
+ if (sendAnalytics) {
+ sendAnalytics(
+ {
+ inputMint,
+ outputMint,
+ amount,
+ slippage,
+ swapMode,
+ wallet,
+ routingMode,
+ },
+ 'handleGetRoutes',
+ )
+ }
+
+ wallet ||= PublicKey.default.toBase58()
+
+ let maxAccounts: number
+ if (!mangoAccount) {
+ maxAccounts = 64
+ } else {
+ // TODO: replace with client method
+ const totalSlots =
+ 2 * mangoAccount.tokensActive().length +
+ mangoAccount.serum3Active().length +
+ 2 * mangoAccount.perpActive().length
+ maxAccounts = 54 - totalSlots
+ }
+
+ const routes = []
+
+ if (
+ swapMode === 'ExactIn' &&
+ (isMultiRoutingMode(routingMode) || isRaydiumRoutingMode(routingMode))
+ ) {
+ const raydiumRoute = fetchRaydiumRoute(
+ inputMint,
+ outputMint,
+ toUiDecimals(amount, inputTokenDecimals!),
+ slippage,
+ connection,
+ wallet,
+ !mangoAccount,
+ sendAnalytics,
+ )
+ routes.push(raydiumRoute)
+ }
+
+ if (
+ routingMode === 'ALL_AND_JUPITER_DIRECT' ||
+ routingMode === 'JUPITER_DIRECT'
+ ) {
+ const jupiterDirectRoute = fetchJupiterRoute(
+ inputMint,
+ outputMint,
+ amount,
+ slippage,
+ swapMode,
+ true,
+ maxAccounts,
+ connection,
+ wallet,
+ sendAnalytics,
+ )
+ routes.push(jupiterDirectRoute)
+ }
+
+ if (isMultiRoutingMode(routingMode) || routingMode === 'JUPITER') {
+ const jupiterRoute = fetchJupiterRoute(
+ inputMint,
+ outputMint,
+ amount,
+ slippage,
+ swapMode,
+ false,
+ maxAccounts,
+ connection,
+ wallet,
+ sendAnalytics,
+ )
+ routes.push(jupiterRoute)
+ }
+
+ if (isMultiRoutingMode(routingMode) || routingMode === 'MANGO') {
+ const mangoRoute = fetchMangoRoute(
+ inputMint,
+ outputMint,
+ amount,
+ slippage,
+ swapMode,
+ sendAnalytics,
+ )
+ routes.push(mangoRoute)
+ }
+
+ const results = await Promise.allSettled(routes)
+
+ const responses = results
+ .filter((x) => x.status === 'fulfilled' && x.value?.bestRoute !== null)
+ .map((x) => (x as any).value)
+ if (!responses.length) {
+ throw 'No route found'
+ }
+ const sortedByBiggestOutAmount = (
+ responses as {
+ bestRoute: JupiterV6RouteInfo
+ }[]
+ ).sort((a, b) =>
+ swapMode === 'ExactIn'
+ ? Number(b.bestRoute.outAmount) - Number(a.bestRoute.outAmount)
+ : Number(a.bestRoute.inAmount) - Number(b.bestRoute.inAmount),
+ )
+ return {
+ bestRoute: sortedByBiggestOutAmount.length
+ ? sortedByBiggestOutAmount[0]?.bestRoute
+ : null,
+ }
+ } catch (e) {
+ if (sendAnalytics) {
+ sendAnalytics(
+ {
+ error: `${e}`,
+ },
+ 'noRouteFoundError',
+ )
+ }
+ return {
+ bestRoute: null,
+ }
+ }
+}
+
+const useQuoteRoutes = ({
+ inputMint,
+ outputMint,
+ amount,
+ slippage,
+ swapMode,
+ wallet,
+ mangoAccount,
+ routingMode = 'ALL',
+ inDecimals,
+ outDecimals,
+ enabled,
+}: useQuoteRoutesPropTypes) => {
+ const connection = mangoStore((s) => s.connection)
+ const { sendAnalytics } = useAnalytics()
+
+ const decimals = useMemo(() => {
+ return swapMode === 'ExactIn' ? inDecimals || 6 : outDecimals || 6
+ }, [swapMode, inDecimals, outDecimals])
+
+ const nativeAmount = useMemo(() => {
+ return amount && !Number.isNaN(+amount)
+ ? new Decimal(amount).mul(10 ** decimals)
+ : new Decimal(0)
+ }, [amount, decimals])
+
+ const res = useQuery<{ bestRoute: JupiterV6RouteInfo | null }, Error>(
+ [
+ [
+ 'swap-routes',
+ nativeAmount.toString(),
+ inputMint,
+ outputMint,
+ swapMode,
+ wallet,
+ routingMode,
+ ],
+ inputMint,
+ outputMint,
+ amount,
+ slippage,
+ swapMode,
+ wallet,
+ routingMode,
+ ],
+ async () => {
+ if (
+ isMultiRoutingMode(routingMode) ||
+ isRaydiumRoutingMode(routingMode)
+ ) {
+ return handleGetRoutes(
+ inputMint,
+ outputMint,
+ nativeAmount.toNumber(),
+ slippage,
+ swapMode,
+ wallet,
+ mangoAccount,
+ routingMode,
+ connection,
+ sendAnalytics,
+ decimals,
+ )
+ } else {
+ return handleGetRoutes(
+ inputMint,
+ outputMint,
+ nativeAmount.toNumber(),
+ slippage,
+ swapMode,
+ wallet,
+ mangoAccount,
+ routingMode,
+ connection,
+ sendAnalytics,
+ )
+ }
+ },
+ {
+ cacheTime: 1000 * 60,
+ staleTime: 1000 * 3,
+ enabled: enabled
+ ? enabled()
+ : nativeAmount.toNumber() && inputMint && outputMint
+ ? true
+ : false,
+ refetchInterval: 20000,
+ retry: 3,
+ },
+ )
+
+ return amount
+ ? {
+ ...(res.data ?? {
+ routes: [],
+ bestRoute: undefined,
+ }),
+ isFetching: res.isFetching,
+ isLoading: res.isLoading,
+ isInitialLoading: res.isInitialLoading,
+ refetch: res.refetch,
+ }
+ : {
+ routes: [],
+ bestRoute: undefined,
+ isFetching: false,
+ isLoading: false,
+ isInitialLoading: false,
+ refetch: undefined,
+ }
+}
+
+export default useQuoteRoutes
diff --git a/hooks/useStakeRates.ts b/hooks/useStakeRates.ts
index 7375f2d..0d3e5fa 100644
--- a/hooks/useStakeRates.ts
+++ b/hooks/useStakeRates.ts
@@ -95,7 +95,7 @@ export default function useStakeRates() {
cacheTime: 1000 * 60 * 5,
staleTime: 1000 * 60,
retry: 3,
- refetchOnWindowFocus: true,
+ refetchOnWindowFocus: false,
})
return {
diff --git a/package.json b/package.json
index 2548cea..e0cb0e7 100644
--- a/package.json
+++ b/package.json
@@ -38,11 +38,13 @@
"@blockworks-foundation/mango-feeds": "0.1.7",
"@blockworks-foundation/mango-v4": "0.31.3",
"@blockworks-foundation/mango-v4-settings": "0.14.24",
+ "@blockworks-foundation/mangolana": "0.0.17",
"@glitchful-dev/sol-apy-sdk": "3.0.2",
"@headlessui/react": "1.6.6",
"@heroicons/react": "2.0.10",
- "@project-serum/anchor": "0.25.0",
+ "@project-serum/anchor": "0.26.0",
"@pythnetwork/client": "2.15.0",
+ "@raydium-io/raydium-sdk": "1.3.1-beta.57",
"@solana/spl-governance": "0.3.27",
"@solana/spl-token": "0.3.7",
"@solana/wallet-adapter-base": "0.9.23",
@@ -90,7 +92,7 @@
"@types/react-grid-layout": "1.3.2",
"@types/react-window": "1.8.5",
"@types/recharts": "1.8.24",
- "@typescript-eslint/eslint-plugin": "5.43.0",
+ "@typescript-eslint/eslint-plugin": "7.15.0",
"autoprefixer": "10.4.13",
"eslint": "8.13.0",
"eslint-config-next": "13.4.17",
@@ -104,7 +106,7 @@
"prettier": "3.0.2",
"prettier-plugin-tailwindcss": "0.5.3",
"tailwindcss": "3.3.3",
- "typescript": "4.9.4"
+ "typescript": "5.5.3"
},
"resolutions": {
"@coral-xyz/anchor": "^0.27.0",
diff --git a/types/jupiter.ts b/types/jupiter.ts
index 88bb9b6..2086d03 100644
--- a/types/jupiter.ts
+++ b/types/jupiter.ts
@@ -149,3 +149,38 @@ export type Token = {
}
tags: string[]
}
+
+export interface JupiterV6RoutePlan {
+ swapInfo: {
+ ammKey: string
+ label?: string
+ inputMint: string
+ outputMint: string
+ inAmount: number
+ outAmount: number
+ feeAmount: number
+ feeMint: string
+ }
+ percent: number
+}
+
+export interface JupiterV6RouteInfo {
+ inputMint: string
+ inAmount: number
+ outputMint: string
+ outAmount: number
+ otherAmountThreshold: number
+ swapMode: SwapMode
+ slippageBps: number
+ platformFee?: {
+ amount: string
+ feeBps: number
+ }
+ priceImpactPct: number
+ routePlan: JupiterV6RoutePlan[] | undefined
+ contextSlot?: number
+ timeTaken?: number
+ error?: string
+ instructions?: TransactionInstruction[]
+ origin?: 'jupiter' | 'mango' | 'raydium'
+}
diff --git a/utils/constants.ts b/utils/constants.ts
index d2389dc..1a29beb 100644
--- a/utils/constants.ts
+++ b/utils/constants.ts
@@ -284,6 +284,8 @@ export const LAST_WALLET_NAME = 'lastWalletName'
export const PRIVACY_MODE = 'privacy-mode-0.1'
+export const JUPITER_V6_QUOTE_API_MAINNET = 'https://quote-api.jup.ag/v6'
+
// Unused
export const PROFILE_CATEGORIES = [
'borrower',
diff --git a/utils/swap/raydium.ts b/utils/swap/raydium.ts
new file mode 100644
index 0000000..06f7671
--- /dev/null
+++ b/utils/swap/raydium.ts
@@ -0,0 +1,324 @@
+import {
+ LIQUIDITY_STATE_LAYOUT_V4,
+ LiquidityPoolKeys,
+ Liquidity,
+ Token,
+ TokenAmount,
+ Percent,
+ MARKET_STATE_LAYOUT_V3,
+ Market,
+ CurrencyAmount,
+ Price,
+} from '@raydium-io/raydium-sdk'
+import { TOKEN_PROGRAM_ID } from '@solana/spl-governance'
+import { getAssociatedTokenAddressSync } from '@solana/spl-token'
+import {
+ Connection,
+ GetProgramAccountsResponse,
+ PublicKey,
+ TransactionInstruction,
+} from '@solana/web3.js'
+import BN from 'bn.js'
+
+const RAYDIUM_V4_PROGRAM_ID = '675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8'
+
+const _getProgramAccounts = (
+ connection: Connection,
+ baseMint: string,
+ quoteMint: string,
+): Promise => {
+ const layout = LIQUIDITY_STATE_LAYOUT_V4
+
+ return connection.getProgramAccounts(new PublicKey(RAYDIUM_V4_PROGRAM_ID), {
+ filters: [
+ { dataSize: layout.span },
+ {
+ memcmp: {
+ offset: layout.offsetOf('baseMint'),
+ bytes: new PublicKey(baseMint).toBase58(),
+ },
+ },
+ {
+ memcmp: {
+ offset: layout.offsetOf('quoteMint'),
+ bytes: new PublicKey(quoteMint).toBase58(),
+ },
+ },
+ ],
+ })
+}
+
+const getProgramAccounts = async (
+ connection: Connection,
+ baseMint: string,
+ quoteMint: string,
+) => {
+ const response = await Promise.all([
+ _getProgramAccounts(connection, baseMint, quoteMint),
+ _getProgramAccounts(connection, quoteMint, baseMint),
+ ])
+
+ return response.filter((r) => r.length > 0).flatMap((x) => x)
+}
+
+export const findRaydiumPoolInfo = async (
+ connection: Connection,
+ baseMint: string,
+ quoteMint: string,
+): Promise => {
+ const layout = LIQUIDITY_STATE_LAYOUT_V4
+
+ const programData = await getProgramAccounts(connection, baseMint, quoteMint)
+
+ const collectedPoolResults = programData
+ .map((info) => ({
+ id: new PublicKey(info.pubkey),
+ version: 4,
+ programId: new PublicKey(RAYDIUM_V4_PROGRAM_ID),
+ ...layout.decode(info.account.data),
+ }))
+ .flat()
+
+ const pools = await Promise.all([
+ fetch(`https://api.dexscreener.com/latest/dex/search?q=${baseMint}`),
+ fetch(`https://api.dexscreener.com/latest/dex/search?q=${quoteMint}`),
+ ])
+ const resp = await Promise.all([...pools.map((x) => x.json())])
+
+ const bestDexScannerPoolId = resp
+ .flatMap((x) => x.pairs)
+ .find(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ (x: any) =>
+ x.dexId === 'raydium' &&
+ ((x.baseToken.address === baseMint &&
+ x.quoteToken.address === quoteMint) ||
+ (x.baseToken.address === quoteMint &&
+ x.quoteToken.address === baseMint)),
+ )?.pairAddress
+
+ const pool = collectedPoolResults.find(
+ (x) => x.id.toBase58() === bestDexScannerPoolId,
+ )
+
+ if (!pool) return undefined
+
+ const market = await connection
+ .getAccountInfo(pool.marketId)
+ .then((item) => ({
+ programId: item!.owner,
+ ...MARKET_STATE_LAYOUT_V3.decode(item!.data),
+ }))
+
+ const authority = Liquidity.getAssociatedAuthority({
+ programId: new PublicKey(RAYDIUM_V4_PROGRAM_ID),
+ }).publicKey
+
+ const marketProgramId = market.programId
+
+ const poolKeys = {
+ id: pool.id,
+ baseMint: pool.baseMint,
+ quoteMint: pool.quoteMint,
+ lpMint: pool.lpMint,
+ baseDecimals: Number.parseInt(pool.baseDecimal.toString()),
+ quoteDecimals: Number.parseInt(pool.quoteDecimal.toString()),
+ lpDecimals: Number.parseInt(pool.baseDecimal.toString()),
+ version: pool.version,
+ programId: pool.programId,
+ openOrders: pool.openOrders,
+ targetOrders: pool.targetOrders,
+ baseVault: pool.baseVault,
+ quoteVault: pool.quoteVault,
+ marketVersion: 3,
+ authority: authority,
+ marketProgramId,
+ marketId: market.ownAddress,
+ marketAuthority: Market.getAssociatedAuthority({
+ programId: marketProgramId,
+ marketId: market.ownAddress,
+ }).publicKey,
+ marketBaseVault: market.baseVault,
+ marketQuoteVault: market.quoteVault,
+ marketBids: market.bids,
+ marketAsks: market.asks,
+ marketEventQueue: market.eventQueue,
+ withdrawQueue: pool.withdrawQueue,
+ lpVault: pool.lpVault,
+ lookupTableAccount: PublicKey.default,
+ } as LiquidityPoolKeys
+
+ return poolKeys
+}
+
+const calcAmountOut = async (
+ connection: Connection,
+ poolKeys: LiquidityPoolKeys,
+ rawAmountIn: number,
+ slippage = 5,
+ swapInDirection: boolean,
+) => {
+ const poolInfo = await Liquidity.fetchInfo({
+ connection: connection,
+ poolKeys,
+ })
+
+ let currencyInMint = poolKeys.baseMint
+ let currencyInDecimals = poolInfo.baseDecimals
+ let currencyOutMint = poolKeys.quoteMint
+ let currencyOutDecimals = poolInfo.quoteDecimals
+
+ if (!swapInDirection) {
+ currencyInMint = poolKeys.quoteMint
+ currencyInDecimals = poolInfo.quoteDecimals
+ currencyOutMint = poolKeys.baseMint
+ currencyOutDecimals = poolInfo.baseDecimals
+ }
+
+ const currencyIn = new Token(
+ TOKEN_PROGRAM_ID,
+ currencyInMint,
+ currencyInDecimals,
+ )
+ const amountIn = new TokenAmount(
+ currencyIn,
+ rawAmountIn.toFixed(currencyInDecimals),
+ false,
+ )
+ const currencyOut = new Token(
+ TOKEN_PROGRAM_ID,
+ currencyOutMint,
+ currencyOutDecimals,
+ )
+ const slippageX = new Percent(Math.ceil(slippage * 10), 1000)
+
+ const {
+ amountOut,
+ minAmountOut,
+ currentPrice,
+ executionPrice,
+ priceImpact,
+ fee,
+ } = Liquidity.computeAmountOut({
+ poolKeys,
+ poolInfo,
+ amountIn,
+ currencyOut,
+ slippage: slippageX,
+ })
+
+ return {
+ amountIn: amountIn,
+ amountOut: amountOut,
+ inAmount: amountIn.raw.toNumber(),
+ outAmount: amountOut.raw.toNumber(),
+ otherAmountThreshold: minAmountOut.raw.toNumber(),
+ minAmountOut: minAmountOut,
+ currentPrice,
+ executionPrice,
+ priceImpactPct: Number(priceImpact.toSignificant()) / 100,
+ fee,
+ inputMint: poolKeys.quoteMint.toBase58(),
+ outputMint: poolKeys.baseMint.toBase58(),
+ routePlan: [
+ {
+ swapInfo: {
+ ammKey: poolKeys.id.toBase58(),
+ label: 'Raydium',
+ inputMint: poolKeys.quoteMint.toBase58(),
+ outputMint: poolKeys.baseMint.toBase58(),
+ inAmount: amountIn.raw.toNumber(),
+ outAmount: amountOut.raw.toNumber(),
+ feeAmount: 0,
+ feeMint: poolKeys.lpMint.toBase58(),
+ },
+ percent: 100,
+ },
+ ],
+ }
+}
+
+export const getSwapTransaction = async (
+ connection: Connection,
+ toToken: string,
+ amount: number,
+ poolKeys: LiquidityPoolKeys,
+ slippage = 5,
+ wallet: PublicKey,
+ mangoAccountSwap: boolean,
+): Promise<{
+ bestRoute: {
+ amountIn: TokenAmount
+ amountOut: TokenAmount | CurrencyAmount
+ inAmount: number
+ outAmount: number
+ otherAmountThreshold: number
+ currentPrice: Price
+ executionPrice: Price | null
+ priceImpactPct: number
+ fee: CurrencyAmount
+ instructions: TransactionInstruction[]
+ }
+}> => {
+ const directionIn = poolKeys.quoteMint.toString() == toToken
+
+ const bestRoute = await calcAmountOut(
+ connection,
+ poolKeys,
+ amount,
+ slippage,
+ directionIn,
+ )
+
+ const tokenInAta = getAssociatedTokenAddressSync(
+ new PublicKey(directionIn ? bestRoute.outputMint : bestRoute.inputMint),
+ wallet,
+ )
+ const tokenOutAta = getAssociatedTokenAddressSync(
+ new PublicKey(directionIn ? bestRoute.inputMint : bestRoute.outputMint),
+ wallet,
+ )
+ const swapTransaction = Liquidity.makeSwapInstruction({
+ poolKeys: {
+ ...poolKeys,
+ },
+ userKeys: {
+ tokenAccountIn: tokenInAta,
+ tokenAccountOut: tokenOutAta,
+ owner: wallet,
+ },
+ amountIn: bestRoute.amountIn.raw,
+ amountOut: bestRoute.minAmountOut.raw.sub(bestRoute.fee?.raw ?? new BN(0)),
+ fixedSide: !directionIn ? 'in' : 'out',
+ })
+
+ const instructions =
+ swapTransaction.innerTransaction.instructions.filter(Boolean)
+
+ const filtered_instructions = mangoAccountSwap
+ ? instructions
+ .filter((ix) => !isSetupIx(ix.programId))
+ .filter(
+ (ix) => !isDuplicateAta(ix, poolKeys.baseMint, poolKeys.quoteMint),
+ )
+ : instructions
+
+ return { bestRoute: { ...bestRoute, instructions: filtered_instructions } }
+}
+
+const isSetupIx = (pk: PublicKey): boolean =>
+ pk.toString() === 'ComputeBudget111111111111111111111111111111' ||
+ pk.toString() === 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
+
+const isDuplicateAta = (
+ ix: TransactionInstruction,
+ inputMint: PublicKey,
+ outputMint: PublicKey,
+): boolean => {
+ return (
+ ix.programId.toString() ===
+ 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL' &&
+ (ix.keys[3].pubkey.toString() === inputMint.toString() ||
+ ix.keys[3].pubkey.toString() === outputMint.toString())
+ )
+}
diff --git a/utils/transactions.ts b/utils/transactions.ts
index f707880..413a450 100644
--- a/utils/transactions.ts
+++ b/utils/transactions.ts
@@ -3,8 +3,10 @@ import {
Bank,
FlashLoanType,
Group,
+ MANGO_ROUTER_API_URL,
MangoAccount,
MangoClient,
+ MangoError,
MangoSignatureStatus,
RouteInfo,
TokenIndex,
@@ -15,6 +17,7 @@ import {
toNative,
toNativeI80F48,
toUiDecimals,
+ tryStringify,
} from '@blockworks-foundation/mango-v4'
import { TOKEN_PROGRAM_ID } from '@solana/spl-governance'
import {
@@ -28,16 +31,22 @@ import {
Connection,
Keypair,
PublicKey,
+ RpcResponseAndContext,
SYSVAR_INSTRUCTIONS_PUBKEY,
+ SignatureResult,
SystemProgram,
+ Transaction,
TransactionInstruction,
TransactionMessage,
VersionedTransaction,
} from '@solana/web3.js'
import { floorToDecimal } from './numbers'
-import { BOOST_ACCOUNT_PREFIX } from './constants'
+import { BOOST_ACCOUNT_PREFIX, JUPITER_V6_QUOTE_API_MAINNET } from './constants'
import { notify } from './notifications'
import { getStakableTokensDataForMint } from './tokens'
+import { JupiterV6RouteInfo } from 'types/jupiter'
+import { WalletContextState } from '@solana/wallet-adapter-react'
+import { awaitTransactionSignatureConfirmation } from '@blockworks-foundation/mangolana/lib/transactions'
export const withdrawAndClose = async (
client: MangoClient,
@@ -962,3 +971,155 @@ export const getNextAccountNumber = (accounts: MangoAccount[]): number => {
}
return 0
}
+
+export const walletSwap = async (
+ selectedRoute: JupiterV6RouteInfo,
+ connection: Connection,
+ slippage: number,
+ wallet: WalletContextState,
+ client: MangoClient,
+) => {
+ const vtx = await fetchJupiterWalletSwapTransaction(
+ selectedRoute,
+ wallet!.publicKey!,
+ slippage,
+ selectedRoute.origin,
+ )
+
+ const latestBlockhash = await connection.getLatestBlockhash()
+ const sign = wallet.signTransaction!
+ const signed = await sign(vtx)
+
+ const txid = await sendTxAndConfirm(
+ client.opts.multipleConnections,
+ connection,
+ signed,
+ latestBlockhash,
+ )
+ return { txid, outAmount: selectedRoute.outAmount }
+}
+
+/** Given a Jupiter route, fetch the transaction for the user to sign.
+ **This function should ONLY be used for wallet swaps* */
+export const fetchJupiterWalletSwapTransaction = async (
+ selectedRoute: JupiterV6RouteInfo,
+ userPublicKey: PublicKey,
+ slippage: number,
+ origin?: 'mango' | 'jupiter' | 'raydium',
+): Promise => {
+ // docs https://station.jup.ag/api-v6/post-swap
+ const params: {
+ quoteResponse: JupiterV6RouteInfo
+ userPublicKey: PublicKey
+ slippageBps: number
+ autoCreateOutAta?: boolean
+ wrapAndUnwrapSol?: boolean
+ } = {
+ // response from /quote api
+ quoteResponse: selectedRoute,
+ // user public key to be used for the swap
+ userPublicKey,
+ slippageBps: Math.ceil(slippage * 100),
+ }
+
+ if (origin === 'mango') {
+ params.autoCreateOutAta = true
+ params.wrapAndUnwrapSol = true
+ }
+
+ const transactions = await (
+ await fetch(
+ `${
+ origin === 'mango' ? MANGO_ROUTER_API_URL : JUPITER_V6_QUOTE_API_MAINNET
+ }/swap`,
+ {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(params),
+ },
+ )
+ ).json()
+
+ const { swapTransaction } = transactions
+ const parsedSwapTransaction = VersionedTransaction.deserialize(
+ Buffer.from(swapTransaction, 'base64'),
+ )
+ return parsedSwapTransaction
+}
+
+export const sendTxAndConfirm = async (
+ multipleConnections: Connection[] = [],
+ connection: Connection,
+ tx: Transaction | VersionedTransaction,
+ latestBlockhash: {
+ lastValidBlockHeight: number
+ blockhash: string
+ },
+) => {
+ let signature = ''
+ const abortController = new AbortController()
+ try {
+ const allConnections = [connection, ...multipleConnections]
+ const rawTransaction = tx.serialize()
+ signature = await Promise.any(
+ allConnections.map((c) => {
+ return c.sendRawTransaction(rawTransaction, {
+ skipPreflight: true,
+ })
+ }),
+ )
+ await Promise.any(
+ allConnections.map((c) =>
+ awaitTransactionSignatureConfirmation({
+ txid: signature,
+ confirmLevel: 'processed',
+ connection: c,
+ timeoutStrategy: {
+ block: latestBlockhash,
+ },
+ abortSignal: abortController.signal,
+ }),
+ ),
+ )
+ abortController.abort()
+ return signature
+ } catch (e) {
+ abortController.abort()
+ if (e instanceof AggregateError) {
+ for (const individualError of e.errors) {
+ const stringifiedError = tryStringify(individualError)
+ throw new MangoError({
+ txid: signature,
+ message: `${
+ stringifiedError
+ ? stringifiedError
+ : individualError
+ ? individualError
+ : 'Unknown error'
+ }`,
+ })
+ }
+ }
+ if (isErrorWithSignatureResult(e)) {
+ const stringifiedError = tryStringify(e?.value?.err)
+ throw new MangoError({
+ txid: signature,
+ message: `${stringifiedError ? stringifiedError : e?.value?.err}`,
+ })
+ }
+ const stringifiedError = tryStringify(e)
+ throw new MangoError({
+ txid: signature,
+ message: `${stringifiedError ? stringifiedError : e}`,
+ })
+ }
+}
+
+function isErrorWithSignatureResult(
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ err: any,
+): err is RpcResponseAndContext {
+ return err && typeof err.value !== 'undefined'
+}
diff --git a/yarn.lock b/yarn.lock
index 28bdb8b..2af21ef 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -201,6 +201,14 @@
superstruct "^0.15.4"
toml "^3.0.0"
+"@coral-xyz/borsh@^0.26.0":
+ version "0.26.0"
+ resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.26.0.tgz#d054f64536d824634969e74138f9f7c52bbbc0d5"
+ integrity sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==
+ dependencies:
+ bn.js "^5.1.2"
+ buffer-layout "^1.2.0"
+
"@coral-xyz/borsh@^0.27.0":
version "0.27.0"
resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.27.0.tgz#700c647ea5262b1488957ac7fb4e8acf72c72b63"
@@ -217,6 +225,18 @@
bn.js "^5.1.2"
buffer-layout "^1.2.0"
+"@eslint-community/eslint-utils@^4.4.0":
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
+ integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
+ dependencies:
+ eslint-visitor-keys "^3.3.0"
+
+"@eslint-community/regexpp@^4.10.0":
+ version "4.11.0"
+ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.11.0.tgz#b0ffd0312b4a3fd2d6f77237e7248a5ad3a680ae"
+ integrity sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==
+
"@eslint/eslintrc@^1.2.1":
version "1.4.1"
resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.4.1.tgz#af58772019a2d271b7e2d4c23ff4ddcba3ccfb3e"
@@ -1375,18 +1395,18 @@
resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45"
integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==
-"@project-serum/anchor@0.25.0":
- version "0.25.0"
- resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.25.0.tgz#88ee4843336005cf5a64c80636ce626f0996f503"
- integrity sha512-E6A5Y/ijqpfMJ5psJvbw0kVTzLZFUcOFgs6eSM2M2iWE1lVRF18T6hWZVNl6zqZsoz98jgnNHtVGJMs+ds9A7A==
+"@project-serum/anchor@0.26.0":
+ version "0.26.0"
+ resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.26.0.tgz#99e15a3923a5d10514f8185b2d3909e5699d60d5"
+ integrity sha512-Nq+COIjE1135T7qfnOHEn7E0q39bQTgXLFk837/rgFe6Hkew9WML7eHsS+lSYD2p3OJaTiUOHTAq1lHy36oIqQ==
dependencies:
- "@project-serum/borsh" "^0.2.5"
- "@solana/web3.js" "^1.36.0"
+ "@coral-xyz/borsh" "^0.26.0"
+ "@solana/web3.js" "^1.68.0"
base64-js "^1.5.1"
bn.js "^5.1.2"
bs58 "^4.0.1"
buffer-layout "^1.2.2"
- camelcase "^5.3.1"
+ camelcase "^6.3.0"
cross-fetch "^3.1.5"
crypto-hash "^1.3.0"
eventemitter3 "^4.0.7"
@@ -1532,6 +1552,22 @@
"@coral-xyz/anchor" "^0.26.0"
buffer "^6.0.1"
+"@raydium-io/raydium-sdk@1.3.1-beta.57":
+ version "1.3.1-beta.57"
+ resolved "https://registry.yarnpkg.com/@raydium-io/raydium-sdk/-/raydium-sdk-1.3.1-beta.57.tgz#ca498a2b0c54454cc444e9a55d7ac690f9ba094a"
+ integrity sha512-oHp9/N4CUEUmxxupaYr+gbrgGiZci9QJ/UX146G2jd0tSkcXu046EV1SE2HSJRnQfMFiJUoxwZi7qzENWH6dDQ==
+ dependencies:
+ "@solana/buffer-layout" "^4.0.1"
+ "@solana/spl-token" "^0.3.9"
+ axios "^1.6.2"
+ big.js "^6.2.1"
+ bn.js "^5.2.1"
+ decimal.js "^10.4.3"
+ decimal.js-light "^2.5.1"
+ fecha "^4.2.3"
+ lodash "^4.17.21"
+ toformat "^2.0.0"
+
"@react-native-async-storage/async-storage@^1.17.7":
version "1.17.11"
resolved "https://registry.yarnpkg.com/@react-native-async-storage/async-storage/-/async-storage-1.17.11.tgz#7ec329c1b9f610e344602e806b04d7c928a2341d"
@@ -1617,6 +1653,66 @@
dependencies:
buffer "~6.0.3"
+"@solana/codecs-core@2.0.0-preview.2":
+ version "2.0.0-preview.2"
+ resolved "https://registry.yarnpkg.com/@solana/codecs-core/-/codecs-core-2.0.0-preview.2.tgz#689784d032fbc1fedbde40bb25d76cdcecf6553b"
+ integrity sha512-gLhCJXieSCrAU7acUJjbXl+IbGnqovvxQLlimztPoGgfLQ1wFYu+XJswrEVQqknZYK1pgxpxH3rZ+OKFs0ndQg==
+ dependencies:
+ "@solana/errors" "2.0.0-preview.2"
+
+"@solana/codecs-data-structures@2.0.0-preview.2":
+ version "2.0.0-preview.2"
+ resolved "https://registry.yarnpkg.com/@solana/codecs-data-structures/-/codecs-data-structures-2.0.0-preview.2.tgz#e82cb1b6d154fa636cd5c8953ff3f32959cc0370"
+ integrity sha512-Xf5vIfromOZo94Q8HbR04TbgTwzigqrKII0GjYr21K7rb3nba4hUW2ir8kguY7HWFBcjHGlU5x3MevKBOLp3Zg==
+ dependencies:
+ "@solana/codecs-core" "2.0.0-preview.2"
+ "@solana/codecs-numbers" "2.0.0-preview.2"
+ "@solana/errors" "2.0.0-preview.2"
+
+"@solana/codecs-numbers@2.0.0-preview.2":
+ version "2.0.0-preview.2"
+ resolved "https://registry.yarnpkg.com/@solana/codecs-numbers/-/codecs-numbers-2.0.0-preview.2.tgz#56995c27396cd8ee3bae8bd055363891b630bbd0"
+ integrity sha512-aLZnDTf43z4qOnpTcDsUVy1Ci9im1Md8thWipSWbE+WM9ojZAx528oAql+Cv8M8N+6ALKwgVRhPZkto6E59ARw==
+ dependencies:
+ "@solana/codecs-core" "2.0.0-preview.2"
+ "@solana/errors" "2.0.0-preview.2"
+
+"@solana/codecs-strings@2.0.0-preview.2":
+ version "2.0.0-preview.2"
+ resolved "https://registry.yarnpkg.com/@solana/codecs-strings/-/codecs-strings-2.0.0-preview.2.tgz#8bd01a4e48614d5289d72d743c3e81305d445c46"
+ integrity sha512-EgBwY+lIaHHgMJIqVOGHfIfpdmmUDNoNO/GAUGeFPf+q0dF+DtwhJPEMShhzh64X2MeCZcmSO6Kinx0Bvmmz2g==
+ dependencies:
+ "@solana/codecs-core" "2.0.0-preview.2"
+ "@solana/codecs-numbers" "2.0.0-preview.2"
+ "@solana/errors" "2.0.0-preview.2"
+
+"@solana/codecs@2.0.0-preview.2":
+ version "2.0.0-preview.2"
+ resolved "https://registry.yarnpkg.com/@solana/codecs/-/codecs-2.0.0-preview.2.tgz#d6615fec98f423166fb89409f9a4ad5b74c10935"
+ integrity sha512-4HHzCD5+pOSmSB71X6w9ptweV48Zj1Vqhe732+pcAQ2cMNnN0gMPMdDq7j3YwaZDZ7yrILVV/3+HTnfT77t2yA==
+ dependencies:
+ "@solana/codecs-core" "2.0.0-preview.2"
+ "@solana/codecs-data-structures" "2.0.0-preview.2"
+ "@solana/codecs-numbers" "2.0.0-preview.2"
+ "@solana/codecs-strings" "2.0.0-preview.2"
+ "@solana/options" "2.0.0-preview.2"
+
+"@solana/errors@2.0.0-preview.2":
+ version "2.0.0-preview.2"
+ resolved "https://registry.yarnpkg.com/@solana/errors/-/errors-2.0.0-preview.2.tgz#e0ea8b008c5c02528d5855bc1903e5e9bbec322e"
+ integrity sha512-H2DZ1l3iYF5Rp5pPbJpmmtCauWeQXRJapkDg8epQ8BJ7cA2Ut/QEtC3CMmw/iMTcuS6uemFNLcWvlOfoQhvQuA==
+ dependencies:
+ chalk "^5.3.0"
+ commander "^12.0.0"
+
+"@solana/options@2.0.0-preview.2":
+ version "2.0.0-preview.2"
+ resolved "https://registry.yarnpkg.com/@solana/options/-/options-2.0.0-preview.2.tgz#13ff008bf43a5056ef9a091dc7bb3f39321e867e"
+ integrity sha512-FAHqEeH0cVsUOTzjl5OfUBw2cyT8d5Oekx4xcn5hn+NyPAfQJgM3CEThzgRD6Q/4mM5pVUnND3oK/Mt1RzSE/w==
+ dependencies:
+ "@solana/codecs-core" "2.0.0-preview.2"
+ "@solana/codecs-numbers" "2.0.0-preview.2"
+
"@solana/spl-governance@0.3.27":
version "0.3.27"
resolved "https://registry.yarnpkg.com/@solana/spl-governance/-/spl-governance-0.3.27.tgz#54ab8310a142b3d581d8abc3df37e3511f02619c"
@@ -1630,6 +1726,14 @@
bs58 "^4.0.1"
superstruct "^0.15.2"
+"@solana/spl-token-metadata@^0.1.2":
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/@solana/spl-token-metadata/-/spl-token-metadata-0.1.4.tgz#5cdc3b857a8c4a6877df24e24a8648c4132d22ba"
+ integrity sha512-N3gZ8DlW6NWDV28+vCCDJoTqaCZiF/jDUnk3o8GRkAFzHObiR60Bs1gXHBa8zCPdvOwiG6Z3dg5pg7+RW6XNsQ==
+ dependencies:
+ "@solana/codecs" "2.0.0-preview.2"
+ "@solana/spl-type-length-value" "0.1.0"
+
"@solana/spl-token@0.3.7":
version "0.3.7"
resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.7.tgz#6f027f9ad8e841f792c32e50920d9d2e714fc8da"
@@ -1651,13 +1755,21 @@
buffer-layout "^1.2.0"
dotenv "10.0.0"
-"@solana/spl-token@^0.3.8":
- version "0.3.8"
- resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.8.tgz#8e9515ea876e40a4cc1040af865f61fc51d27edf"
- integrity sha512-ogwGDcunP9Lkj+9CODOWMiVJEdRtqHAtX2rWF62KxnnSWtMZtV9rDhTrZFshiyJmxDnRL/1nKE1yJHg4jjs3gg==
+"@solana/spl-token@^0.3.8", "@solana/spl-token@^0.3.9":
+ version "0.3.11"
+ resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.11.tgz#cdc10f9472b29b39c8983c92592cadd06627fb9a"
+ integrity sha512-bvohO3rIMSVL24Pb+I4EYTJ6cL82eFpInEXD/I8K8upOGjpqHsKUoAempR/RnUlI1qSFNyFlWJfu6MNUgfbCQQ==
dependencies:
"@solana/buffer-layout" "^4.0.0"
"@solana/buffer-layout-utils" "^0.2.0"
+ "@solana/spl-token-metadata" "^0.1.2"
+ buffer "^6.0.3"
+
+"@solana/spl-type-length-value@0.1.0":
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/@solana/spl-type-length-value/-/spl-type-length-value-0.1.0.tgz#b5930cf6c6d8f50c7ff2a70463728a4637a2f26b"
+ integrity sha512-JBMGB0oR4lPttOZ5XiUGyvylwLQjt1CPJa6qQ5oM+MBCndfjz2TKKkw0eATlLLcYmq1jBVsNlJ2cD6ns2GR7lA==
+ dependencies:
buffer "^6.0.3"
"@solana/wallet-adapter-alpha@^0.1.10":
@@ -2602,11 +2714,6 @@
resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-3.0.3.tgz#d6bfbbdd0c187354ca555213d1962f6d0691ff4e"
integrity sha512-Xe7IImK09HP1sv2M/aI+48a20VX+TdRJucfq4vfRVy6nWN8PYPOEnlMRSgxJAgYQIXJVL8dZ4/ilAM7dWNaOww==
-"@types/json-schema@^7.0.9":
- version "7.0.11"
- resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
- integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
-
"@types/json5@^0.0.29":
version "0.0.29"
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
@@ -2712,11 +2819,6 @@
resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
-"@types/semver@^7.3.12":
- version "7.3.13"
- resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91"
- integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==
-
"@types/sinon@^17.0.3":
version "17.0.3"
resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-17.0.3.tgz#9aa7e62f0a323b9ead177ed23a36ea757141a5fa"
@@ -2743,20 +2845,20 @@
dependencies:
"@types/node" "*"
-"@typescript-eslint/eslint-plugin@5.43.0":
- version "5.43.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.43.0.tgz#4a5248eb31b454715ddfbf8cfbf497529a0a78bc"
- integrity sha512-wNPzG+eDR6+hhW4yobEmpR36jrqqQv1vxBq5LJO3fBAktjkvekfr4BRl+3Fn1CM/A+s8/EiGUbOMDoYqWdbtXA==
- dependencies:
- "@typescript-eslint/scope-manager" "5.43.0"
- "@typescript-eslint/type-utils" "5.43.0"
- "@typescript-eslint/utils" "5.43.0"
- debug "^4.3.4"
- ignore "^5.2.0"
- natural-compare-lite "^1.4.0"
- regexpp "^3.2.0"
- semver "^7.3.7"
- tsutils "^3.21.0"
+"@typescript-eslint/eslint-plugin@7.15.0":
+ version "7.15.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.15.0.tgz#8eaf396ac2992d2b8f874b68eb3fcd6b179cb7f3"
+ integrity sha512-uiNHpyjZtFrLwLDpHnzaDlP3Tt6sGMqTCiqmxaN4n4RP0EfYZDODJyddiFDF44Hjwxr5xAcaYxVKm9QKQFJFLA==
+ dependencies:
+ "@eslint-community/regexpp" "^4.10.0"
+ "@typescript-eslint/scope-manager" "7.15.0"
+ "@typescript-eslint/type-utils" "7.15.0"
+ "@typescript-eslint/utils" "7.15.0"
+ "@typescript-eslint/visitor-keys" "7.15.0"
+ graphemer "^1.4.0"
+ ignore "^5.3.1"
+ natural-compare "^1.4.0"
+ ts-api-utils "^1.3.0"
"@typescript-eslint/parser@^5.4.2 || ^6.0.0":
version "6.4.0"
@@ -2769,14 +2871,6 @@
"@typescript-eslint/visitor-keys" "6.4.0"
debug "^4.3.4"
-"@typescript-eslint/scope-manager@5.43.0":
- version "5.43.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.43.0.tgz#566e46303392014d5d163704724872e1f2dd3c15"
- integrity sha512-XNWnGaqAtTJsUiZaoiGIrdJYHsUOd3BZ3Qj5zKp9w6km6HsrjPk/TGZv0qMTWyWj0+1QOqpHQ2gZOLXaGA9Ekw==
- dependencies:
- "@typescript-eslint/types" "5.43.0"
- "@typescript-eslint/visitor-keys" "5.43.0"
-
"@typescript-eslint/scope-manager@6.4.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.4.0.tgz#3048e4262ba3eafa4e2e69b08912d9037ec646ae"
@@ -2785,43 +2879,38 @@
"@typescript-eslint/types" "6.4.0"
"@typescript-eslint/visitor-keys" "6.4.0"
-"@typescript-eslint/type-utils@5.43.0":
- version "5.43.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.43.0.tgz#91110fb827df5161209ecca06f70d19a96030be6"
- integrity sha512-K21f+KY2/VvYggLf5Pk4tgBOPs2otTaIHy2zjclo7UZGLyFH86VfUOm5iq+OtDtxq/Zwu2I3ujDBykVW4Xtmtg==
+"@typescript-eslint/scope-manager@7.15.0":
+ version "7.15.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-7.15.0.tgz#201b34b0720be8b1447df17b963941bf044999b2"
+ integrity sha512-Q/1yrF/XbxOTvttNVPihxh1b9fxamjEoz2Os/Pe38OHwxC24CyCqXxGTOdpb4lt6HYtqw9HetA/Rf6gDGaMPlw==
dependencies:
- "@typescript-eslint/typescript-estree" "5.43.0"
- "@typescript-eslint/utils" "5.43.0"
+ "@typescript-eslint/types" "7.15.0"
+ "@typescript-eslint/visitor-keys" "7.15.0"
+
+"@typescript-eslint/type-utils@7.15.0":
+ version "7.15.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-7.15.0.tgz#5b83c904c6de91802fb399305a50a56d10472c39"
+ integrity sha512-SkgriaeV6PDvpA6253PDVep0qCqgbO1IOBiycjnXsszNTVQe5flN5wR5jiczoEoDEnAqYFSFFc9al9BSGVltkg==
+ dependencies:
+ "@typescript-eslint/typescript-estree" "7.15.0"
+ "@typescript-eslint/utils" "7.15.0"
debug "^4.3.4"
- tsutils "^3.21.0"
+ ts-api-utils "^1.3.0"
"@typescript-eslint/types@4.33.0":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.33.0.tgz#a1e59036a3b53ae8430ceebf2a919dc7f9af6d72"
integrity sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==
-"@typescript-eslint/types@5.43.0":
- version "5.43.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.43.0.tgz#e4ddd7846fcbc074325293515fa98e844d8d2578"
- integrity sha512-jpsbcD0x6AUvV7tyOlyvon0aUsQpF8W+7TpJntfCUWU1qaIKu2K34pMwQKSzQH8ORgUrGYY6pVIh1Pi8TNeteg==
-
"@typescript-eslint/types@6.4.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.4.0.tgz#5b109a59a805f0d8d375895e42d9e5f0037f66ee"
integrity sha512-+FV9kVFrS7w78YtzkIsNSoYsnOtrYVnKWSTVXoL1761CsCRv5wpDOINgsXpxD67YCLZtVQekDDyaxfjVWUJmmg==
-"@typescript-eslint/typescript-estree@5.43.0":
- version "5.43.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.43.0.tgz#b6883e58ba236a602c334be116bfc00b58b3b9f2"
- integrity sha512-BZ1WVe+QQ+igWal2tDbNg1j2HWUkAa+CVqdU79L4HP9izQY6CNhXfkNwd1SS4+sSZAP/EthI1uiCSY/+H0pROg==
- dependencies:
- "@typescript-eslint/types" "5.43.0"
- "@typescript-eslint/visitor-keys" "5.43.0"
- debug "^4.3.4"
- globby "^11.1.0"
- is-glob "^4.0.3"
- semver "^7.3.7"
- tsutils "^3.21.0"
+"@typescript-eslint/types@7.15.0":
+ version "7.15.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-7.15.0.tgz#fb894373a6e3882cbb37671ffddce44f934f62fc"
+ integrity sha512-aV1+B1+ySXbQH0pLK0rx66I3IkiZNidYobyfn0WFsdGhSXw+P3YOqeTq5GED458SfB24tg+ux3S+9g118hjlTw==
"@typescript-eslint/typescript-estree@6.4.0":
version "6.4.0"
@@ -2836,6 +2925,20 @@
semver "^7.5.4"
ts-api-utils "^1.0.1"
+"@typescript-eslint/typescript-estree@7.15.0":
+ version "7.15.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-7.15.0.tgz#e323bfa3966e1485b638ce751f219fc1f31eba37"
+ integrity sha512-gjyB/rHAopL/XxfmYThQbXbzRMGhZzGw6KpcMbfe8Q3nNQKStpxnUKeXb0KiN/fFDR42Z43szs6rY7eHk0zdGQ==
+ dependencies:
+ "@typescript-eslint/types" "7.15.0"
+ "@typescript-eslint/visitor-keys" "7.15.0"
+ debug "^4.3.4"
+ globby "^11.1.0"
+ is-glob "^4.0.3"
+ minimatch "^9.0.4"
+ semver "^7.6.0"
+ ts-api-utils "^1.3.0"
+
"@typescript-eslint/typescript-estree@^4.33.0", "@typescript-eslint/typescript-estree@^4.8.2":
version "4.33.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz#0dfb51c2908f68c5c08d82aefeaf166a17c24609"
@@ -2849,19 +2952,15 @@
semver "^7.3.5"
tsutils "^3.21.0"
-"@typescript-eslint/utils@5.43.0":
- version "5.43.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.43.0.tgz#00fdeea07811dbdf68774a6f6eacfee17fcc669f"
- integrity sha512-8nVpA6yX0sCjf7v/NDfeaOlyaIIqL7OaIGOWSPFqUKK59Gnumd3Wa+2l8oAaYO2lk0sO+SbWFWRSvhu8gLGv4A==
- dependencies:
- "@types/json-schema" "^7.0.9"
- "@types/semver" "^7.3.12"
- "@typescript-eslint/scope-manager" "5.43.0"
- "@typescript-eslint/types" "5.43.0"
- "@typescript-eslint/typescript-estree" "5.43.0"
- eslint-scope "^5.1.1"
- eslint-utils "^3.0.0"
- semver "^7.3.7"
+"@typescript-eslint/utils@7.15.0":
+ version "7.15.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-7.15.0.tgz#9e6253c4599b6e7da2fb64ba3f549c73eb8c1960"
+ integrity sha512-hfDMDqaqOqsUVGiEPSMLR/AjTSCsmJwjpKkYQRo1FNbmW4tBwBspYDwO9eh7sKSTwMQgBw9/T4DHudPaqshRWA==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.4.0"
+ "@typescript-eslint/scope-manager" "7.15.0"
+ "@typescript-eslint/types" "7.15.0"
+ "@typescript-eslint/typescript-estree" "7.15.0"
"@typescript-eslint/visitor-keys@4.33.0":
version "4.33.0"
@@ -2871,14 +2970,6 @@
"@typescript-eslint/types" "4.33.0"
eslint-visitor-keys "^2.0.0"
-"@typescript-eslint/visitor-keys@5.43.0":
- version "5.43.0"
- resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.43.0.tgz#cbbdadfdfea385310a20a962afda728ea106befa"
- integrity sha512-icl1jNH/d18OVHLfcwdL3bWUKsBeIiKYTGxMJCoGe7xFht+E4QgzOqoWYrU8XSLJWhVw8nTacbm03v23J/hFTg==
- dependencies:
- "@typescript-eslint/types" "5.43.0"
- eslint-visitor-keys "^3.3.0"
-
"@typescript-eslint/visitor-keys@6.4.0":
version "6.4.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.0.tgz#96a426cdb1add28274abd7a34aefe27f8b7d51ef"
@@ -2887,6 +2978,14 @@
"@typescript-eslint/types" "6.4.0"
eslint-visitor-keys "^3.4.1"
+"@typescript-eslint/visitor-keys@7.15.0":
+ version "7.15.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-7.15.0.tgz#1da0726201a859343fe6a05742a7c1792fff5b66"
+ integrity sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==
+ dependencies:
+ "@typescript-eslint/types" "7.15.0"
+ eslint-visitor-keys "^3.4.3"
+
"@wallet-standard/app@^1.0.1":
version "1.0.1"
resolved "https://registry.yarnpkg.com/@wallet-standard/app/-/app-1.0.1.tgz#f83c3ae887f7fb52497a7b259bba734ae10a2994"
@@ -3463,7 +3562,7 @@ axios@^0.21.0:
dependencies:
follow-redirects "^1.14.0"
-axios@^1.1.3, axios@^1.2.0:
+axios@^1.1.3, axios@^1.2.0, axios@^1.6.2:
version "1.7.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.2.tgz#b625db8a7051fbea61c35a3cbb3a1daa7b9c7621"
integrity sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==
@@ -3645,6 +3744,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"
+brace-expansion@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
+ integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+ dependencies:
+ balanced-match "^1.0.0"
+
braces@^3.0.2, braces@~3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
@@ -3877,6 +3983,11 @@ chalk@^4.0.0, chalk@^4.1.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
+chalk@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385"
+ integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==
+
chokidar@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
@@ -4008,6 +4119,11 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
+commander@^12.0.0:
+ version "12.1.0"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-12.1.0.tgz#01423b36f501259fdaac4d0e4d60c96c991585d3"
+ integrity sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==
+
commander@^2.16.0, commander@^2.20.3, commander@^2.8.1:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -4316,7 +4432,7 @@ decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
-decimal.js-light@^2.4.1:
+decimal.js-light@^2.4.1, decimal.js-light@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz#134fd32508f19e208f4fb2f8dac0d2626a867934"
integrity sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==
@@ -5012,14 +5128,6 @@ eslint-plugin-tailwindcss@3.13.0:
fast-glob "^3.2.5"
postcss "^8.4.4"
-eslint-scope@^5.1.1:
- version "5.1.1"
- resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
- integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==
- dependencies:
- esrecurse "^4.3.0"
- estraverse "^4.1.1"
-
eslint-scope@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
@@ -5040,7 +5148,7 @@ eslint-visitor-keys@^2.0.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
-eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1:
+eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
version "3.4.3"
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
@@ -5114,11 +5222,6 @@ esrecurse@^4.3.0:
dependencies:
estraverse "^5.2.0"
-estraverse@^4.1.1:
- version "4.3.0"
- resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d"
- integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==
-
estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
@@ -5287,6 +5390,11 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
+fecha@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.3.tgz#4d9ccdbc61e8629b259fdca67e65891448d569fd"
+ integrity sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==
+
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
version "3.2.0"
resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
@@ -5659,6 +5767,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.2.3, graceful-fs@^4.2.4:
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+graphemer@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
+ integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+
graphviz@0.0.9:
version "0.0.9"
resolved "https://registry.yarnpkg.com/graphviz/-/graphviz-0.0.9.tgz#0bbf1df588c6a92259282da35323622528c4bbc4"
@@ -5872,10 +5985,10 @@ ieee754@^1.1.13, ieee754@^1.2.1:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
-ignore@^5.2.0:
- version "5.2.4"
- resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
- integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
+ignore@^5.2.0, ignore@^5.3.1:
+ version "5.3.1"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef"
+ integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==
immer@9.0.12:
version "9.0.12"
@@ -6829,13 +6942,6 @@ lower-case@^2.0.2:
dependencies:
tslib "^2.0.3"
-lru-cache@^6.0.0:
- version "6.0.0"
- resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
- integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
- dependencies:
- yallist "^4.0.0"
-
madge@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/madge/-/madge-4.0.2.tgz#56a3aff8021a5844f8713e0789f6ee94095f2f41"
@@ -6963,6 +7069,13 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
dependencies:
brace-expansion "^1.1.7"
+minimatch@^9.0.4:
+ version "9.0.5"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
+ integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+ dependencies:
+ brace-expansion "^2.0.1"
+
minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
@@ -7094,11 +7207,6 @@ napi-build-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz#b1fddc0b2c46e380a0b7a76f984dd47c41a13806"
integrity sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==
-natural-compare-lite@^1.4.0:
- version "1.4.0"
- resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4"
- integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==
-
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -8513,12 +8621,10 @@ semver@^6.0.0, semver@^6.3.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.3.2, semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.4:
- version "7.5.4"
- resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
- integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
- dependencies:
- lru-cache "^6.0.0"
+semver@^7.3.2, semver@^7.3.5, semver@^7.3.8, semver@^7.5.4, semver@^7.6.0:
+ version "7.6.2"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13"
+ integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==
set-blocking@^2.0.0, set-blocking@~2.0.0:
version "2.0.0"
@@ -9025,6 +9131,11 @@ to-regex-range@^5.0.1:
dependencies:
is-number "^7.0.0"
+toformat@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/toformat/-/toformat-2.0.0.tgz#7a043fd2dfbe9021a4e36e508835ba32056739d8"
+ integrity sha512-03SWBVop6nU8bpyZCx7SodpYznbZF5R4ljwNLBcTQzKOD9xuihRo/psX58llS1BMFhhAI08H3luot5GoXJz2pQ==
+
toggle-selection@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32"
@@ -9060,10 +9171,10 @@ truncate-utf8-bytes@^1.0.0:
dependencies:
utf8-byte-length "^1.0.1"
-ts-api-utils@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.0.1.tgz#8144e811d44c749cd65b2da305a032510774452d"
- integrity sha512-lC/RGlPmwdrIBFTX59wwNzqh7aR2otPNPR/5brHZm/XKFYKsfqxihXUe9pU3JI+3vGkl+vyCoNNnPhJn3aLK1A==
+ts-api-utils@^1.0.1, ts-api-utils@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.3.0.tgz#4b490e27129f1e8e686b45cc4ab63714dc60eea1"
+ integrity sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==
ts-ev@^0.4.0:
version "0.4.0"
@@ -9151,10 +9262,10 @@ typed-array-length@^1.0.4:
for-each "^0.3.3"
is-typed-array "^1.1.9"
-typescript@4.9.4:
- version "4.9.4"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78"
- integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==
+typescript@5.5.3:
+ version "5.5.3"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.3.tgz#e1b0a3c394190838a0b168e771b0ad56a0af0faa"
+ integrity sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==
typescript@^3.9.10, typescript@^3.9.5, typescript@^3.9.7:
version "3.9.10"