@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
@@ -5,8 +5,13 @@ import { ParaswapQuoteProvider, type SwapEstimateResult } from './paraswap.js';
5
5
  import { CROSS_CHAIN_TOKENS } from './stablecoins-tokens.js';
6
6
  import { AftermathQuoteProvider } from './aftermath.js';
7
7
  import type { RouterCompleteTradeRoute } from 'aftermath-ts-sdk';
8
- import { calculateAmounts } from '../defillama.js';
9
8
  import { LiquidSwapQuoteProvider, type LiquidSwapQuoteResponse } from './liquidswap.js';
9
+ import { RaydiumQuoteProvider, type RaydiumPumpQuoteParams, type RaydiumQuoteResponse } from './raydium.js';
10
+ import { PumpFunQuoteProvider } from './pumpfun/index.js';
11
+
12
+ const compareAddresses = (firstAddress?: string, secondAddress?: string): boolean => {
13
+ return !!firstAddress && !!secondAddress && firstAddress.toLowerCase() === secondAddress.toLowerCase();
14
+ };
10
15
 
11
16
  type SingleChainQuoteParams = {
12
17
  chainId: ChainID;
@@ -14,13 +19,15 @@ type SingleChainQuoteParams = {
14
19
  tokenIn: string;
15
20
  tokenOut: string;
16
21
  slippageBps?: number;
22
+ provider?: RouteProvider; // Optional provider override
17
23
  };
18
24
 
19
- export type RouteProvider = 'paraswap' | 'jupiter' | 'aftermath' | 'liquidswap';
25
+ export type RouteProvider = 'paraswap' | 'jupiter' | 'aftermath' | 'liquidswap' | 'raydium' | 'pumpfun';
20
26
 
21
27
  export type Quote = {
22
28
  amountIn: bigint;
23
29
  amountOut: bigint;
30
+ amountOutMin?: bigint; // Optional, for quotes that provide a minimum guaranteed output
24
31
  amountOutUsd: number;
25
32
  amountInUsd: number;
26
33
  slippage: number;
@@ -29,6 +36,13 @@ export type Quote = {
29
36
  rawQuote: any;
30
37
  };
31
38
 
39
+ export type SimpleQuote = {
40
+ amountIn: bigint;
41
+ amountOut: bigint;
42
+ amountOutMin?: bigint;
43
+ slippage: number;
44
+ };
45
+
32
46
  export type IntentsQuoteParams = {
33
47
  sourceChainId: ChainID;
34
48
  destChainId: ChainID;
@@ -37,56 +51,85 @@ export type IntentsQuoteParams = {
37
51
  tokenOut: string;
38
52
  };
39
53
 
40
- export type QuoteResponse = {
41
- amountInUsd: number;
42
- estimatedAmountInAsMinStablecoinAmount: bigint;
54
+ export type QuoteResponseInternal = {
55
+ amountInUsd: number
56
+ estimatedAmountInAsMinStablecoinAmount: bigint
43
57
 
44
- estimatedAmountOut: bigint;
45
- estimatedAmountOutUsd: number;
58
+ estimatedAmountOut: bigint
59
+ estimatedAmountOutUsd: number
46
60
 
47
- estimatedAmountOutReduced: bigint;
48
- estimatedAmountOutUsdReduced: number;
49
- };
61
+ estimatedAmountOutReduced: bigint
62
+ estimatedAmountOutUsdReduced: number
63
+ }
50
64
 
51
- export class QuoteProvider {
52
- private static readonly DEFAULT_BRIDGE_TOKEN = 'USDC';
53
- private static readonly DEFAULT_BRIDGE_TOKEN_DECIMALS = 6;
65
+ // Public response with metadata and derived fields
66
+ export type QuoteResponse = QuoteResponseInternal & {
54
67
 
55
- public static async getQuoteFromDefillama(params: IntentsQuoteParams): Promise<QuoteResponse> {
56
- const defillamaQuote = await calculateAmounts({
57
- amountIn: params.amount,
58
- tokenIn: params.tokenIn,
59
- tokenOut: params.tokenOut,
60
- destChainId: params.destChainId,
61
- srcChainId: params.sourceChainId,
62
- });
63
68
 
64
- return {
65
- amountInUsd: 0,
66
- estimatedAmountOutUsd: 0,
67
- estimatedAmountOutUsdReduced: 0,
69
+ /** Metadata for source token */
70
+ tokenIn: {
71
+ address: string
72
+ chainId: ChainID
73
+ }
68
74
 
69
- estimatedAmountInAsMinStablecoinAmount: defillamaQuote.minStablecoinsAmount,
70
- estimatedAmountOut: defillamaQuote.amountOut,
71
- estimatedAmountOutReduced: defillamaQuote.amountOut,
72
- };
75
+ /** Metadata for destination token */
76
+ tokenOut: {
77
+ address: string
78
+ chainId: ChainID
73
79
  }
80
+ }
81
+
82
+
83
+ export class QuoteProvider {
84
+ private static readonly DEFAULT_BRIDGE_TOKEN = 'USDC';
85
+ private static readonly DEFAULT_BRIDGE_TOKEN_DECIMALS = 6;
74
86
 
75
- public static async getQuoteWithDefillamaFallback(params: IntentsQuoteParams): Promise<QuoteResponse> {
87
+ public static async getQuote(params: IntentsQuoteParams): Promise<QuoteResponse> {
76
88
  try {
77
89
  const quote = await this.getQuoteFromRouters(params);
78
- return quote;
90
+ // Adapt QuoteResponseInternal to QuoteResponse by adding tokenIn and tokenOut metadata fields
91
+ return {
92
+ ...quote,
93
+ tokenIn: {
94
+ address: params.tokenIn,
95
+ chainId: params.sourceChainId
96
+ },
97
+ tokenOut: {
98
+ address: params.tokenOut,
99
+ chainId: params.destChainId
100
+ }
101
+ };
79
102
  } catch (e) {
80
- const quote = await this.getQuoteFromDefillama(params);
81
- return quote;
103
+ console.error('Error getting quote from routers:', e);
104
+ throw new Error('Failed to get quote from routers');
82
105
  }
83
106
  }
84
107
 
85
- public static async getQuote(params: IntentsQuoteParams): Promise<QuoteResponse> {
86
- return this.getQuoteWithDefillamaFallback(params);
108
+ /**
109
+ * Get a quote for Solana with explicit provider selection
110
+ * @param params - Quote parameters including provider preference
111
+ * @returns Quote response
112
+ */
113
+ public static async getSolanaQuote(params: {
114
+ amount: bigint;
115
+ tokenIn: string;
116
+ tokenOut: string;
117
+ slippageBps?: number;
118
+ provider?: 'jupiter' | 'raydium' | 'pumpfun';
119
+ }): Promise<Quote> {
120
+ const singleChainParams: SingleChainQuoteParams = {
121
+ chainId: ChainID.Solana,
122
+ amount: params.amount,
123
+ tokenIn: params.tokenIn,
124
+ tokenOut: params.tokenOut,
125
+ slippageBps: params.slippageBps,
126
+ provider: params.provider,
127
+ };
128
+
129
+ return this.getSingleChainQuote(singleChainParams);
87
130
  }
88
131
 
89
- private static async getQuoteFromRouters(params: IntentsQuoteParams): Promise<QuoteResponse> {
132
+ private static async getQuoteFromRouters(params: IntentsQuoteParams): Promise<QuoteResponseInternal> {
90
133
  const bridgeTokenSymbol = QuoteProvider.DEFAULT_BRIDGE_TOKEN;
91
134
 
92
135
  const sourceBridgeToken = CROSS_CHAIN_TOKENS[bridgeTokenSymbol]?.[params.sourceChainId] || '';
@@ -110,15 +153,14 @@ export class QuoteProvider {
110
153
 
111
154
  const destQuote = await this.getSingleChainQuote(destSingleChainQuoteParams);
112
155
 
113
- // Use weighted reduction instead of fixed percentage
114
- const weightedReductionFactor = this.calculateWeightedReductionFactorForUsd(destQuote.amountOutUsd);
156
+ // // Use weighted reduction instead of fixed percentage
157
+ // const weightedReductionFactor = this.calculateWeightedReductionFactorForUsd(destQuote.amountOutUsd);
158
+ // const weightedReductionFactor = 0.999;
115
159
 
116
- const estimatedAmountOutReduced = BigInt(Math.floor(Number(destQuote.amountOut) * weightedReductionFactor));
117
- const estimatedAmountOutUsdReduced = destQuote.amountOutUsd * weightedReductionFactor;
160
+ const estimatedAmountOutUsdReduced = destQuote.amountOutUsd;
118
161
 
119
- const reducedAmountInUsd = this.calculateWeightedReductionFactorForUsd(sourceQuote.amountInUsd);
120
162
  const estimatedAmountInAsMinStablecoinAmount = BigInt(
121
- Math.floor(reducedAmountInUsd * 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS),
163
+ Math.floor(sourceQuote.amountInUsd * 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS),
122
164
  );
123
165
 
124
166
  return {
@@ -126,16 +168,16 @@ export class QuoteProvider {
126
168
  estimatedAmountOutUsd: destQuote.amountOutUsd,
127
169
  amountInUsd: sourceQuote.amountInUsd,
128
170
  estimatedAmountInAsMinStablecoinAmount,
129
- estimatedAmountOutReduced,
171
+ estimatedAmountOutReduced: destQuote.amountOutMin || destQuote.amountOut,
130
172
  estimatedAmountOutUsdReduced,
131
173
  };
132
174
  }
133
175
 
134
176
  public static async getSingleChainQuote(params: SingleChainQuoteParams): Promise<Quote> {
135
- if (params.tokenIn.toLowerCase() === params.tokenOut.toLowerCase()) {
177
+ if (compareAddresses(params.tokenIn, params.tokenOut)) {
136
178
  let amountUsd = 0;
137
179
 
138
- if (params.tokenIn.toLowerCase() === CROSS_CHAIN_TOKENS.USDC![params.chainId].toLowerCase()) {
180
+ if (compareAddresses(params.tokenIn, CROSS_CHAIN_TOKENS.USDC![params.chainId])) {
139
181
  amountUsd = Number(params.amount) / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS;
140
182
  }
141
183
 
@@ -146,7 +188,7 @@ export class QuoteProvider {
146
188
  amountInUsd: amountUsd,
147
189
  slippage: 0,
148
190
  priceImpact: 0,
149
- provider: getProviderNameByChainId(params.chainId),
191
+ provider: params.provider || getProviderNameByChainId(params.chainId),
150
192
  rawQuote: null,
151
193
  };
152
194
  }
@@ -157,8 +199,38 @@ export class QuoteProvider {
157
199
  }
158
200
 
159
201
  if (params.chainId === ChainID.Solana) {
160
- const jupiterQuote = await QuoteProvider.getJupiterQuote(params);
161
- return QuoteProvider.transformJupiterQuote(jupiterQuote);
202
+ // Check if provider is explicitly specified
203
+ if (params.provider === 'jupiter') {
204
+ const jupiterQuote = await QuoteProvider.getJupiterQuote(params);
205
+ return QuoteProvider.transformJupiterQuote(jupiterQuote);
206
+ } else if (params.provider === 'raydium') {
207
+ const raydiumQuote = await QuoteProvider.getRaydiumQuote(params);
208
+ return raydiumQuote;
209
+ } else if (params.provider === 'pumpfun') {
210
+ const pumpfunQuote = await QuoteProvider.getPumpFunQuote(params);
211
+ return pumpfunQuote;
212
+ } else {
213
+ // Default: Try Jupiter first, fallback to Raydium and Pump if it fails
214
+ try {
215
+ const jupiterQuote = await QuoteProvider.getJupiterQuote(params);
216
+ if (!jupiterQuote.quote) {
217
+ throw new Error('Jupiter quote failed');
218
+ }
219
+
220
+ const transformedQuote = QuoteProvider.transformJupiterQuote(jupiterQuote);
221
+ return transformedQuote;
222
+ } catch (error) {
223
+ console.warn('Jupiter quote failed, falling back to Raydium:', error);
224
+ // Try Raydium first, then Pumpfun
225
+ try {
226
+ const raydiumQuote = await QuoteProvider.getRaydiumQuote(params);
227
+ return raydiumQuote;
228
+ } catch {
229
+ const pumpfunQuote = await QuoteProvider.getPumpFunQuote(params);
230
+ return pumpfunQuote;
231
+ }
232
+ }
233
+ }
162
234
  }
163
235
 
164
236
  if (params.chainId === ChainID.Sui) {
@@ -223,7 +295,7 @@ export class QuoteProvider {
223
295
  tokenIn: params.tokenIn,
224
296
  tokenOut: params.tokenOut,
225
297
  swapMode: 'ExactIn',
226
- slippageBps: params.slippageBps,
298
+ slippageBps: params.slippageBps ? params.slippageBps : 20, // Default 0.2% slippage
227
299
  });
228
300
  }
229
301
 
@@ -248,6 +320,36 @@ export class QuoteProvider {
248
320
  });
249
321
  }
250
322
 
323
+ //TODO: For Raydium and Pump we are applying slippage twice if there are 2 swaps (e.g., USDC -> SOL -> Token), should we just apply half and half on each swap?
324
+
325
+ protected static async getRaydiumQuote(params: SingleChainQuoteParams): Promise<Quote> {
326
+ const raydiumQuoter = new RaydiumQuoteProvider();
327
+ let request: RaydiumPumpQuoteParams = {
328
+ inputMint: params.tokenIn,
329
+ outputMint: params.tokenOut,
330
+ amount: params.amount.toString(),
331
+ slippageBps: params.slippageBps || 20, // Default 0.2% slippage
332
+ };
333
+ try {
334
+ return this.transformRaydiumQuote(await raydiumQuoter.getQuote(request));
335
+ } catch (e) {
336
+ console.log('Raydium AMM quote cannot find a route, trying with prebonded raydium');
337
+ return simpleQuoteToQuote(await raydiumQuoter.getQuotePrebonded(request), 'raydium');
338
+ }
339
+ }
340
+
341
+ protected static async getPumpFunQuote(params: SingleChainQuoteParams): Promise<Quote> {
342
+ const pumpfunQuoter = new PumpFunQuoteProvider();
343
+
344
+ let request: RaydiumPumpQuoteParams = {
345
+ inputMint: params.tokenIn,
346
+ outputMint: params.tokenOut,
347
+ amount: params.amount.toString(),
348
+ slippageBps: params.slippageBps || 20, // Default 0.2% slippage
349
+ };
350
+ return simpleQuoteToQuote(await pumpfunQuoter.getQuote(request), 'pumpfun');
351
+ }
352
+
251
353
  private static transformAftermathQuote({
252
354
  quote,
253
355
  request,
@@ -257,11 +359,11 @@ export class QuoteProvider {
257
359
  }): Quote {
258
360
  let amountUsd = 0;
259
361
 
260
- if (request.tokenIn === CROSS_CHAIN_TOKENS.USDC![request.chainId]) {
362
+ if (compareAddresses(request.tokenIn, CROSS_CHAIN_TOKENS.USDC![request.chainId])) {
261
363
  amountUsd = Number(quote.coinIn.amount) / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS;
262
364
  }
263
365
 
264
- if (request.tokenOut === CROSS_CHAIN_TOKENS.USDC![request.chainId]) {
366
+ if (compareAddresses(request.tokenOut, CROSS_CHAIN_TOKENS.USDC![request.chainId])) {
265
367
  amountUsd = Number(quote.coinOut.amount) / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS;
266
368
  }
267
369
 
@@ -290,23 +392,26 @@ export class QuoteProvider {
290
392
  };
291
393
  }
292
394
 
293
- private static transformJupiterQuote({ quote }: { quote: JupiterQuoteResponse }): Quote {
395
+ public static transformJupiterQuote({ quote }: { quote: JupiterQuoteResponse }): Quote {
294
396
  const amountIn = Number(quote.inAmount);
295
397
  const amountOut = Number(quote.outAmount);
398
+ const amountOutMin = Number(quote.otherAmountThreshold);
399
+
296
400
  let amountUsd = 0;
297
401
  const priceImpact = quote.priceImpactPct ? Number(quote.priceImpactPct) * 100 : 0;
298
402
 
299
- if (quote.outputMint === CROSS_CHAIN_TOKENS.USDC![ChainID.Solana]) {
403
+ if (compareAddresses(quote.outputMint, CROSS_CHAIN_TOKENS.USDC![ChainID.Solana])) {
300
404
  amountUsd = amountOut / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS;
301
405
  }
302
406
 
303
- if (quote.inputMint === CROSS_CHAIN_TOKENS.USDC![ChainID.Solana]) {
407
+ if (compareAddresses(quote.inputMint, CROSS_CHAIN_TOKENS.USDC![ChainID.Solana])) {
304
408
  amountUsd = amountIn / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS;
305
409
  }
306
410
 
307
411
  return {
308
412
  amountIn: BigInt(amountIn),
309
413
  amountOut: BigInt(amountOut),
414
+ amountOutMin: BigInt(amountOutMin),
310
415
  amountOutUsd: amountUsd,
311
416
  amountInUsd: amountUsd,
312
417
  slippage: quote.slippageBps ? quote.slippageBps / 100 : 0,
@@ -315,6 +420,33 @@ export class QuoteProvider {
315
420
  rawQuote: quote,
316
421
  };
317
422
  }
423
+
424
+ private static transformRaydiumQuote(raydiumQuote: RaydiumQuoteResponse): Quote {
425
+ const amountIn = BigInt(raydiumQuote.data.inAmount);
426
+ const amountOut = BigInt(raydiumQuote.data.outAmount);
427
+
428
+ let amountUsd = 0;
429
+ const priceImpact = Number(raydiumQuote.data.priceImpactPct) * 100;
430
+
431
+ if (compareAddresses(raydiumQuote.data.outputMint, CROSS_CHAIN_TOKENS.USDC![ChainID.Solana])) {
432
+ amountUsd = Number(amountOut) / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS;
433
+ }
434
+
435
+ if (compareAddresses(raydiumQuote.data.inputMint, CROSS_CHAIN_TOKENS.USDC![ChainID.Solana])) {
436
+ amountUsd = Number(amountIn) / 10 ** this.DEFAULT_BRIDGE_TOKEN_DECIMALS;
437
+ }
438
+
439
+ return {
440
+ amountIn,
441
+ amountOut,
442
+ amountOutUsd: amountUsd,
443
+ amountInUsd: amountUsd,
444
+ slippage: raydiumQuote.data.slippageBps / 100,
445
+ priceImpact,
446
+ provider: 'raydium',
447
+ rawQuote: raydiumQuote,
448
+ };
449
+ }
318
450
  }
319
451
 
320
452
  function getProviderNameByChainId(chainId: ChainID): RouteProvider {
@@ -322,10 +454,33 @@ function getProviderNameByChainId(chainId: ChainID): RouteProvider {
322
454
  case ChainID.Sui:
323
455
  return 'aftermath';
324
456
  case ChainID.Solana:
325
- return 'jupiter';
457
+ return 'jupiter'; // Primary provider, with Raydium and Pumpfun as fallbacks
326
458
  case ChainID.Hyperliquid:
327
459
  return 'liquidswap';
328
460
  default:
329
461
  return 'paraswap';
330
462
  }
331
463
  }
464
+
465
+ export function quoteToSimpleQuote(quote: Quote): SimpleQuote {
466
+ return {
467
+ amountIn: quote.amountIn,
468
+ amountOut: quote.amountOut,
469
+ amountOutMin: quote.amountOutMin,
470
+ slippage: quote.slippage,
471
+ };
472
+ }
473
+
474
+ export function simpleQuoteToQuote(simpleQuote: SimpleQuote, provider: RouteProvider): Quote {
475
+ return {
476
+ amountIn: simpleQuote.amountIn,
477
+ amountOut: simpleQuote.amountOut,
478
+ amountOutMin: simpleQuote.amountOutMin,
479
+ slippage: simpleQuote.slippage,
480
+ amountInUsd: 0,
481
+ amountOutUsd: 0,
482
+ priceImpact: 0,
483
+ provider,
484
+ rawQuote: simpleQuote,
485
+ };
486
+ }
@@ -10,6 +10,7 @@ const rpcProvidersUrl: Record<SupportedEvmChain, string> = {
10
10
  [ChainID.Optimism]: 'https://optimism-rpc.publicnode.com',
11
11
  [ChainID.Base]: 'https://base-rpc.publicnode.com',
12
12
  [ChainID.Hyperliquid]: 'https://rpc.hyperliquid.xyz/evm',
13
+ [ChainID.BSC]: 'https://bsc-dataseed.ninicoin.io',
13
14
  };
14
15
 
15
16
  export type ParaswapQuoteRequestParams = {
@@ -0,0 +1,206 @@
1
+ import type { BondingCurve, GlobalAccount } from './models.js';
2
+
3
+ export const BPS_DEN = 10000n;
4
+
5
+ export enum PreBondedTradeType {
6
+ BuyExactIn = 'BuyExactIn',
7
+ BuyExactOut = 'BuyExactOut',
8
+ SellExactIn = 'SellExactIn',
9
+ SellExactOut = 'SellExactOut',
10
+ }
11
+
12
+ class PreBondingError extends Error {
13
+ constructor(message: string) {
14
+ super(message);
15
+ this.name = 'PreBondingError';
16
+ }
17
+ }
18
+
19
+ /**
20
+ * Calculate output amount for constant product AMM: (amount_in * y_out) / (x_in + amount_in)
21
+ */
22
+ function cpAmountOut(amountIn: bigint, xIn: bigint, yOut: bigint): bigint {
23
+ if (xIn === 0n || yOut === 0n) {
24
+ throw new PreBondingError('Zero value for reserve');
25
+ }
26
+
27
+ const num = amountIn * yOut;
28
+ const den = xIn + amountIn;
29
+
30
+ return num / den;
31
+ }
32
+
33
+ /**
34
+ * Buy exact SOL in, get tokens out.
35
+ * Fee is deducted from SOL before the swap calculation.
36
+ */
37
+ export function buyExactIn(curve: BondingCurve, solIn: bigint, feeBps: bigint): bigint {
38
+ if (curve.complete) {
39
+ throw new PreBondingError(`Pool not in fundraising state: ${JSON.stringify(curve)}`);
40
+ }
41
+ if (solIn === 0n) {
42
+ return 0n;
43
+ }
44
+
45
+ const vSol = curve.virtualSolReserves;
46
+ const vTok = curve.virtualTokenReserves;
47
+
48
+ // Deduct protocol + creator fee from input SOL
49
+ const fee = (solIn * feeBps) / BPS_DEN;
50
+ const effectiveSolIn = solIn - fee;
51
+
52
+ let out = cpAmountOut(effectiveSolIn, vSol, vTok);
53
+
54
+ // Observed rounding in buy path (+1)
55
+ out = out + 1n;
56
+
57
+ // Cap by real tokens available
58
+ return out < curve.realTokenReserves ? out : curve.realTokenReserves;
59
+ }
60
+
61
+ /**
62
+ * Sell exact tokens in, get SOL out.
63
+ * Fee is deducted from SOL output.
64
+ */
65
+ export function sellExactIn(curve: BondingCurve, tokensIn: bigint, feeBps: bigint): bigint {
66
+ if (curve.complete) {
67
+ throw new PreBondingError(`Pool not in fundraising state: ${JSON.stringify(curve)}`);
68
+ }
69
+ if (tokensIn === 0n) {
70
+ return 0n;
71
+ }
72
+
73
+ const vTok = curve.virtualTokenReserves;
74
+ const vSol = curve.virtualSolReserves;
75
+
76
+ const gross = cpAmountOut(tokensIn, vTok, vSol);
77
+
78
+ // Deduct protocol + creator fee from output SOL
79
+ const fee = (gross * feeBps) / BPS_DEN;
80
+ let net = gross - fee;
81
+
82
+ // Cap by real SOL available
83
+ net = net < curve.realSolReserves ? net : curve.realSolReserves;
84
+
85
+ return net;
86
+ }
87
+
88
+ /**
89
+ * Buy exact tokens out, calculate required SOL in.
90
+ * This calculates the EXACT amount needed, but doesn't account for slippage buffer.
91
+ */
92
+ export function buyExactOut(curve: BondingCurve, tokensOut: bigint, feeBps: bigint): bigint {
93
+ if (curve.complete) {
94
+ throw new PreBondingError(`Pool not in fundraising state: ${JSON.stringify(curve)}`);
95
+ }
96
+ if (tokensOut === 0n) {
97
+ return 0n;
98
+ }
99
+ if (tokensOut > curve.realTokenReserves) {
100
+ throw new PreBondingError('Insufficient liquidity: real_token cap');
101
+ }
102
+
103
+ const vSol = curve.virtualSolReserves;
104
+ const vTok = curve.virtualTokenReserves;
105
+
106
+ if (tokensOut >= vTok) {
107
+ throw new PreBondingError('Insufficient liquidity: tokens_out >= v_tok');
108
+ }
109
+
110
+ // Calculate effective SOL needed before fees: ceil((vSol * tokensOut) / (vTok - tokensOut))
111
+ const num = vSol * tokensOut;
112
+ const den = vTok - tokensOut;
113
+ const effIn = (num + den - 1n) / den; // ceiling division
114
+
115
+ const feeNum = BPS_DEN - feeBps;
116
+ if (feeNum === 0n) {
117
+ throw new PreBondingError('Invalid fee configuration');
118
+ }
119
+
120
+ // Calculate gross SOL including fees: ceil((effIn * BPS_DEN) / feeNum)
121
+ const grossIn = (effIn * BPS_DEN + feeNum - 1n) / feeNum;
122
+
123
+ return grossIn;
124
+ }
125
+
126
+ /**
127
+ * Sell exact SOL out, calculate required tokens in.
128
+ * This calculates the EXACT amount needed, but doesn't account for slippage buffer.
129
+ */
130
+ export function sellExactOut(curve: BondingCurve, solOut: bigint, feeBps: bigint): bigint {
131
+ if (curve.complete) {
132
+ throw new PreBondingError(`Pool not in fundraising state: ${JSON.stringify(curve)}`);
133
+ }
134
+ if (solOut === 0n) {
135
+ return 0n;
136
+ }
137
+ if (solOut > curve.realSolReserves) {
138
+ throw new PreBondingError('Insufficient liquidity: real_sol cap');
139
+ }
140
+
141
+ const vTok = curve.virtualTokenReserves;
142
+ const vSol = curve.virtualSolReserves;
143
+
144
+ // fee_num = BPS_DEN - fee_bps
145
+ const feeNum = BPS_DEN - feeBps;
146
+ if (feeNum === 0n) {
147
+ throw new PreBondingError('Invalid fee configuration');
148
+ }
149
+
150
+ // Upper bound via continuous formula: gross = ceil((solOut * BPS_DEN) / feeNum)
151
+ const gross = (solOut * BPS_DEN + feeNum - 1n) / feeNum; // ceil
152
+ if (gross >= vSol) {
153
+ throw new PreBondingError('Insufficient liquidity: gross >= v_sol');
154
+ }
155
+
156
+ // hi = ceil((vTok * gross) / (vSol - gross))
157
+ const num = vTok * gross;
158
+ const den = vSol - gross;
159
+ let hi = (num + den - 1n) / den; // ceil
160
+ if (hi === 0n) {
161
+ hi = 1n;
162
+ }
163
+
164
+ // Monotonic binary search on sellExactIn using forward path (with real floors)
165
+ let lo = 0n;
166
+ let ans = hi;
167
+ while (lo <= hi) {
168
+ const mid = lo + (hi - lo) / 2n;
169
+ const got = sellExactIn(curve, mid, feeBps);
170
+ if (got >= solOut) {
171
+ ans = mid;
172
+ if (mid === 0n) break;
173
+ hi = mid - 1n;
174
+ } else {
175
+ lo = mid + 1n;
176
+ }
177
+ }
178
+
179
+ return ans;
180
+ }
181
+
182
+ export function estimatePumpFunPrebonded(
183
+ bondingCurve: BondingCurve,
184
+ globalAccount: GlobalAccount,
185
+ amount: bigint,
186
+ tradeType: PreBondedTradeType,
187
+ ): bigint {
188
+ if (bondingCurve.complete) {
189
+ throw new PreBondingError(`Pool not in fundraising state: ${JSON.stringify(bondingCurve)}`);
190
+ }
191
+
192
+ const totalFeeBps = globalAccount.feeBasisPoints + globalAccount.creatorFeeBasisPoints;
193
+
194
+ switch (tradeType) {
195
+ case PreBondedTradeType.BuyExactIn:
196
+ return buyExactIn(bondingCurve, amount, totalFeeBps);
197
+ case PreBondedTradeType.BuyExactOut:
198
+ return buyExactOut(bondingCurve, amount, totalFeeBps);
199
+ case PreBondedTradeType.SellExactIn:
200
+ return sellExactIn(bondingCurve, amount, totalFeeBps);
201
+ case PreBondedTradeType.SellExactOut:
202
+ return sellExactOut(bondingCurve, amount, totalFeeBps);
203
+ default:
204
+ throw new PreBondingError(`Invalid trade type: ${tradeType}`);
205
+ }
206
+ }