@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
|
@@ -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
|
|
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
|
-
|
|
52
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
/** Metadata for source token */
|
|
70
|
+
tokenIn: {
|
|
71
|
+
address: string
|
|
72
|
+
chainId: ChainID
|
|
73
|
+
}
|
|
68
74
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
|
87
|
+
public static async getQuote(params: IntentsQuoteParams): Promise<QuoteResponse> {
|
|
76
88
|
try {
|
|
77
89
|
const quote = await this.getQuoteFromRouters(params);
|
|
78
|
-
|
|
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
|
-
|
|
81
|
-
|
|
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
|
-
|
|
86
|
-
|
|
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<
|
|
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
|
|
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(
|
|
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
|
|
177
|
+
if (compareAddresses(params.tokenIn, params.tokenOut)) {
|
|
136
178
|
let amountUsd = 0;
|
|
137
179
|
|
|
138
|
-
if (params.tokenIn
|
|
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
|
-
|
|
161
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
+
}
|