max accounts fix (#424)
* raydium instruction amm * adjustments * fix * fix * fix
This commit is contained in:
parent
0b61387829
commit
2462f5ee14
|
@ -342,11 +342,13 @@ const ListToken = ({ goBack }: { goBack: () => void }) => {
|
|||
mode,
|
||||
walletForCheck,
|
||||
undefined, // mangoAccount
|
||||
'JUPITER',
|
||||
onlyDirect,
|
||||
onlyDirect ? 'JUPITER_DIRECT' : 'JUPITER',
|
||||
connection,
|
||||
null,
|
||||
false,
|
||||
)
|
||||
},
|
||||
[wallet.publicKey],
|
||||
[wallet.publicKey, connection],
|
||||
)
|
||||
|
||||
const handleLiquidityCheck = useCallback(
|
||||
|
|
|
@ -83,6 +83,7 @@ const MarketSwapForm = ({
|
|||
swapMode,
|
||||
wallet: publicKey?.toBase58(),
|
||||
mangoAccount,
|
||||
mangoAccountSwap: true,
|
||||
enabled: () =>
|
||||
!!(
|
||||
inputBank?.mint &&
|
||||
|
@ -100,6 +101,7 @@ const MarketSwapForm = ({
|
|||
swapMode,
|
||||
wallet: publicKey?.toBase58(),
|
||||
mangoAccount,
|
||||
mangoAccountSwap: true,
|
||||
mode: 'JUPITER_DIRECT',
|
||||
enabled: () =>
|
||||
!!(
|
||||
|
@ -231,22 +233,26 @@ const MarketSwapForm = ({
|
|||
depending on the swapMode and set those values in state
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (typeof bestRoute !== 'undefined') {
|
||||
setSelectedRoute(bestRoute)
|
||||
if (
|
||||
typeof bestRoute !== 'undefined' ||
|
||||
typeof bestDirectRoute !== 'undefined'
|
||||
) {
|
||||
const newRoute = bestRoute || bestDirectRoute
|
||||
setSelectedRoute(newRoute)
|
||||
|
||||
if (inputBank && swapMode === 'ExactOut' && bestRoute?.inAmount) {
|
||||
const inAmount = new Decimal(bestRoute.inAmount)
|
||||
if (inputBank && swapMode === 'ExactOut' && newRoute?.inAmount) {
|
||||
const inAmount = new Decimal(newRoute.inAmount)
|
||||
.div(10 ** inputBank.mintDecimals)
|
||||
.toString()
|
||||
setAmountInFormValue(inAmount)
|
||||
} else if (outputBank && swapMode === 'ExactIn' && bestRoute?.outAmount) {
|
||||
const outAmount = new Decimal(bestRoute.outAmount)
|
||||
} else if (outputBank && swapMode === 'ExactIn' && newRoute?.outAmount) {
|
||||
const outAmount = new Decimal(newRoute.outAmount)
|
||||
.div(10 ** outputBank.mintDecimals)
|
||||
.toString()
|
||||
setAmountOutFormValue(outAmount)
|
||||
}
|
||||
}
|
||||
}, [bestRoute, swapMode, inputBank, outputBank])
|
||||
}, [bestRoute, bestDirectRoute, swapMode, inputBank, outputBank])
|
||||
|
||||
const handleSwitchTokens = useCallback(() => {
|
||||
if (amountInAsDecimal?.gt(0) && amountOutAsDecimal.gte(0)) {
|
||||
|
@ -312,7 +318,7 @@ const MarketSwapForm = ({
|
|||
onSuccess={onSuccess}
|
||||
refetchRoute={refetchRoute}
|
||||
routes={
|
||||
bestRoute
|
||||
bestRoute || bestDirectRoute
|
||||
? ([bestRoute, bestDirectRoute].filter(
|
||||
(x) => x && !x.error,
|
||||
) as JupiterV6RouteInfo[])
|
||||
|
|
|
@ -286,6 +286,7 @@ const SwapReviewRouteInfo = ({
|
|||
}: JupiterRouteInfoProps) => {
|
||||
const { t } = useTranslation(['common', 'account', 'swap', 'trade'])
|
||||
const slippage = mangoStore((s) => s.swap.slippage)
|
||||
|
||||
const wallet = useWallet()
|
||||
const [showRoutesModal, setShowRoutesModal] = useState<boolean>(false)
|
||||
const [swapRate, setSwapRate] = useState<boolean>(false)
|
||||
|
@ -429,6 +430,7 @@ const SwapReviewRouteInfo = ({
|
|||
)
|
||||
return
|
||||
setSubmitting(true)
|
||||
|
||||
const [ixs, alts] =
|
||||
// selectedRoute.routerName === 'Mango'
|
||||
// ? await prepareMangoRouterInstructions(
|
||||
|
@ -438,14 +440,16 @@ const SwapReviewRouteInfo = ({
|
|||
// mangoAccount.owner,
|
||||
// )
|
||||
// :
|
||||
await fetchJupiterTransaction(
|
||||
connection,
|
||||
selectedRoute,
|
||||
wallet.publicKey,
|
||||
slippage,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
)
|
||||
selectedRoute.instructions
|
||||
? [selectedRoute.instructions, []]
|
||||
: await fetchJupiterTransaction(
|
||||
connection,
|
||||
selectedRoute,
|
||||
wallet.publicKey,
|
||||
slippage,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
)
|
||||
|
||||
try {
|
||||
const { signature: tx, slot } = await client.marginTrade({
|
||||
|
|
|
@ -69,6 +69,7 @@ const WalletSwapForm = ({ setShowTokenSelect }: WalletSwapFormProps) => {
|
|||
swapMode,
|
||||
wallet: publicKey?.toBase58(),
|
||||
mangoAccount: undefined,
|
||||
mangoAccountSwap: false,
|
||||
mode: 'JUPITER',
|
||||
enabled: () =>
|
||||
!!(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
import { PublicKey } from '@solana/web3.js'
|
||||
import { Connection, PublicKey } from '@solana/web3.js'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import Decimal from 'decimal.js'
|
||||
import { JupiterV6RouteInfo } from 'types/jupiter'
|
||||
|
@ -7,9 +7,12 @@ import { JupiterV6RouteInfo } from 'types/jupiter'
|
|||
import useJupiterSwapData from './useJupiterSwapData'
|
||||
import { useMemo } from 'react'
|
||||
import { JUPITER_V6_QUOTE_API_MAINNET } from 'utils/constants'
|
||||
import { MangoAccount } from '@blockworks-foundation/mango-v4'
|
||||
import { MangoAccount, toUiDecimals } from '@blockworks-foundation/mango-v4'
|
||||
import { findRaydiumPoolInfo, getSwapTransaction } from 'utils/swap/raydium'
|
||||
import mangoStore from '@store/mangoStore'
|
||||
import { fetchJupiterTransaction } from './SwapReviewRouteInfo'
|
||||
|
||||
type SwapModes = 'ALL' | 'JUPITER' | 'MANGO' | 'JUPITER_DIRECT'
|
||||
type SwapModes = 'ALL' | 'JUPITER' | 'MANGO' | 'JUPITER_DIRECT' | 'RAYDIUM'
|
||||
|
||||
type useQuoteRoutesPropTypes = {
|
||||
inputMint: string | undefined
|
||||
|
@ -20,6 +23,7 @@ type useQuoteRoutesPropTypes = {
|
|||
wallet: string | undefined
|
||||
mangoAccount: MangoAccount | undefined
|
||||
mode?: SwapModes
|
||||
mangoAccountSwap: boolean
|
||||
enabled?: () => boolean
|
||||
}
|
||||
|
||||
|
@ -31,6 +35,8 @@ const fetchJupiterRoute = async (
|
|||
swapMode = 'ExactIn',
|
||||
onlyDirectRoutes = true,
|
||||
maxAccounts = 64,
|
||||
connection: Connection,
|
||||
wallet: string,
|
||||
) => {
|
||||
if (!inputMint || !outputMint) return
|
||||
try {
|
||||
|
@ -60,8 +66,20 @@ const fetchJupiterRoute = async (
|
|||
`${JUPITER_V6_QUOTE_API_MAINNET}/quote?${paramsString}`,
|
||||
)
|
||||
const res: JupiterV6RouteInfo = await response.json()
|
||||
const [ixes] = await fetchJupiterTransaction(
|
||||
connection,
|
||||
res,
|
||||
new PublicKey(wallet),
|
||||
slippage,
|
||||
new PublicKey(inputMint),
|
||||
new PublicKey(outputMint),
|
||||
)
|
||||
return {
|
||||
bestRoute: res,
|
||||
bestRoute:
|
||||
[...ixes.flatMap((x) => x.keys.flatMap((k) => k.pubkey))].length <=
|
||||
maxAccounts
|
||||
? res
|
||||
: undefined,
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
@ -69,6 +87,39 @@ const fetchJupiterRoute = async (
|
|||
}
|
||||
}
|
||||
|
||||
const fetchRaydiumRoute = async (
|
||||
inputMint: string | undefined,
|
||||
outputMint: string | undefined,
|
||||
amount = 0,
|
||||
slippage = 50,
|
||||
connection: Connection,
|
||||
wallet: string,
|
||||
mangoAccountSwap: boolean,
|
||||
) => {
|
||||
if (!inputMint || !outputMint) return
|
||||
try {
|
||||
const poolKeys = await findRaydiumPoolInfo(
|
||||
connection,
|
||||
outputMint,
|
||||
inputMint,
|
||||
)
|
||||
|
||||
if (poolKeys) {
|
||||
return await getSwapTransaction(
|
||||
connection,
|
||||
outputMint,
|
||||
amount,
|
||||
poolKeys!,
|
||||
slippage,
|
||||
new PublicKey(wallet),
|
||||
mangoAccountSwap,
|
||||
)
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error fetching raydium route', e)
|
||||
}
|
||||
}
|
||||
|
||||
// const fetchMangoRoutes = async (
|
||||
// inputMint = 'So11111111111111111111111111111111111111112',
|
||||
// outputMint = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
|
||||
|
@ -135,7 +186,9 @@ export const handleGetRoutes = async (
|
|||
wallet: string | undefined,
|
||||
mangoAccount: MangoAccount | undefined,
|
||||
mode: SwapModes = 'ALL',
|
||||
jupiterOnlyDirectRoutes = false,
|
||||
connection: Connection,
|
||||
inputTokenDecimals: number | null,
|
||||
mangoAccountSwap: boolean,
|
||||
) => {
|
||||
try {
|
||||
wallet ||= PublicKey.default.toBase58()
|
||||
|
@ -154,35 +207,41 @@ export const handleGetRoutes = async (
|
|||
|
||||
const routes = []
|
||||
|
||||
// FIXME: Disable for now, mango router needs to use ALTs
|
||||
// if (mode === 'ALL' || mode === 'MANGO') {
|
||||
// const mangoRoute = fetchMangoRoutes(
|
||||
// inputMint,
|
||||
// outputMint,
|
||||
// amount,
|
||||
// slippage,
|
||||
// swapMode,
|
||||
// feeBps,
|
||||
// wallet,
|
||||
// )
|
||||
// routes.push(mangoRoute)
|
||||
// }
|
||||
if (
|
||||
connection &&
|
||||
inputTokenDecimals &&
|
||||
swapMode === 'ExactIn' &&
|
||||
(mode === 'ALL' || mode === 'RAYDIUM')
|
||||
) {
|
||||
const raydiumRoute = fetchRaydiumRoute(
|
||||
inputMint,
|
||||
outputMint,
|
||||
toUiDecimals(amount, inputTokenDecimals),
|
||||
slippage,
|
||||
connection,
|
||||
wallet,
|
||||
mangoAccountSwap,
|
||||
)
|
||||
if (raydiumRoute) {
|
||||
routes.push(raydiumRoute)
|
||||
}
|
||||
}
|
||||
|
||||
if (mode === 'ALL' || mode === 'JUPITER' || mode === 'JUPITER_DIRECT') {
|
||||
const jupiterRoute = await fetchJupiterRoute(
|
||||
const jupiterRoute = fetchJupiterRoute(
|
||||
inputMint,
|
||||
outputMint,
|
||||
amount,
|
||||
slippage,
|
||||
swapMode,
|
||||
jupiterOnlyDirectRoutes
|
||||
? jupiterOnlyDirectRoutes
|
||||
: mode === 'JUPITER_DIRECT'
|
||||
? true
|
||||
: false,
|
||||
mode === 'JUPITER_DIRECT' ? true : false,
|
||||
maxAccounts,
|
||||
connection,
|
||||
wallet,
|
||||
)
|
||||
routes.push(jupiterRoute)
|
||||
if (jupiterRoute) {
|
||||
routes.push(jupiterRoute)
|
||||
}
|
||||
}
|
||||
|
||||
const results = await Promise.allSettled(routes)
|
||||
|
@ -199,6 +258,7 @@ export const handleGetRoutes = async (
|
|||
? Number(b.bestRoute.outAmount) - Number(a.bestRoute.outAmount)
|
||||
: Number(a.bestRoute.inAmount) - Number(b.bestRoute.inAmount),
|
||||
)
|
||||
|
||||
return {
|
||||
bestRoute: sortedByBiggestOutAmount[0].bestRoute,
|
||||
}
|
||||
|
@ -218,8 +278,10 @@ const useQuoteRoutes = ({
|
|||
wallet,
|
||||
mangoAccount,
|
||||
mode = 'ALL',
|
||||
mangoAccountSwap,
|
||||
enabled,
|
||||
}: useQuoteRoutesPropTypes) => {
|
||||
const connection = mangoStore((s) => s.connection)
|
||||
const { inputTokenInfo, outputTokenInfo } = useJupiterSwapData()
|
||||
const decimals = useMemo(() => {
|
||||
return swapMode === 'ExactIn'
|
||||
|
@ -254,6 +316,9 @@ const useQuoteRoutes = ({
|
|||
wallet,
|
||||
mangoAccount,
|
||||
mode,
|
||||
connection,
|
||||
decimals,
|
||||
mangoAccountSwap,
|
||||
),
|
||||
{
|
||||
cacheTime: 1000 * 60,
|
||||
|
|
|
@ -231,6 +231,7 @@ export default function SpotMarketOrderSwapForm() {
|
|||
swapMode: 'ExactIn',
|
||||
wallet: publicKey?.toBase58(),
|
||||
mangoAccount,
|
||||
mangoAccountSwap: true,
|
||||
mode: 'JUPITER',
|
||||
enabled: () =>
|
||||
!!(
|
||||
|
@ -260,15 +261,25 @@ export default function SpotMarketOrderSwapForm() {
|
|||
return
|
||||
|
||||
setPlacingOrder(true)
|
||||
|
||||
const [ixs, alts] = await fetchJupiterTransaction(
|
||||
connection,
|
||||
selectedRoute,
|
||||
publicKey,
|
||||
slippage,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
)
|
||||
const [ixs, alts] =
|
||||
// selectedRoute.routerName === 'Mango'
|
||||
// ? await prepareMangoRouterInstructions(
|
||||
// selectedRoute,
|
||||
// inputBank.mint,
|
||||
// outputBank.mint,
|
||||
// mangoAccount.owner,
|
||||
// )
|
||||
// :
|
||||
selectedRoute.instructions
|
||||
? [selectedRoute.instructions, []]
|
||||
: await fetchJupiterTransaction(
|
||||
connection,
|
||||
selectedRoute,
|
||||
publicKey,
|
||||
slippage,
|
||||
inputBank.mint,
|
||||
outputBank.mint,
|
||||
)
|
||||
|
||||
try {
|
||||
const { signature: tx, slot } = await client.marginTrade({
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { AccountInfo } from '@solana/web3.js'
|
||||
import { AccountInfo, TransactionInstruction } from '@solana/web3.js'
|
||||
|
||||
export declare type SideType = typeof Side.Ask | typeof Side.Bid
|
||||
export declare const Side: {
|
||||
|
@ -123,6 +123,7 @@ export interface JupiterV6RouteInfo {
|
|||
contextSlot?: number
|
||||
timeTaken?: number
|
||||
error?: string
|
||||
instructions?: TransactionInstruction[]
|
||||
}
|
||||
|
||||
export interface JupiterV6RoutePlan {
|
||||
|
|
|
@ -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<GetProgramAccountsResponse> => {
|
||||
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<LiquidityPoolKeys | undefined> => {
|
||||
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())
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue