@shogun-sdk/intents-sdk 1.2.4 → 1.2.6-test
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -19
- package/dist/esm/chains.js +3 -0
- package/dist/esm/chains.js.map +1 -1
- package/dist/esm/constants.js +13 -4
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/core/evm/chain-provider.js +2 -1
- package/dist/esm/core/evm/chain-provider.js.map +1 -1
- package/dist/esm/core/evm/cross-chain-limit-order.js +1 -1
- package/dist/esm/core/evm/cross-chain-limit-order.js.map +1 -1
- package/dist/esm/core/evm/permit2-signature-transfer.js +15 -0
- package/dist/esm/core/evm/permit2-signature-transfer.js.map +1 -0
- package/dist/esm/core/evm/single-chain-dca-order.js +1 -1
- package/dist/esm/core/evm/single-chain-dca-order.js.map +1 -1
- package/dist/esm/core/evm/single-chain-limit-order.js +1 -1
- package/dist/esm/core/evm/single-chain-limit-order.js.map +1 -1
- package/dist/esm/core/solana/dca/single-chain-dca-order.js +3 -2
- package/dist/esm/core/solana/dca/single-chain-dca-order.js.map +1 -1
- package/dist/esm/core/solana/dca/single-chain-limit-order.js +3 -2
- package/dist/esm/core/solana/dca/single-chain-limit-order.js.map +1 -1
- package/dist/esm/core/solana/utils.js +14 -4
- package/dist/esm/core/solana/utils.js.map +1 -1
- package/dist/esm/core/sui/single-chain-dca-order.js +2 -1
- package/dist/esm/core/sui/single-chain-dca-order.js.map +1 -1
- package/dist/esm/core/sui/single-chain-limit-order.js +2 -1
- package/dist/esm/core/sui/single-chain-limit-order.js.map +1 -1
- package/dist/esm/index.js +6 -3
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/token-list.js +2 -0
- package/dist/esm/types/token-list.js.map +1 -0
- package/dist/esm/utils/defillama.js +1 -0
- package/dist/esm/utils/defillama.js.map +1 -1
- package/dist/esm/utils/quote/aggregator.js +158 -42
- package/dist/esm/utils/quote/aggregator.js.map +1 -1
- package/dist/esm/utils/quote/paraswap.js +1 -0
- package/dist/esm/utils/quote/paraswap.js.map +1 -1
- package/dist/esm/utils/quote/pumpfun/estimations.js +169 -0
- package/dist/esm/utils/quote/pumpfun/estimations.js.map +1 -0
- package/dist/esm/utils/quote/pumpfun/estimations_amm.js +130 -0
- package/dist/esm/utils/quote/pumpfun/estimations_amm.js.map +1 -0
- package/dist/esm/utils/quote/pumpfun/index.js +198 -0
- package/dist/esm/utils/quote/pumpfun/index.js.map +1 -0
- package/dist/esm/utils/quote/pumpfun/models.js +70 -0
- package/dist/esm/utils/quote/pumpfun/models.js.map +1 -0
- package/dist/esm/utils/quote/pumpfun/utils.js +269 -0
- package/dist/esm/utils/quote/pumpfun/utils.js.map +1 -0
- package/dist/esm/utils/quote/raydium.js +336 -0
- package/dist/esm/utils/quote/raydium.js.map +1 -0
- package/dist/esm/utils/quote/stablecoins-tokens.js +1 -0
- package/dist/esm/utils/quote/stablecoins-tokens.js.map +1 -1
- package/dist/esm/utils/quote/utils.js +8 -0
- package/dist/esm/utils/quote/utils.js.map +1 -0
- package/dist/esm/utils/tokens/index.js +37 -0
- package/dist/esm/utils/tokens/index.js.map +1 -0
- package/dist/types/chains.d.ts +4 -2
- package/dist/types/chains.d.ts.map +1 -1
- package/dist/types/constants.d.ts +5 -4
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/core/evm/chain-provider.d.ts.map +1 -1
- package/dist/types/core/evm/permit2-signature-transfer.d.ts +3 -0
- package/dist/types/core/evm/permit2-signature-transfer.d.ts.map +1 -0
- package/dist/types/core/solana/dca/single-chain-dca-order.d.ts +1 -2
- package/dist/types/core/solana/dca/single-chain-dca-order.d.ts.map +1 -1
- package/dist/types/core/solana/dca/single-chain-limit-order.d.ts +1 -2
- package/dist/types/core/solana/dca/single-chain-limit-order.d.ts.map +1 -1
- package/dist/types/core/solana/utils.d.ts +2 -0
- package/dist/types/core/solana/utils.d.ts.map +1 -1
- package/dist/types/core/sui/single-chain-dca-order.d.ts.map +1 -1
- package/dist/types/core/sui/single-chain-limit-order.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/types/token-list.d.ts +30 -0
- package/dist/types/types/token-list.d.ts.map +1 -0
- package/dist/types/utils/defillama.d.ts +1 -0
- package/dist/types/utils/defillama.d.ts.map +1 -1
- package/dist/types/utils/quote/aggregator.d.ts +42 -5
- package/dist/types/utils/quote/aggregator.d.ts.map +1 -1
- package/dist/types/utils/quote/paraswap.d.ts.map +1 -1
- package/dist/types/utils/quote/pumpfun/estimations.d.ts +30 -0
- package/dist/types/utils/quote/pumpfun/estimations.d.ts.map +1 -0
- package/dist/types/utils/quote/pumpfun/estimations_amm.d.ts +49 -0
- package/dist/types/utils/quote/pumpfun/estimations_amm.d.ts.map +1 -0
- package/dist/types/utils/quote/pumpfun/index.d.ts +10 -0
- package/dist/types/utils/quote/pumpfun/index.d.ts.map +1 -0
- package/dist/types/utils/quote/pumpfun/models.d.ts +94 -0
- package/dist/types/utils/quote/pumpfun/models.d.ts.map +1 -0
- package/dist/types/utils/quote/pumpfun/utils.d.ts +50 -0
- package/dist/types/utils/quote/pumpfun/utils.d.ts.map +1 -0
- package/dist/types/utils/quote/raydium.d.ts +98 -0
- package/dist/types/utils/quote/raydium.d.ts.map +1 -0
- package/dist/types/utils/quote/stablecoins-tokens.d.ts.map +1 -1
- package/dist/types/utils/quote/utils.d.ts +5 -0
- package/dist/types/utils/quote/utils.d.ts.map +1 -0
- package/dist/types/utils/tokens/index.d.ts +20 -0
- package/dist/types/utils/tokens/index.d.ts.map +1 -0
- package/package.json +20 -18
- package/src/chains.ts +3 -0
- package/src/constants.ts +16 -6
- package/src/core/evm/chain-provider.ts +2 -1
- package/src/core/evm/cross-chain-limit-order.ts +1 -1
- package/src/core/evm/permit2-signature-transfer.ts +15 -0
- package/src/core/evm/single-chain-dca-order.ts +1 -1
- package/src/core/evm/single-chain-limit-order.ts +1 -1
- package/src/core/solana/dca/single-chain-dca-order.ts +3 -3
- package/src/core/solana/dca/single-chain-limit-order.ts +3 -2
- package/src/core/solana/utils.ts +24 -12
- package/src/core/sui/single-chain-dca-order.ts +2 -2
- package/src/core/sui/single-chain-limit-order.ts +2 -2
- package/src/index.ts +14 -4
- package/src/types/token-list.ts +35 -0
- package/src/utils/defillama.ts +1 -0
- package/src/utils/quote/aggregator.ts +210 -55
- package/src/utils/quote/paraswap.ts +1 -0
- package/src/utils/quote/pumpfun/estimations.ts +206 -0
- package/src/utils/quote/pumpfun/estimations_amm.ts +165 -0
- package/src/utils/quote/pumpfun/index.ts +266 -0
- package/src/utils/quote/pumpfun/models.ts +163 -0
- package/src/utils/quote/pumpfun/utils.ts +343 -0
- package/src/utils/quote/raydium.ts +506 -0
- package/src/utils/quote/stablecoins-tokens.ts +1 -0
- package/src/utils/quote/utils.ts +8 -0
- package/src/utils/tokens/index.ts +38 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import type { GlobalConfig, TokenAccount } from './models.js';
|
|
2
|
+
import { BPS_DEN, PreBondedTradeType } from './estimations.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Reserves structure for AMM calculations
|
|
6
|
+
* X = reserve of the token you pay (input side)
|
|
7
|
+
* Y = reserve of the token you receive (output side)
|
|
8
|
+
*/
|
|
9
|
+
interface Reserves {
|
|
10
|
+
xIn: bigint;
|
|
11
|
+
yOut: bigint;
|
|
12
|
+
feeBps: bigint;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Ceiling division for bigints
|
|
17
|
+
* Returns ceil(n / d)
|
|
18
|
+
*/
|
|
19
|
+
function ceilDiv(n: bigint, d: bigint): bigint {
|
|
20
|
+
if (d === 0n) {
|
|
21
|
+
throw new Error('Division by zero');
|
|
22
|
+
}
|
|
23
|
+
return (n + d - 1n) / d;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Calculate output amount for constant product AMM with exact input
|
|
28
|
+
* Formula: dy = (dx_effective * y_out) / (x_in + dx_effective)
|
|
29
|
+
* where dx_effective = dx * BPS_DEN / (BPS_DEN + fee_bps)
|
|
30
|
+
*/
|
|
31
|
+
function amountOutExactIn(dx: bigint, r: Reserves): bigint {
|
|
32
|
+
if (dx === 0n) {
|
|
33
|
+
return 0n;
|
|
34
|
+
}
|
|
35
|
+
if (r.xIn === 0n || r.yOut === 0n) {
|
|
36
|
+
throw new Error('Insufficient liquidity: empty reserves');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const denom = BPS_DEN + r.feeBps;
|
|
40
|
+
const dxEff = (dx * BPS_DEN) / denom;
|
|
41
|
+
const num = dxEff * r.yOut;
|
|
42
|
+
const den = r.xIn + dxEff;
|
|
43
|
+
|
|
44
|
+
return num / den; // floor division
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Calculate input amount needed for exact output in constant product AMM
|
|
49
|
+
* Formula: dx = ceil(dx_effective * (BPS_DEN + fee_bps) / BPS_DEN)
|
|
50
|
+
* where dx_effective = ceil((x_in * dy) / (y_out - dy))
|
|
51
|
+
*/
|
|
52
|
+
function amountInExactOut(dy: bigint, r: Reserves): bigint {
|
|
53
|
+
if (dy === 0n) {
|
|
54
|
+
return 0n;
|
|
55
|
+
}
|
|
56
|
+
if (dy >= r.yOut || r.xIn === 0n) {
|
|
57
|
+
throw new Error('Insufficient liquidity: dy >= reserve or x_in is zero');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const dxEff = ceilDiv(r.xIn * dy, r.yOut - dy);
|
|
61
|
+
const denom = BPS_DEN + r.feeBps;
|
|
62
|
+
|
|
63
|
+
return ceilDiv(dxEff * denom, BPS_DEN);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Calculate base tokens received when buying with exact quote amount
|
|
68
|
+
* @param quoteIn Amount of quote tokens (SOL) to spend
|
|
69
|
+
* @param baseRes Base token reserve
|
|
70
|
+
* @param quoteRes Quote token reserve
|
|
71
|
+
* @param feeBps Fee in basis points
|
|
72
|
+
* @returns Amount of base tokens received
|
|
73
|
+
*/
|
|
74
|
+
export function buyBaseExactInQuote(quoteIn: bigint, baseRes: bigint, quoteRes: bigint, feeBps: bigint): bigint {
|
|
75
|
+
return amountOutExactIn(quoteIn, {
|
|
76
|
+
xIn: quoteRes,
|
|
77
|
+
yOut: baseRes,
|
|
78
|
+
feeBps,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Calculate quote tokens needed to buy exact base amount
|
|
84
|
+
* @param baseOut Desired amount of base tokens
|
|
85
|
+
* @param baseRes Base token reserve
|
|
86
|
+
* @param quoteRes Quote token reserve
|
|
87
|
+
* @param feeBps Fee in basis points
|
|
88
|
+
* @returns Amount of quote tokens needed
|
|
89
|
+
*/
|
|
90
|
+
export function buyBaseExactOutQuote(baseOut: bigint, baseRes: bigint, quoteRes: bigint, feeBps: bigint): bigint {
|
|
91
|
+
return amountInExactOut(baseOut, {
|
|
92
|
+
xIn: quoteRes,
|
|
93
|
+
yOut: baseRes,
|
|
94
|
+
feeBps,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Calculate quote tokens received when selling exact base amount
|
|
100
|
+
* @param baseIn Amount of base tokens to sell
|
|
101
|
+
* @param baseRes Base token reserve
|
|
102
|
+
* @param quoteRes Quote token reserve
|
|
103
|
+
* @param feeBps Fee in basis points
|
|
104
|
+
* @returns Amount of quote tokens received
|
|
105
|
+
*/
|
|
106
|
+
export function sellBaseExactInQuote(baseIn: bigint, baseRes: bigint, quoteRes: bigint, feeBps: bigint): bigint {
|
|
107
|
+
return amountOutExactIn(baseIn, {
|
|
108
|
+
xIn: baseRes,
|
|
109
|
+
yOut: quoteRes,
|
|
110
|
+
feeBps,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Calculate base tokens needed to receive exact quote amount
|
|
116
|
+
* @param quoteOut Desired amount of quote tokens
|
|
117
|
+
* @param baseRes Base token reserve
|
|
118
|
+
* @param quoteRes Quote token reserve
|
|
119
|
+
* @param feeBps Fee in basis points
|
|
120
|
+
* @returns Amount of base tokens needed
|
|
121
|
+
*/
|
|
122
|
+
export function sellBaseExactOutQuote(quoteOut: bigint, baseRes: bigint, quoteRes: bigint, feeBps: bigint): bigint {
|
|
123
|
+
return amountInExactOut(quoteOut, {
|
|
124
|
+
xIn: baseRes,
|
|
125
|
+
yOut: quoteRes,
|
|
126
|
+
feeBps,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Main estimation function for PumpFun AMM
|
|
132
|
+
* @param baseToken Base token account info
|
|
133
|
+
* @param quoteToken Quote token account info
|
|
134
|
+
* @param globalConfig Global configuration with fee settings
|
|
135
|
+
* @param amount Amount to trade
|
|
136
|
+
* @param tradeType Type of trade (BuyExactIn, BuyExactOut, SellExactIn, SellExactOut)
|
|
137
|
+
* @returns Estimated output/input amount
|
|
138
|
+
*/
|
|
139
|
+
export function estimatePumpFunAmm(
|
|
140
|
+
baseToken: TokenAccount,
|
|
141
|
+
quoteToken: TokenAccount,
|
|
142
|
+
globalConfig: GlobalConfig,
|
|
143
|
+
amount: bigint,
|
|
144
|
+
tradeType: PreBondedTradeType,
|
|
145
|
+
): bigint {
|
|
146
|
+
const totalFeeBps =
|
|
147
|
+
globalConfig.lpFeeBasisPoints + globalConfig.protocolFeeBasisPoints + globalConfig.coinCreatorFeeBasisPoints;
|
|
148
|
+
|
|
149
|
+
switch (tradeType) {
|
|
150
|
+
case PreBondedTradeType.BuyExactIn:
|
|
151
|
+
return buyBaseExactInQuote(amount, baseToken.amount, quoteToken.amount, totalFeeBps);
|
|
152
|
+
|
|
153
|
+
case PreBondedTradeType.BuyExactOut:
|
|
154
|
+
return buyBaseExactOutQuote(amount, baseToken.amount, quoteToken.amount, totalFeeBps);
|
|
155
|
+
|
|
156
|
+
case PreBondedTradeType.SellExactIn:
|
|
157
|
+
return sellBaseExactInQuote(amount, baseToken.amount, quoteToken.amount, totalFeeBps);
|
|
158
|
+
|
|
159
|
+
case PreBondedTradeType.SellExactOut:
|
|
160
|
+
return sellBaseExactOutQuote(amount, baseToken.amount, quoteToken.amount, totalFeeBps);
|
|
161
|
+
|
|
162
|
+
default:
|
|
163
|
+
throw new Error(`Invalid trade type: ${tradeType}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { address, type Address } from '@solana/kit';
|
|
2
|
+
import { type SimpleQuote } from '../aggregator.js';
|
|
3
|
+
import type { RaydiumPumpQuoteParams } from '../raydium.js';
|
|
4
|
+
import type { BondingCurve } from './models.js';
|
|
5
|
+
import {
|
|
6
|
+
getPumpFunGlobalAccount,
|
|
7
|
+
getPumpFunBondingCurve,
|
|
8
|
+
getPumpFunGlobalConfig,
|
|
9
|
+
getPumpFunMigratedPool,
|
|
10
|
+
getPoolTokensInfo,
|
|
11
|
+
} from './utils.js';
|
|
12
|
+
import { NATIVE_SOLANA_TOKEN_ADDRESS, WRAPPED_SOL_MINT_ADDRESS } from '../../../constants.js';
|
|
13
|
+
import { JupiterQuoteProvider, type JupiterQuoteParams } from '../jupiter.js';
|
|
14
|
+
import { estimatePumpFunPrebonded, PreBondedTradeType } from './estimations.js';
|
|
15
|
+
import { estimatePumpFunAmm } from './estimations_amm.js';
|
|
16
|
+
import { applySlippage } from '../utils.js';
|
|
17
|
+
|
|
18
|
+
export class PumpFunQuoteProvider {
|
|
19
|
+
public async getQuote(raydiumParams: RaydiumPumpQuoteParams): Promise<SimpleQuote> {
|
|
20
|
+
// Check which token is pump and if it is in prebonding stage
|
|
21
|
+
const inputMint = address(raydiumParams.inputMint);
|
|
22
|
+
const outputMint = address(raydiumParams.outputMint);
|
|
23
|
+
|
|
24
|
+
const [inputBondingCurve, outputBondingCurve] = await this.getTokensBondingCurve(inputMint, outputMint);
|
|
25
|
+
|
|
26
|
+
const { bondingCurve, isInputPrebonded } = this.ensureSinglePrebondedToken(inputBondingCurve, outputBondingCurve);
|
|
27
|
+
|
|
28
|
+
let isInputSol = inputMint === NATIVE_SOLANA_TOKEN_ADDRESS || inputMint === WRAPPED_SOL_MINT_ADDRESS;
|
|
29
|
+
let isOutputSol = outputMint === NATIVE_SOLANA_TOKEN_ADDRESS || outputMint === WRAPPED_SOL_MINT_ADDRESS;
|
|
30
|
+
|
|
31
|
+
if (bondingCurve.complete) {
|
|
32
|
+
console.log('Bonding curve is complete, using Pump AMM for quote');
|
|
33
|
+
// We have to estimate prebonded first and then swap from quote token to output token (if needed)
|
|
34
|
+
// Use Pump AMM logic to get quote
|
|
35
|
+
let [globalConfig, _] = await getPumpFunGlobalConfig();
|
|
36
|
+
|
|
37
|
+
if (isInputPrebonded) {
|
|
38
|
+
let [migratePoolData, _] = await getPumpFunMigratedPool(inputMint);
|
|
39
|
+
let [inputTokenAccount, outputTokenAccount] = await getPoolTokensInfo(
|
|
40
|
+
migratePoolData.poolBaseTokenAccount,
|
|
41
|
+
migratePoolData.poolQuoteTokenAccount,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
let pumpAmountOut = estimatePumpFunAmm(
|
|
45
|
+
inputTokenAccount,
|
|
46
|
+
outputTokenAccount,
|
|
47
|
+
globalConfig,
|
|
48
|
+
BigInt(raydiumParams.amount),
|
|
49
|
+
PreBondedTradeType.SellExactIn,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// Then if output token is SOL or WSOL, return directly
|
|
53
|
+
if (isOutputSol) {
|
|
54
|
+
// Apply slippage to amountOut
|
|
55
|
+
const pumpMinAmountOut = applySlippage(pumpAmountOut, raydiumParams.slippageBps);
|
|
56
|
+
return {
|
|
57
|
+
amountIn: BigInt(raydiumParams.amount),
|
|
58
|
+
amountOut: pumpAmountOut,
|
|
59
|
+
amountOutMin: pumpMinAmountOut,
|
|
60
|
+
slippage: raydiumParams.slippageBps / 100, // convert to percentage
|
|
61
|
+
};
|
|
62
|
+
} else {
|
|
63
|
+
// Then if output token is not SOL or WSOL, get a jupiter quote to swap SOL to output token
|
|
64
|
+
let jupiterQuoteRequest: JupiterQuoteParams = {
|
|
65
|
+
amount: pumpAmountOut,
|
|
66
|
+
tokenIn: WRAPPED_SOL_MINT_ADDRESS.toString(),
|
|
67
|
+
tokenOut: raydiumParams.outputMint,
|
|
68
|
+
swapMode: 'ExactIn',
|
|
69
|
+
slippageBps: raydiumParams.slippageBps, // Here we use all slippage to simplify
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const jupiterQuoter = new JupiterQuoteProvider();
|
|
73
|
+
const jupiterQuote = await jupiterQuoter.getQuote(jupiterQuoteRequest);
|
|
74
|
+
if (!jupiterQuote.quote) {
|
|
75
|
+
throw new Error('Jupiter quote failed');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
amountIn: BigInt(raydiumParams.amount),
|
|
80
|
+
amountOut: BigInt(jupiterQuote.quote.outAmount),
|
|
81
|
+
amountOutMin: BigInt(jupiterQuote.quote.otherAmountThreshold),
|
|
82
|
+
slippage: raydiumParams.slippageBps / 100, // convert to percentage
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
let [migratePoolData, _] = await getPumpFunMigratedPool(outputMint);
|
|
87
|
+
let [inputTokenAccount, outputTokenAccount] = await getPoolTokensInfo(
|
|
88
|
+
migratePoolData.poolBaseTokenAccount,
|
|
89
|
+
migratePoolData.poolQuoteTokenAccount,
|
|
90
|
+
);
|
|
91
|
+
let amountOutFromInput = BigInt(raydiumParams.amount);
|
|
92
|
+
let amountOutFromInputMin = applySlippage(BigInt(raydiumParams.amount), raydiumParams.slippageBps);
|
|
93
|
+
// Buying prebonded token with SOL
|
|
94
|
+
// If input token is not SOL or WSOL, get a jupiter quote to swap input token to SOL first
|
|
95
|
+
if (!isInputSol) {
|
|
96
|
+
let jupiterQuoteRequest: JupiterQuoteParams = {
|
|
97
|
+
amount: BigInt(raydiumParams.amount),
|
|
98
|
+
tokenIn: raydiumParams.inputMint,
|
|
99
|
+
tokenOut: WRAPPED_SOL_MINT_ADDRESS.toString(),
|
|
100
|
+
swapMode: 'ExactIn',
|
|
101
|
+
slippageBps: raydiumParams.slippageBps, // Use all slippage to simplify
|
|
102
|
+
};
|
|
103
|
+
const jupiterQuoter = new JupiterQuoteProvider();
|
|
104
|
+
const jupiterQuote = await jupiterQuoter.getQuote(jupiterQuoteRequest);
|
|
105
|
+
if (!jupiterQuote.quote) {
|
|
106
|
+
throw new Error('Jupiter quote failed');
|
|
107
|
+
}
|
|
108
|
+
amountOutFromInput = BigInt(jupiterQuote.quote.outAmount);
|
|
109
|
+
amountOutFromInputMin = BigInt(jupiterQuote.quote.otherAmountThreshold);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Slippage is already applied to amountOutFromInputMin
|
|
113
|
+
let pumpAmountOut = estimatePumpFunAmm(
|
|
114
|
+
inputTokenAccount,
|
|
115
|
+
outputTokenAccount,
|
|
116
|
+
globalConfig,
|
|
117
|
+
amountOutFromInput,
|
|
118
|
+
PreBondedTradeType.BuyExactIn,
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
let pumpAmountOutMin = estimatePumpFunAmm(
|
|
122
|
+
inputTokenAccount,
|
|
123
|
+
outputTokenAccount,
|
|
124
|
+
globalConfig,
|
|
125
|
+
amountOutFromInputMin,
|
|
126
|
+
PreBondedTradeType.BuyExactIn,
|
|
127
|
+
);
|
|
128
|
+
return {
|
|
129
|
+
amountIn: BigInt(raydiumParams.amount),
|
|
130
|
+
amountOut: pumpAmountOut,
|
|
131
|
+
amountOutMin: pumpAmountOutMin,
|
|
132
|
+
slippage: raydiumParams.slippageBps / 100, // convert to percentage
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
// Use Pump logic to get quote during prebonding
|
|
137
|
+
let globalAccount = await getPumpFunGlobalAccount();
|
|
138
|
+
if (isInputPrebonded) {
|
|
139
|
+
// Selling prebonded token for SOL
|
|
140
|
+
// First get sell estimation from PumpFun
|
|
141
|
+
const pumpAmountOut = estimatePumpFunPrebonded(
|
|
142
|
+
bondingCurve,
|
|
143
|
+
globalAccount[0],
|
|
144
|
+
BigInt(raydiumParams.amount),
|
|
145
|
+
PreBondedTradeType.SellExactIn,
|
|
146
|
+
);
|
|
147
|
+
console.log('pumpAmountOut', pumpAmountOut);
|
|
148
|
+
|
|
149
|
+
// Then if output token is SOL or WSOL, return directly
|
|
150
|
+
if (isOutputSol) {
|
|
151
|
+
// Apply slippage to amountOut
|
|
152
|
+
const pumpMinAmountOut = applySlippage(pumpAmountOut, raydiumParams.slippageBps);
|
|
153
|
+
return {
|
|
154
|
+
amountIn: BigInt(raydiumParams.amount),
|
|
155
|
+
amountOut: pumpAmountOut,
|
|
156
|
+
amountOutMin: pumpMinAmountOut,
|
|
157
|
+
slippage: raydiumParams.slippageBps / 100, // convert to percentage
|
|
158
|
+
};
|
|
159
|
+
} else {
|
|
160
|
+
// Then if output token is not SOL or WSOL, get a jupiter quote to swap SOL to output token
|
|
161
|
+
let jupiterQuoteRequest: JupiterQuoteParams = {
|
|
162
|
+
amount: pumpAmountOut,
|
|
163
|
+
tokenIn: WRAPPED_SOL_MINT_ADDRESS.toString(),
|
|
164
|
+
tokenOut: raydiumParams.outputMint,
|
|
165
|
+
swapMode: 'ExactIn',
|
|
166
|
+
slippageBps: raydiumParams.slippageBps, // Here we use all slippage to simplify
|
|
167
|
+
};
|
|
168
|
+
const jupiterQuoter = new JupiterQuoteProvider();
|
|
169
|
+
const jupiterQuote = await jupiterQuoter.getQuote(jupiterQuoteRequest);
|
|
170
|
+
if (!jupiterQuote.quote) {
|
|
171
|
+
throw new Error('Jupiter quote failed');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
amountIn: BigInt(raydiumParams.amount),
|
|
176
|
+
amountOut: BigInt(jupiterQuote.quote.outAmount),
|
|
177
|
+
amountOutMin: BigInt(jupiterQuote.quote.otherAmountThreshold),
|
|
178
|
+
slippage: raydiumParams.slippageBps / 100, // convert to percentage
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
let amountOutFromInput = BigInt(raydiumParams.amount);
|
|
183
|
+
let amountOutFromInputMin = applySlippage(BigInt(raydiumParams.amount), raydiumParams.slippageBps);
|
|
184
|
+
// Buying prebonded token with SOL
|
|
185
|
+
// If input token is not SOL or WSOL, get a jupiter quote to swap input token to SOL first
|
|
186
|
+
if (!isInputSol) {
|
|
187
|
+
let jupiterQuoteRequest: JupiterQuoteParams = {
|
|
188
|
+
amount: BigInt(raydiumParams.amount),
|
|
189
|
+
tokenIn: raydiumParams.inputMint,
|
|
190
|
+
tokenOut: WRAPPED_SOL_MINT_ADDRESS.toString(),
|
|
191
|
+
swapMode: 'ExactIn',
|
|
192
|
+
slippageBps: raydiumParams.slippageBps, // Use all slippage to simplify
|
|
193
|
+
};
|
|
194
|
+
const jupiterQuoter = new JupiterQuoteProvider();
|
|
195
|
+
const jupiterQuote = await jupiterQuoter.getQuote(jupiterQuoteRequest);
|
|
196
|
+
if (!jupiterQuote.quote) {
|
|
197
|
+
throw new Error('Jupiter quote failed');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Calculate minimum amount with slippage applied
|
|
201
|
+
amountOutFromInput = BigInt(jupiterQuote.quote.outAmount);
|
|
202
|
+
amountOutFromInputMin = BigInt(jupiterQuote.quote.otherAmountThreshold);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Slippage is already applied to amountOutFromInputMin
|
|
206
|
+
const amountOut = estimatePumpFunPrebonded(
|
|
207
|
+
bondingCurve,
|
|
208
|
+
globalAccount[0],
|
|
209
|
+
amountOutFromInput,
|
|
210
|
+
PreBondedTradeType.BuyExactIn,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
const minAmountOut = estimatePumpFunPrebonded(
|
|
214
|
+
bondingCurve,
|
|
215
|
+
globalAccount[0],
|
|
216
|
+
amountOutFromInputMin,
|
|
217
|
+
PreBondedTradeType.BuyExactIn,
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
amountIn: BigInt(raydiumParams.amount),
|
|
222
|
+
amountOut: amountOut,
|
|
223
|
+
amountOutMin: minAmountOut,
|
|
224
|
+
slippage: raydiumParams.slippageBps / 100, // convert to percentage
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async getTokensBondingCurve(
|
|
231
|
+
inputMint: Address,
|
|
232
|
+
outputMint: Address,
|
|
233
|
+
): Promise<[BondingCurve | null, BondingCurve | null]> {
|
|
234
|
+
let inputBondingCurve: BondingCurve | null = null;
|
|
235
|
+
let outputBondingCurve: BondingCurve | null = null;
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
inputBondingCurve = (await getPumpFunBondingCurve(inputMint))[0];
|
|
239
|
+
} catch (e) {
|
|
240
|
+
// No bonding curve for input token
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
outputBondingCurve = (await getPumpFunBondingCurve(outputMint))[0];
|
|
245
|
+
} catch (e) {
|
|
246
|
+
// No bonding curve for output token
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return [inputBondingCurve, outputBondingCurve];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private ensureSinglePrebondedToken(
|
|
253
|
+
inputBonding: BondingCurve | null,
|
|
254
|
+
outputBonding: BondingCurve | null,
|
|
255
|
+
): { bondingCurve: BondingCurve; isInputPrebonded: boolean } {
|
|
256
|
+
if (inputBonding && outputBonding) {
|
|
257
|
+
throw new Error('Both input and output tokens are prebonded, which is not supported');
|
|
258
|
+
}
|
|
259
|
+
if (!inputBonding && !outputBonding) {
|
|
260
|
+
throw new Error('Neither input nor output token is prebonded, which is not supported');
|
|
261
|
+
}
|
|
262
|
+
return inputBonding
|
|
263
|
+
? { bondingCurve: inputBonding, isInputPrebonded: true }
|
|
264
|
+
: { bondingCurve: outputBonding!, isInputPrebonded: false };
|
|
265
|
+
}
|
|
266
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getAddressDecoder,
|
|
3
|
+
getArrayDecoder,
|
|
4
|
+
getBooleanDecoder,
|
|
5
|
+
getStructDecoder,
|
|
6
|
+
getU16Decoder,
|
|
7
|
+
getU64Decoder,
|
|
8
|
+
getU8Decoder,
|
|
9
|
+
type Address,
|
|
10
|
+
type Decoder,
|
|
11
|
+
} from '@solana/kit';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Represents a Solana Token Account
|
|
15
|
+
*/
|
|
16
|
+
export interface TokenAccount {
|
|
17
|
+
mint: Address;
|
|
18
|
+
owner: Address;
|
|
19
|
+
amount: bigint;
|
|
20
|
+
delegate: Address | null;
|
|
21
|
+
state: number;
|
|
22
|
+
isNative: bigint | null;
|
|
23
|
+
delegatedAmount: bigint;
|
|
24
|
+
closeAuthority: Address | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* BondingCurve structure matching the Rust implementation
|
|
29
|
+
* This mirrors the on-chain account layout
|
|
30
|
+
*/
|
|
31
|
+
export interface BondingCurve {
|
|
32
|
+
virtualTokenReserves: bigint;
|
|
33
|
+
virtualSolReserves: bigint;
|
|
34
|
+
realTokenReserves: bigint;
|
|
35
|
+
realSolReserves: bigint;
|
|
36
|
+
tokenTotalSupply: bigint;
|
|
37
|
+
complete: boolean;
|
|
38
|
+
creator: Address;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* GlobalAccount structure matching the Rust implementation
|
|
43
|
+
* Contains global configuration for the PumpFun protocol
|
|
44
|
+
*/
|
|
45
|
+
export interface GlobalAccount {
|
|
46
|
+
initialized: boolean;
|
|
47
|
+
authority: Address;
|
|
48
|
+
feeRecipient: Address;
|
|
49
|
+
initialVirtualTokenReserves: bigint;
|
|
50
|
+
initialVirtualSolReserves: bigint;
|
|
51
|
+
initialRealTokenReserves: bigint;
|
|
52
|
+
tokenTotalSupply: bigint;
|
|
53
|
+
feeBasisPoints: bigint;
|
|
54
|
+
withdrawAuthority: Address;
|
|
55
|
+
enableMigrate: boolean;
|
|
56
|
+
poolMigrationFee: bigint;
|
|
57
|
+
creatorFeeBasisPoints: bigint;
|
|
58
|
+
feeRecipients: Address[]; // Array of 7 Pubkeys
|
|
59
|
+
setCreatorAuthority: Address;
|
|
60
|
+
adminSetCreatorAuthority: Address;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* GlobalConfig structure matching the Rust implementation
|
|
65
|
+
* This is the ACTUAL structure used by PumpFun on-chain
|
|
66
|
+
*/
|
|
67
|
+
export interface GlobalConfig {
|
|
68
|
+
admin: Address;
|
|
69
|
+
lpFeeBasisPoints: bigint;
|
|
70
|
+
protocolFeeBasisPoints: bigint;
|
|
71
|
+
disableFlags: number;
|
|
72
|
+
protocolFeeRecipients: Address[]; // Array of 8 Pubkeys
|
|
73
|
+
coinCreatorFeeBasisPoints: bigint;
|
|
74
|
+
adminSetCoinCreatorAuthority: Address;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* PumpPoolData for migrated AMM pools
|
|
79
|
+
*/
|
|
80
|
+
export interface PumpPoolData {
|
|
81
|
+
poolBump: number;
|
|
82
|
+
index: number;
|
|
83
|
+
creator: Address;
|
|
84
|
+
baseMint: Address;
|
|
85
|
+
quoteMint: Address;
|
|
86
|
+
lpMint: Address;
|
|
87
|
+
poolBaseTokenAccount: Address;
|
|
88
|
+
poolQuoteTokenAccount: Address;
|
|
89
|
+
lpSupply: bigint;
|
|
90
|
+
coinCreator: Address;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Creates a decoder for PumpPoolData
|
|
95
|
+
*/
|
|
96
|
+
export function getPumpPoolDataDecoder(): Decoder<PumpPoolData> {
|
|
97
|
+
return getStructDecoder([
|
|
98
|
+
['poolBump', getU8Decoder()],
|
|
99
|
+
['index', getU16Decoder()],
|
|
100
|
+
['creator', getAddressDecoder()],
|
|
101
|
+
['baseMint', getAddressDecoder()],
|
|
102
|
+
['quoteMint', getAddressDecoder()],
|
|
103
|
+
['lpMint', getAddressDecoder()],
|
|
104
|
+
['poolBaseTokenAccount', getAddressDecoder()],
|
|
105
|
+
['poolQuoteTokenAccount', getAddressDecoder()],
|
|
106
|
+
['lpSupply', getU64Decoder()],
|
|
107
|
+
['coinCreator', getAddressDecoder()],
|
|
108
|
+
]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Creates a decoder for the BondingCurve account structure
|
|
113
|
+
* The layout must match exactly the Rust struct serialization order
|
|
114
|
+
*/
|
|
115
|
+
export function getBondingCurveDecoder(): Decoder<BondingCurve> {
|
|
116
|
+
return getStructDecoder([
|
|
117
|
+
['virtualTokenReserves', getU64Decoder()],
|
|
118
|
+
['virtualSolReserves', getU64Decoder()],
|
|
119
|
+
['realTokenReserves', getU64Decoder()],
|
|
120
|
+
['realSolReserves', getU64Decoder()],
|
|
121
|
+
['tokenTotalSupply', getU64Decoder()],
|
|
122
|
+
['complete', getBooleanDecoder()],
|
|
123
|
+
['creator', getAddressDecoder()],
|
|
124
|
+
]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Creates a decoder for the GlobalAccount structure
|
|
129
|
+
*/
|
|
130
|
+
export function getGlobalAccountDecoder(): Decoder<GlobalAccount> {
|
|
131
|
+
return getStructDecoder([
|
|
132
|
+
['initialized', getBooleanDecoder()],
|
|
133
|
+
['authority', getAddressDecoder()],
|
|
134
|
+
['feeRecipient', getAddressDecoder()],
|
|
135
|
+
['initialVirtualTokenReserves', getU64Decoder()],
|
|
136
|
+
['initialVirtualSolReserves', getU64Decoder()],
|
|
137
|
+
['initialRealTokenReserves', getU64Decoder()],
|
|
138
|
+
['tokenTotalSupply', getU64Decoder()],
|
|
139
|
+
['feeBasisPoints', getU64Decoder()],
|
|
140
|
+
['withdrawAuthority', getAddressDecoder()],
|
|
141
|
+
['enableMigrate', getBooleanDecoder()],
|
|
142
|
+
['poolMigrationFee', getU64Decoder()],
|
|
143
|
+
['creatorFeeBasisPoints', getU64Decoder()],
|
|
144
|
+
['feeRecipients', getArrayDecoder(getAddressDecoder(), { size: 7 })], // Fixed array of 7 Pubkeys
|
|
145
|
+
['setCreatorAuthority', getAddressDecoder()],
|
|
146
|
+
['adminSetCreatorAuthority', getAddressDecoder()],
|
|
147
|
+
]);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Creates a decoder for the GlobalConfig structure
|
|
152
|
+
*/
|
|
153
|
+
export function getGlobalConfigDecoder(): Decoder<GlobalConfig> {
|
|
154
|
+
return getStructDecoder([
|
|
155
|
+
['admin', getAddressDecoder()],
|
|
156
|
+
['lpFeeBasisPoints', getU64Decoder()],
|
|
157
|
+
['protocolFeeBasisPoints', getU64Decoder()],
|
|
158
|
+
['disableFlags', getU8Decoder()],
|
|
159
|
+
['protocolFeeRecipients', getArrayDecoder(getAddressDecoder(), { size: 8 })], // Fixed array of 8 Pubkeys
|
|
160
|
+
['coinCreatorFeeBasisPoints', getU64Decoder()],
|
|
161
|
+
['adminSetCoinCreatorAuthority', getAddressDecoder()],
|
|
162
|
+
]);
|
|
163
|
+
}
|