@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.
Files changed (121) hide show
  1. package/README.md +3 -19
  2. package/dist/esm/chains.js +3 -0
  3. package/dist/esm/chains.js.map +1 -1
  4. package/dist/esm/constants.js +13 -4
  5. package/dist/esm/constants.js.map +1 -1
  6. package/dist/esm/core/evm/chain-provider.js +2 -1
  7. package/dist/esm/core/evm/chain-provider.js.map +1 -1
  8. package/dist/esm/core/evm/cross-chain-limit-order.js +1 -1
  9. package/dist/esm/core/evm/cross-chain-limit-order.js.map +1 -1
  10. package/dist/esm/core/evm/permit2-signature-transfer.js +15 -0
  11. package/dist/esm/core/evm/permit2-signature-transfer.js.map +1 -0
  12. package/dist/esm/core/evm/single-chain-dca-order.js +1 -1
  13. package/dist/esm/core/evm/single-chain-dca-order.js.map +1 -1
  14. package/dist/esm/core/evm/single-chain-limit-order.js +1 -1
  15. package/dist/esm/core/evm/single-chain-limit-order.js.map +1 -1
  16. package/dist/esm/core/solana/dca/single-chain-dca-order.js +3 -2
  17. package/dist/esm/core/solana/dca/single-chain-dca-order.js.map +1 -1
  18. package/dist/esm/core/solana/dca/single-chain-limit-order.js +3 -2
  19. package/dist/esm/core/solana/dca/single-chain-limit-order.js.map +1 -1
  20. package/dist/esm/core/solana/utils.js +14 -4
  21. package/dist/esm/core/solana/utils.js.map +1 -1
  22. package/dist/esm/core/sui/single-chain-dca-order.js +2 -1
  23. package/dist/esm/core/sui/single-chain-dca-order.js.map +1 -1
  24. package/dist/esm/core/sui/single-chain-limit-order.js +2 -1
  25. package/dist/esm/core/sui/single-chain-limit-order.js.map +1 -1
  26. package/dist/esm/index.js +6 -3
  27. package/dist/esm/index.js.map +1 -1
  28. package/dist/esm/types/token-list.js +2 -0
  29. package/dist/esm/types/token-list.js.map +1 -0
  30. package/dist/esm/utils/defillama.js +1 -0
  31. package/dist/esm/utils/defillama.js.map +1 -1
  32. package/dist/esm/utils/quote/aggregator.js +158 -42
  33. package/dist/esm/utils/quote/aggregator.js.map +1 -1
  34. package/dist/esm/utils/quote/paraswap.js +1 -0
  35. package/dist/esm/utils/quote/paraswap.js.map +1 -1
  36. package/dist/esm/utils/quote/pumpfun/estimations.js +169 -0
  37. package/dist/esm/utils/quote/pumpfun/estimations.js.map +1 -0
  38. package/dist/esm/utils/quote/pumpfun/estimations_amm.js +130 -0
  39. package/dist/esm/utils/quote/pumpfun/estimations_amm.js.map +1 -0
  40. package/dist/esm/utils/quote/pumpfun/index.js +198 -0
  41. package/dist/esm/utils/quote/pumpfun/index.js.map +1 -0
  42. package/dist/esm/utils/quote/pumpfun/models.js +70 -0
  43. package/dist/esm/utils/quote/pumpfun/models.js.map +1 -0
  44. package/dist/esm/utils/quote/pumpfun/utils.js +269 -0
  45. package/dist/esm/utils/quote/pumpfun/utils.js.map +1 -0
  46. package/dist/esm/utils/quote/raydium.js +336 -0
  47. package/dist/esm/utils/quote/raydium.js.map +1 -0
  48. package/dist/esm/utils/quote/stablecoins-tokens.js +1 -0
  49. package/dist/esm/utils/quote/stablecoins-tokens.js.map +1 -1
  50. package/dist/esm/utils/quote/utils.js +8 -0
  51. package/dist/esm/utils/quote/utils.js.map +1 -0
  52. package/dist/esm/utils/tokens/index.js +37 -0
  53. package/dist/esm/utils/tokens/index.js.map +1 -0
  54. package/dist/types/chains.d.ts +4 -2
  55. package/dist/types/chains.d.ts.map +1 -1
  56. package/dist/types/constants.d.ts +5 -4
  57. package/dist/types/constants.d.ts.map +1 -1
  58. package/dist/types/core/evm/chain-provider.d.ts.map +1 -1
  59. package/dist/types/core/evm/permit2-signature-transfer.d.ts +3 -0
  60. package/dist/types/core/evm/permit2-signature-transfer.d.ts.map +1 -0
  61. package/dist/types/core/solana/dca/single-chain-dca-order.d.ts +1 -2
  62. package/dist/types/core/solana/dca/single-chain-dca-order.d.ts.map +1 -1
  63. package/dist/types/core/solana/dca/single-chain-limit-order.d.ts +1 -2
  64. package/dist/types/core/solana/dca/single-chain-limit-order.d.ts.map +1 -1
  65. package/dist/types/core/solana/utils.d.ts +2 -0
  66. package/dist/types/core/solana/utils.d.ts.map +1 -1
  67. package/dist/types/core/sui/single-chain-dca-order.d.ts.map +1 -1
  68. package/dist/types/core/sui/single-chain-limit-order.d.ts.map +1 -1
  69. package/dist/types/index.d.ts +5 -3
  70. package/dist/types/index.d.ts.map +1 -1
  71. package/dist/types/types/token-list.d.ts +30 -0
  72. package/dist/types/types/token-list.d.ts.map +1 -0
  73. package/dist/types/utils/defillama.d.ts +1 -0
  74. package/dist/types/utils/defillama.d.ts.map +1 -1
  75. package/dist/types/utils/quote/aggregator.d.ts +42 -5
  76. package/dist/types/utils/quote/aggregator.d.ts.map +1 -1
  77. package/dist/types/utils/quote/paraswap.d.ts.map +1 -1
  78. package/dist/types/utils/quote/pumpfun/estimations.d.ts +30 -0
  79. package/dist/types/utils/quote/pumpfun/estimations.d.ts.map +1 -0
  80. package/dist/types/utils/quote/pumpfun/estimations_amm.d.ts +49 -0
  81. package/dist/types/utils/quote/pumpfun/estimations_amm.d.ts.map +1 -0
  82. package/dist/types/utils/quote/pumpfun/index.d.ts +10 -0
  83. package/dist/types/utils/quote/pumpfun/index.d.ts.map +1 -0
  84. package/dist/types/utils/quote/pumpfun/models.d.ts +94 -0
  85. package/dist/types/utils/quote/pumpfun/models.d.ts.map +1 -0
  86. package/dist/types/utils/quote/pumpfun/utils.d.ts +50 -0
  87. package/dist/types/utils/quote/pumpfun/utils.d.ts.map +1 -0
  88. package/dist/types/utils/quote/raydium.d.ts +98 -0
  89. package/dist/types/utils/quote/raydium.d.ts.map +1 -0
  90. package/dist/types/utils/quote/stablecoins-tokens.d.ts.map +1 -1
  91. package/dist/types/utils/quote/utils.d.ts +5 -0
  92. package/dist/types/utils/quote/utils.d.ts.map +1 -0
  93. package/dist/types/utils/tokens/index.d.ts +20 -0
  94. package/dist/types/utils/tokens/index.d.ts.map +1 -0
  95. package/package.json +20 -18
  96. package/src/chains.ts +3 -0
  97. package/src/constants.ts +16 -6
  98. package/src/core/evm/chain-provider.ts +2 -1
  99. package/src/core/evm/cross-chain-limit-order.ts +1 -1
  100. package/src/core/evm/permit2-signature-transfer.ts +15 -0
  101. package/src/core/evm/single-chain-dca-order.ts +1 -1
  102. package/src/core/evm/single-chain-limit-order.ts +1 -1
  103. package/src/core/solana/dca/single-chain-dca-order.ts +3 -3
  104. package/src/core/solana/dca/single-chain-limit-order.ts +3 -2
  105. package/src/core/solana/utils.ts +24 -12
  106. package/src/core/sui/single-chain-dca-order.ts +2 -2
  107. package/src/core/sui/single-chain-limit-order.ts +2 -2
  108. package/src/index.ts +14 -4
  109. package/src/types/token-list.ts +35 -0
  110. package/src/utils/defillama.ts +1 -0
  111. package/src/utils/quote/aggregator.ts +210 -55
  112. package/src/utils/quote/paraswap.ts +1 -0
  113. package/src/utils/quote/pumpfun/estimations.ts +206 -0
  114. package/src/utils/quote/pumpfun/estimations_amm.ts +165 -0
  115. package/src/utils/quote/pumpfun/index.ts +266 -0
  116. package/src/utils/quote/pumpfun/models.ts +163 -0
  117. package/src/utils/quote/pumpfun/utils.ts +343 -0
  118. package/src/utils/quote/raydium.ts +506 -0
  119. package/src/utils/quote/stablecoins-tokens.ts +1 -0
  120. package/src/utils/quote/utils.ts +8 -0
  121. 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
+ }