@liquid-af/sdk 0.1.0
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 +776 -0
- package/dist/accounts/index.d.ts +5 -0
- package/dist/accounts/index.d.ts.map +1 -0
- package/dist/accounts/index.js +5 -0
- package/dist/accounts/index.js.map +1 -0
- package/dist/accounts/liquid-fees.d.ts +63 -0
- package/dist/accounts/liquid-fees.d.ts.map +1 -0
- package/dist/accounts/liquid-fees.js +27 -0
- package/dist/accounts/liquid-fees.js.map +1 -0
- package/dist/accounts/liquid-state.d.ts +112 -0
- package/dist/accounts/liquid-state.d.ts.map +1 -0
- package/dist/accounts/liquid-state.js +92 -0
- package/dist/accounts/liquid-state.js.map +1 -0
- package/dist/accounts/liquid-swap.d.ts +97 -0
- package/dist/accounts/liquid-swap.d.ts.map +1 -0
- package/dist/accounts/liquid-swap.js +54 -0
- package/dist/accounts/liquid-swap.js.map +1 -0
- package/dist/accounts/liquid.d.ts +175 -0
- package/dist/accounts/liquid.d.ts.map +1 -0
- package/dist/accounts/liquid.js +66 -0
- package/dist/accounts/liquid.js.map +1 -0
- package/dist/client.d.ts +621 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +511 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +56 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +63 -0
- package/dist/errors.js.map +1 -0
- package/dist/events/index.d.ts +4 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +2 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/parser.d.ts +40 -0
- package/dist/events/parser.d.ts.map +1 -0
- package/dist/events/parser.js +67 -0
- package/dist/events/parser.js.map +1 -0
- package/dist/events/types.d.ts +286 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +2 -0
- package/dist/events/types.js.map +1 -0
- package/dist/helpers/index.d.ts +4 -0
- package/dist/helpers/index.d.ts.map +1 -0
- package/dist/helpers/index.js +3 -0
- package/dist/helpers/index.js.map +1 -0
- package/dist/helpers/preview.d.ts +259 -0
- package/dist/helpers/preview.d.ts.map +1 -0
- package/dist/helpers/preview.js +458 -0
- package/dist/helpers/preview.js.map +1 -0
- package/dist/helpers/user.d.ts +11 -0
- package/dist/helpers/user.d.ts.map +1 -0
- package/dist/helpers/user.js +20 -0
- package/dist/helpers/user.js.map +1 -0
- package/dist/idl/index.d.ts +53 -0
- package/dist/idl/index.d.ts.map +1 -0
- package/dist/idl/index.js +64 -0
- package/dist/idl/index.js.map +1 -0
- package/dist/idl/liquid.d.ts +10523 -0
- package/dist/idl/liquid.d.ts.map +1 -0
- package/dist/idl/liquid.js +2 -0
- package/dist/idl/liquid.js.map +1 -0
- package/dist/idl/liquid.json +10516 -0
- package/dist/idl/liquid_fees.d.ts +1520 -0
- package/dist/idl/liquid_fees.d.ts.map +1 -0
- package/dist/idl/liquid_fees.js +2 -0
- package/dist/idl/liquid_fees.js.map +1 -0
- package/dist/idl/liquid_fees.json +1513 -0
- package/dist/idl/liquid_state.d.ts +2936 -0
- package/dist/idl/liquid_state.d.ts.map +1 -0
- package/dist/idl/liquid_state.js +2 -0
- package/dist/idl/liquid_state.js.map +1 -0
- package/dist/idl/liquid_state.json +2929 -0
- package/dist/idl/liquid_swap.d.ts +5849 -0
- package/dist/idl/liquid_swap.d.ts.map +1 -0
- package/dist/idl/liquid_swap.js +2 -0
- package/dist/idl/liquid_swap.js.map +1 -0
- package/dist/idl/liquid_swap.json +5842 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/instructions/index.d.ts +9 -0
- package/dist/instructions/index.d.ts.map +1 -0
- package/dist/instructions/index.js +9 -0
- package/dist/instructions/index.js.map +1 -0
- package/dist/instructions/liquid-fees.d.ts +111 -0
- package/dist/instructions/liquid-fees.d.ts.map +1 -0
- package/dist/instructions/liquid-fees.js +169 -0
- package/dist/instructions/liquid-fees.js.map +1 -0
- package/dist/instructions/liquid-state.d.ts +173 -0
- package/dist/instructions/liquid-state.d.ts.map +1 -0
- package/dist/instructions/liquid-state.js +194 -0
- package/dist/instructions/liquid-state.js.map +1 -0
- package/dist/instructions/liquid-swap.d.ts +122 -0
- package/dist/instructions/liquid-swap.d.ts.map +1 -0
- package/dist/instructions/liquid-swap.js +167 -0
- package/dist/instructions/liquid-swap.js.map +1 -0
- package/dist/instructions/liquid.d.ts +297 -0
- package/dist/instructions/liquid.d.ts.map +1 -0
- package/dist/instructions/liquid.js +483 -0
- package/dist/instructions/liquid.js.map +1 -0
- package/dist/instructions/program-cache.d.ts +35 -0
- package/dist/instructions/program-cache.d.ts.map +1 -0
- package/dist/instructions/program-cache.js +68 -0
- package/dist/instructions/program-cache.js.map +1 -0
- package/dist/math/amm.d.ts +42 -0
- package/dist/math/amm.d.ts.map +1 -0
- package/dist/math/amm.js +109 -0
- package/dist/math/amm.js.map +1 -0
- package/dist/math/bonding-curve.d.ts +34 -0
- package/dist/math/bonding-curve.d.ts.map +1 -0
- package/dist/math/bonding-curve.js +80 -0
- package/dist/math/bonding-curve.js.map +1 -0
- package/dist/math/constants.d.ts +14 -0
- package/dist/math/constants.d.ts.map +1 -0
- package/dist/math/constants.js +14 -0
- package/dist/math/constants.js.map +1 -0
- package/dist/math/fees.d.ts +88 -0
- package/dist/math/fees.d.ts.map +1 -0
- package/dist/math/fees.js +135 -0
- package/dist/math/fees.js.map +1 -0
- package/dist/math/index.d.ts +8 -0
- package/dist/math/index.d.ts.map +1 -0
- package/dist/math/index.js +6 -0
- package/dist/math/index.js.map +1 -0
- package/dist/math/tiered-fees.d.ts +80 -0
- package/dist/math/tiered-fees.d.ts.map +1 -0
- package/dist/math/tiered-fees.js +129 -0
- package/dist/math/tiered-fees.js.map +1 -0
- package/dist/oracle.d.ts +53 -0
- package/dist/oracle.d.ts.map +1 -0
- package/dist/oracle.js +70 -0
- package/dist/oracle.js.map +1 -0
- package/dist/pda/index.d.ts +89 -0
- package/dist/pda/index.d.ts.map +1 -0
- package/dist/pda/index.js +127 -0
- package/dist/pda/index.js.map +1 -0
- package/dist/pda/liquid-fees.d.ts +27 -0
- package/dist/pda/liquid-fees.d.ts.map +1 -0
- package/dist/pda/liquid-fees.js +36 -0
- package/dist/pda/liquid-fees.js.map +1 -0
- package/dist/pda/liquid-state.d.ts +56 -0
- package/dist/pda/liquid-state.d.ts.map +1 -0
- package/dist/pda/liquid-state.js +79 -0
- package/dist/pda/liquid-state.js.map +1 -0
- package/dist/pda/liquid-swap.d.ts +76 -0
- package/dist/pda/liquid-swap.d.ts.map +1 -0
- package/dist/pda/liquid-swap.js +103 -0
- package/dist/pda/liquid-swap.js.map +1 -0
- package/dist/pda/liquid.d.ts +67 -0
- package/dist/pda/liquid.d.ts.map +1 -0
- package/dist/pda/liquid.js +91 -0
- package/dist/pda/liquid.js.map +1 -0
- package/dist/provider.d.ts +26 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +47 -0
- package/dist/provider.js.map +1 -0
- package/dist/transaction/builder.d.ts +30 -0
- package/dist/transaction/builder.d.ts.map +1 -0
- package/dist/transaction/builder.js +48 -0
- package/dist/transaction/builder.js.map +1 -0
- package/dist/transaction/index.d.ts +3 -0
- package/dist/transaction/index.d.ts.map +1 -0
- package/dist/transaction/index.js +3 -0
- package/dist/transaction/index.js.map +1 -0
- package/dist/transaction/send.d.ts +25 -0
- package/dist/transaction/send.d.ts.map +1 -0
- package/dist/transaction/send.js +52 -0
- package/dist/transaction/send.js.map +1 -0
- package/dist/types.d.ts +311 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +46 -0
- package/dist/types.js.map +1 -0
- package/package.json +112 -0
- package/src/accounts/index.ts +26 -0
- package/src/accounts/liquid-fees.ts +38 -0
- package/src/accounts/liquid-state.ts +134 -0
- package/src/accounts/liquid-swap.ts +79 -0
- package/src/accounts/liquid.ts +100 -0
- package/src/client.ts +1001 -0
- package/src/config.ts +91 -0
- package/src/errors.ts +94 -0
- package/src/events/index.ts +42 -0
- package/src/events/parser.ts +90 -0
- package/src/events/types.ts +310 -0
- package/src/helpers/index.ts +23 -0
- package/src/helpers/preview.ts +798 -0
- package/src/helpers/user.ts +24 -0
- package/src/idl/index.ts +94 -0
- package/src/idl/liquid.json +10516 -0
- package/src/idl/liquid.ts +10522 -0
- package/src/idl/liquid_fees.json +1513 -0
- package/src/idl/liquid_fees.ts +1519 -0
- package/src/idl/liquid_state.json +2929 -0
- package/src/idl/liquid_state.ts +2935 -0
- package/src/idl/liquid_swap.json +5842 -0
- package/src/idl/liquid_swap.ts +5848 -0
- package/src/index.ts +98 -0
- package/src/instructions/index.ts +109 -0
- package/src/instructions/liquid-fees.ts +289 -0
- package/src/instructions/liquid-state.ts +336 -0
- package/src/instructions/liquid-swap.ts +414 -0
- package/src/instructions/liquid.ts +884 -0
- package/src/instructions/program-cache.ts +106 -0
- package/src/math/amm.ts +146 -0
- package/src/math/bonding-curve.ts +122 -0
- package/src/math/constants.ts +19 -0
- package/src/math/fees.ts +191 -0
- package/src/math/index.ts +40 -0
- package/src/math/tiered-fees.ts +165 -0
- package/src/oracle.ts +97 -0
- package/src/pda/index.ts +331 -0
- package/src/pda/liquid-fees.ts +58 -0
- package/src/pda/liquid-state.ts +123 -0
- package/src/pda/liquid-swap.ts +162 -0
- package/src/pda/liquid.ts +152 -0
- package/src/provider.ts +60 -0
- package/src/transaction/builder.ts +80 -0
- package/src/transaction/index.ts +6 -0
- package/src/transaction/send.ts +72 -0
- package/src/types.ts +354 -0
|
@@ -0,0 +1,798 @@
|
|
|
1
|
+
import type { Connection, PublicKey } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
import type { LiquidConfig } from "../config.js";
|
|
4
|
+
import type { BuyCurvePreview, SellCurvePreview, SwapPreview } from "../types.js";
|
|
5
|
+
import { fetchAmmConfig, fetchPoolState } from "../accounts/liquid-swap.js";
|
|
6
|
+
import {
|
|
7
|
+
fetchLiquidGlobalConfig,
|
|
8
|
+
fetchNativeBondingCurve,
|
|
9
|
+
fetchStableBondingCurve,
|
|
10
|
+
} from "../accounts/liquid.js";
|
|
11
|
+
import {
|
|
12
|
+
calculateBuyExpectation,
|
|
13
|
+
calculateSellExpectation,
|
|
14
|
+
} from "../math/bonding-curve.js";
|
|
15
|
+
import { calculateAmmSellOutput, calculateAmmBuyInput } from "../math/amm.js";
|
|
16
|
+
import { BPS_DENOMINATOR } from "../math/constants.js";
|
|
17
|
+
import { calculateVirtualSolReserves } from "../oracle.js";
|
|
18
|
+
import { getAccount, getMint } from "@solana/spl-token";
|
|
19
|
+
import { calculateFeesForPool } from "../math/tiered-fees.js";
|
|
20
|
+
|
|
21
|
+
/** State needed for bonding curve preview calculations (pure math, no I/O). */
|
|
22
|
+
export interface CurvePreviewState {
|
|
23
|
+
globalConfig: {
|
|
24
|
+
protocolFeeBasisPoints: number;
|
|
25
|
+
creatorFeeBasisPoints: number;
|
|
26
|
+
creatorReferralRewardBasisPoints: number;
|
|
27
|
+
traderReferralRewardBasisPoints: number;
|
|
28
|
+
};
|
|
29
|
+
bondingCurve: {
|
|
30
|
+
virtualQuoteReserves: BN;
|
|
31
|
+
virtualTokenReserves: BN;
|
|
32
|
+
realTokenReserves: BN;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** State needed for AMM swap preview calculations (pure math, no I/O). */
|
|
37
|
+
export interface SwapPreviewState {
|
|
38
|
+
ammConfig: {
|
|
39
|
+
lpFeeRate: number;
|
|
40
|
+
creatorFeeRate: number;
|
|
41
|
+
protocolFeeRate: number;
|
|
42
|
+
};
|
|
43
|
+
baseVaultBalance: BN;
|
|
44
|
+
quoteVaultBalance: BN;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Referral options for curve previews. */
|
|
48
|
+
export interface CurvePreviewOptions {
|
|
49
|
+
hasCreatorRef?: boolean;
|
|
50
|
+
hasTraderRef?: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Optional pre-fetched state for {@link previewBuyOnCurve} / {@link previewSellOnCurve}.
|
|
55
|
+
*
|
|
56
|
+
* Provide any subset to skip the corresponding RPC calls. Omitted fields are
|
|
57
|
+
* fetched automatically from chain.
|
|
58
|
+
*/
|
|
59
|
+
export interface CurvePrefetchedState {
|
|
60
|
+
/**
|
|
61
|
+
* Fee configuration from the global config account.
|
|
62
|
+
* Include `initialVirtualReserveUsd` when previewing native curves without
|
|
63
|
+
* a prefetched `bondingCurve` (needed to compute virtual SOL reserves).
|
|
64
|
+
*/
|
|
65
|
+
globalConfig?: {
|
|
66
|
+
protocolFeeBasisPoints: number;
|
|
67
|
+
creatorFeeBasisPoints: number;
|
|
68
|
+
creatorReferralRewardBasisPoints: number;
|
|
69
|
+
traderReferralRewardBasisPoints: number;
|
|
70
|
+
initialVirtualReserveUsd?: BN;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Bonding curve reserves.
|
|
74
|
+
* For native curves, `virtualQuoteReserves` must already be oracle-adjusted
|
|
75
|
+
* (i.e. computed via `calculateVirtualSolReserves`).
|
|
76
|
+
*/
|
|
77
|
+
bondingCurve?: {
|
|
78
|
+
virtualQuoteReserves: BN;
|
|
79
|
+
virtualTokenReserves: BN;
|
|
80
|
+
realTokenReserves: BN;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Shared options for async bonding curve preview functions. */
|
|
85
|
+
interface CurveOptionsBase {
|
|
86
|
+
/** Pre-fetched state to skip RPC calls. */
|
|
87
|
+
prefetched?: CurvePrefetchedState;
|
|
88
|
+
/** Whether the token creator has an active referral. */
|
|
89
|
+
hasCreatorRef?: boolean;
|
|
90
|
+
/** Whether the trader has an active referral. */
|
|
91
|
+
hasTraderRef?: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Options for native SOL bonding curve previews.
|
|
96
|
+
*
|
|
97
|
+
* Requires `solPriceUsd` to compute virtual SOL reserves from the on-chain
|
|
98
|
+
* USD-denominated initial reserve value.
|
|
99
|
+
*/
|
|
100
|
+
export interface NativeCurveOptions extends CurveOptionsBase {
|
|
101
|
+
quoteMint?: never;
|
|
102
|
+
/** Current SOL price in USD (6 decimals, e.g. 150_000_000 = $150). */
|
|
103
|
+
solPriceUsd: BN;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Options for stable token (e.g. USDC) bonding curve previews.
|
|
108
|
+
*
|
|
109
|
+
* Virtual quote reserves are stored on-chain and fetched automatically.
|
|
110
|
+
*/
|
|
111
|
+
export interface StableCurveOptions extends CurveOptionsBase {
|
|
112
|
+
/** Quote token mint address (e.g. USDC mint). */
|
|
113
|
+
quoteMint: PublicKey;
|
|
114
|
+
solPriceUsd?: never;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Options for async bonding curve previews. Discriminated union:
|
|
119
|
+
* - {@link NativeCurveOptions} — native SOL curves (requires `solPriceUsd`)
|
|
120
|
+
* - {@link StableCurveOptions} — stable curves (requires `quoteMint`)
|
|
121
|
+
*/
|
|
122
|
+
export type CurveAsyncOptions = NativeCurveOptions | StableCurveOptions;
|
|
123
|
+
|
|
124
|
+
/** Pre-fetched state for AMM swap async previews. */
|
|
125
|
+
export interface SwapPrefetchedState {
|
|
126
|
+
/**
|
|
127
|
+
* Pre-computed fee rates.
|
|
128
|
+
* For tiered fee systems, use calculateFeesForPool() to determine the rates
|
|
129
|
+
* before passing them here.
|
|
130
|
+
*/
|
|
131
|
+
feeRates?: {
|
|
132
|
+
lpFeeRate: number;
|
|
133
|
+
creatorFeeRate: number;
|
|
134
|
+
protocolFeeRate: number;
|
|
135
|
+
};
|
|
136
|
+
poolState?: {
|
|
137
|
+
baseVault: PublicKey;
|
|
138
|
+
quoteVault: PublicKey;
|
|
139
|
+
baseTokenProgram: PublicKey;
|
|
140
|
+
quoteTokenProgram: PublicKey;
|
|
141
|
+
baseMint: PublicKey;
|
|
142
|
+
quoteMint: PublicKey;
|
|
143
|
+
};
|
|
144
|
+
vaultBalances?: { baseVaultBalance: BN; quoteVaultBalance: BN };
|
|
145
|
+
baseTotalSupply?: BN;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Previews a buy on the bonding curve from pre-fetched state. The function executes offline using the state being passed in.
|
|
150
|
+
*
|
|
151
|
+
* @param amountInQuote - Quote token amount to spend (lamports for SOL, smallest unit for stables)
|
|
152
|
+
* @param state - Pre-fetched bonding curve and global config state
|
|
153
|
+
* @param options - Optional referral flags
|
|
154
|
+
* @returns Buy preview with tokens out, fees, price impact, and completion flag
|
|
155
|
+
*/
|
|
156
|
+
export function calculateBuyCurvePreview(
|
|
157
|
+
amountInQuote: BN,
|
|
158
|
+
state: CurvePreviewState,
|
|
159
|
+
options?: CurvePreviewOptions
|
|
160
|
+
): BuyCurvePreview {
|
|
161
|
+
const { globalConfig, bondingCurve } = state;
|
|
162
|
+
|
|
163
|
+
const feeConfig = {
|
|
164
|
+
protocolFeeBps: globalConfig.protocolFeeBasisPoints,
|
|
165
|
+
creatorFeeBps: globalConfig.creatorFeeBasisPoints,
|
|
166
|
+
creatorReferralBps: globalConfig.creatorReferralRewardBasisPoints,
|
|
167
|
+
traderReferralBps: globalConfig.traderReferralRewardBasisPoints,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const result = calculateBuyExpectation(
|
|
171
|
+
amountInQuote,
|
|
172
|
+
bondingCurve,
|
|
173
|
+
feeConfig,
|
|
174
|
+
options?.hasCreatorRef ?? false,
|
|
175
|
+
options?.hasTraderRef ?? false
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const priceBefore = bondingCurve.virtualQuoteReserves
|
|
179
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
180
|
+
.div(bondingCurve.virtualTokenReserves);
|
|
181
|
+
const priceAfter = result.newVirtualQuote
|
|
182
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
183
|
+
.div(result.newVirtualToken);
|
|
184
|
+
const priceImpactBps = priceAfter
|
|
185
|
+
.sub(priceBefore)
|
|
186
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
187
|
+
.div(priceBefore)
|
|
188
|
+
.toNumber();
|
|
189
|
+
|
|
190
|
+
const willComplete = result.tokensOut.gte(bondingCurve.realTokenReserves);
|
|
191
|
+
|
|
192
|
+
return {
|
|
193
|
+
tokensOut: result.tokensOut,
|
|
194
|
+
fees: {
|
|
195
|
+
protocolFee: result.protocolFee,
|
|
196
|
+
creatorFee: result.creatorFee,
|
|
197
|
+
creatorReferralFee: result.creatorReferralFee,
|
|
198
|
+
traderReferralFee: result.traderReferralFee,
|
|
199
|
+
totalFees: result.totalFees,
|
|
200
|
+
},
|
|
201
|
+
priceImpactBps,
|
|
202
|
+
newReserves: {
|
|
203
|
+
virtualQuote: result.newVirtualQuote,
|
|
204
|
+
virtualToken: result.newVirtualToken,
|
|
205
|
+
},
|
|
206
|
+
willComplete,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Previews a sell on the bonding curve from pre-fetched state. The function executes offline using the state being passed in.
|
|
212
|
+
*
|
|
213
|
+
* @param amountInTokens - Token amount to sell
|
|
214
|
+
* @param state - Pre-fetched bonding curve and global config state
|
|
215
|
+
* @param options - Optional referral flags
|
|
216
|
+
* @returns Sell preview with quote out, fees, and price impact
|
|
217
|
+
*/
|
|
218
|
+
export function calculateSellCurvePreview(
|
|
219
|
+
amountInTokens: BN,
|
|
220
|
+
state: CurvePreviewState,
|
|
221
|
+
options?: CurvePreviewOptions
|
|
222
|
+
): SellCurvePreview {
|
|
223
|
+
const { globalConfig, bondingCurve } = state;
|
|
224
|
+
|
|
225
|
+
const feeConfig = {
|
|
226
|
+
protocolFeeBps: globalConfig.protocolFeeBasisPoints,
|
|
227
|
+
creatorFeeBps: globalConfig.creatorFeeBasisPoints,
|
|
228
|
+
creatorReferralBps: globalConfig.creatorReferralRewardBasisPoints,
|
|
229
|
+
traderReferralBps: globalConfig.traderReferralRewardBasisPoints,
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const result = calculateSellExpectation(
|
|
233
|
+
amountInTokens,
|
|
234
|
+
bondingCurve,
|
|
235
|
+
feeConfig,
|
|
236
|
+
options?.hasCreatorRef ?? false,
|
|
237
|
+
options?.hasTraderRef ?? false
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
const priceBefore = bondingCurve.virtualQuoteReserves
|
|
241
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
242
|
+
.div(bondingCurve.virtualTokenReserves);
|
|
243
|
+
const priceAfter = result.newVirtualQuote
|
|
244
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
245
|
+
.div(result.newVirtualToken);
|
|
246
|
+
const priceImpactBps = priceBefore
|
|
247
|
+
.sub(priceAfter)
|
|
248
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
249
|
+
.div(priceBefore)
|
|
250
|
+
.toNumber();
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
quoteOut: result.quoteOutGross,
|
|
254
|
+
quoteOutNet: result.quoteOutNet,
|
|
255
|
+
fees: {
|
|
256
|
+
protocolFee: result.protocolFee,
|
|
257
|
+
creatorFee: result.creatorFee,
|
|
258
|
+
creatorReferralFee: result.creatorReferralFee,
|
|
259
|
+
traderReferralFee: result.traderReferralFee,
|
|
260
|
+
totalFees: result.totalFees,
|
|
261
|
+
},
|
|
262
|
+
priceImpactBps,
|
|
263
|
+
newReserves: {
|
|
264
|
+
virtualQuote: result.newVirtualQuote,
|
|
265
|
+
virtualToken: result.newVirtualToken,
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Previews a buy swap on the AMM (quote -> base) from pre-fetched state. The function executes offline using the state being passed in.
|
|
272
|
+
*
|
|
273
|
+
* @param amountOut - Desired base token amount out
|
|
274
|
+
* @param state - Pre-fetched AMM config and vault balances
|
|
275
|
+
* @returns Swap preview with quote in, fees, and price impact
|
|
276
|
+
*/
|
|
277
|
+
export function calculateSwapBuyPreview(
|
|
278
|
+
amountOut: BN,
|
|
279
|
+
state: SwapPreviewState
|
|
280
|
+
): SwapPreview {
|
|
281
|
+
const { ammConfig, baseVaultBalance, quoteVaultBalance } = state;
|
|
282
|
+
|
|
283
|
+
const result = calculateAmmBuyInput(
|
|
284
|
+
amountOut,
|
|
285
|
+
baseVaultBalance,
|
|
286
|
+
quoteVaultBalance,
|
|
287
|
+
{
|
|
288
|
+
lpFeeRate: ammConfig.lpFeeRate,
|
|
289
|
+
creatorFeeRate: ammConfig.creatorFeeRate,
|
|
290
|
+
protocolFeeRate: ammConfig.protocolFeeRate,
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
const priceBefore = quoteVaultBalance
|
|
295
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
296
|
+
.div(baseVaultBalance);
|
|
297
|
+
const newBaseVault = baseVaultBalance.sub(amountOut);
|
|
298
|
+
const newQuoteVault = quoteVaultBalance.add(result.quoteInGross);
|
|
299
|
+
const priceAfter = newQuoteVault
|
|
300
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
301
|
+
.div(newBaseVault);
|
|
302
|
+
const priceImpactBps = priceAfter
|
|
303
|
+
.sub(priceBefore)
|
|
304
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
305
|
+
.div(priceBefore)
|
|
306
|
+
.toNumber();
|
|
307
|
+
|
|
308
|
+
return {
|
|
309
|
+
amountOut: result.quoteInGross,
|
|
310
|
+
amountOutNet: result.quoteIn,
|
|
311
|
+
fees: {
|
|
312
|
+
lpFee: result.lpFee,
|
|
313
|
+
creatorFee: result.creatorFee,
|
|
314
|
+
protocolFee: result.protocolFee,
|
|
315
|
+
totalFees: result.totalFees,
|
|
316
|
+
},
|
|
317
|
+
priceImpactBps,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Previews a sell swap on the AMM (base -> quote) from pre-fetched state. The function executes offline using the state being passed in.
|
|
323
|
+
*
|
|
324
|
+
* @param amountIn - Base token amount to sell
|
|
325
|
+
* @param state - Pre-fetched AMM config and vault balances
|
|
326
|
+
* @returns Swap preview with quote out, fees, and price impact
|
|
327
|
+
*/
|
|
328
|
+
export function calculateSwapSellPreview(
|
|
329
|
+
amountIn: BN,
|
|
330
|
+
state: SwapPreviewState
|
|
331
|
+
): SwapPreview {
|
|
332
|
+
const { ammConfig, baseVaultBalance, quoteVaultBalance } = state;
|
|
333
|
+
|
|
334
|
+
const result = calculateAmmSellOutput(
|
|
335
|
+
amountIn,
|
|
336
|
+
baseVaultBalance,
|
|
337
|
+
quoteVaultBalance,
|
|
338
|
+
{
|
|
339
|
+
lpFeeRate: ammConfig.lpFeeRate,
|
|
340
|
+
creatorFeeRate: ammConfig.creatorFeeRate,
|
|
341
|
+
protocolFeeRate: ammConfig.protocolFeeRate,
|
|
342
|
+
}
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
const priceBefore = quoteVaultBalance
|
|
346
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
347
|
+
.div(baseVaultBalance);
|
|
348
|
+
const newBaseVault = baseVaultBalance.add(amountIn);
|
|
349
|
+
const newQuoteVault = quoteVaultBalance.sub(result.quoteOut);
|
|
350
|
+
const priceAfter = newQuoteVault
|
|
351
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
352
|
+
.div(newBaseVault);
|
|
353
|
+
const priceImpactBps = priceBefore
|
|
354
|
+
.sub(priceAfter)
|
|
355
|
+
.mul(new BN(BPS_DENOMINATOR))
|
|
356
|
+
.div(priceBefore)
|
|
357
|
+
.toNumber();
|
|
358
|
+
|
|
359
|
+
return {
|
|
360
|
+
amountOut: result.quoteOut,
|
|
361
|
+
amountOutNet: result.quoteOutNet,
|
|
362
|
+
fees: {
|
|
363
|
+
lpFee: result.lpFee,
|
|
364
|
+
creatorFee: result.creatorFee,
|
|
365
|
+
protocolFee: result.protocolFee,
|
|
366
|
+
totalFees: result.totalFees,
|
|
367
|
+
},
|
|
368
|
+
priceImpactBps,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Fetches the on-chain global config and maps it to the fee/reserve fields
|
|
374
|
+
* required by {@link CurvePreviewState}.
|
|
375
|
+
*
|
|
376
|
+
* @param connection - Solana RPC connection
|
|
377
|
+
* @param config - Liquid protocol configuration (provides the program ID)
|
|
378
|
+
* @returns Fee basis-point fields plus `initialVirtualReserveUsd` for native reserve computation
|
|
379
|
+
*/
|
|
380
|
+
async function fetchCurveGlobalConfig(
|
|
381
|
+
connection: Connection,
|
|
382
|
+
config: LiquidConfig
|
|
383
|
+
): Promise<
|
|
384
|
+
CurvePreviewState["globalConfig"] & { initialVirtualReserveUsd: BN }
|
|
385
|
+
> {
|
|
386
|
+
const raw = await fetchLiquidGlobalConfig(connection, config);
|
|
387
|
+
return {
|
|
388
|
+
protocolFeeBasisPoints: raw.protocolFeeBasisPoints,
|
|
389
|
+
creatorFeeBasisPoints: raw.creatorFeeBasisPoints,
|
|
390
|
+
creatorReferralRewardBasisPoints: raw.creatorReferralRewardBasisPoints,
|
|
391
|
+
traderReferralRewardBasisPoints: raw.traderReferralRewardBasisPoints,
|
|
392
|
+
initialVirtualReserveUsd: raw.initialVirtualReserveUsd,
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Resolves {@link CurvePreviewState} for native SOL curves.
|
|
398
|
+
*
|
|
399
|
+
* Fetches the global config and native bonding curve in parallel (skipping
|
|
400
|
+
* any that are prefetched), then computes `virtualQuoteReserves` from
|
|
401
|
+
* `initialVirtualReserveUsd` and `solPriceUsd`.
|
|
402
|
+
*
|
|
403
|
+
* @param connection - Solana RPC connection
|
|
404
|
+
* @param mint - Token mint address of the native bonding curve
|
|
405
|
+
* @param options - Native curve options containing `solPriceUsd` and optional `prefetched` state
|
|
406
|
+
* @param config - Liquid protocol configuration
|
|
407
|
+
* @returns Resolved curve preview state ready for pure-math preview calculations
|
|
408
|
+
*/
|
|
409
|
+
async function resolveNativeCurve(
|
|
410
|
+
connection: Connection,
|
|
411
|
+
mint: PublicKey,
|
|
412
|
+
options: NativeCurveOptions,
|
|
413
|
+
config: LiquidConfig
|
|
414
|
+
): Promise<CurvePreviewState> {
|
|
415
|
+
const pre = options.prefetched;
|
|
416
|
+
|
|
417
|
+
const [globalConfig, nativeCurve] = await Promise.all([
|
|
418
|
+
pre?.globalConfig ?? fetchCurveGlobalConfig(connection, config),
|
|
419
|
+
pre?.bondingCurve
|
|
420
|
+
? null
|
|
421
|
+
: fetchNativeBondingCurve(connection, mint, config),
|
|
422
|
+
]);
|
|
423
|
+
|
|
424
|
+
const virtualSolReserves = calculateVirtualSolReserves(
|
|
425
|
+
BigInt(globalConfig.initialVirtualReserveUsd!.toString()),
|
|
426
|
+
BigInt(options.solPriceUsd.toString())
|
|
427
|
+
);
|
|
428
|
+
|
|
429
|
+
const bondingCurve = pre?.bondingCurve ?? {
|
|
430
|
+
virtualQuoteReserves: new BN(virtualSolReserves.toString()),
|
|
431
|
+
virtualTokenReserves: nativeCurve!.virtualTokenReserves,
|
|
432
|
+
realTokenReserves: nativeCurve!.realTokenReserves,
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
return { globalConfig, bondingCurve };
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
/**
|
|
439
|
+
* Resolves {@link CurvePreviewState} for stable (e.g. USDC) curves.
|
|
440
|
+
*
|
|
441
|
+
* Fetches the global config and stable bonding curve in parallel (skipping
|
|
442
|
+
* any that are prefetched). `virtualQuoteReserves` is read directly from
|
|
443
|
+
* the on-chain account.
|
|
444
|
+
*
|
|
445
|
+
* @param connection - Solana RPC connection
|
|
446
|
+
* @param mint - Token mint address of the stable bonding curve
|
|
447
|
+
* @param options - Stable curve options containing `quoteMint` and optional `prefetched` state
|
|
448
|
+
* @param config - Liquid protocol configuration
|
|
449
|
+
* @returns Resolved curve preview state ready for pure-math preview calculations
|
|
450
|
+
*/
|
|
451
|
+
async function resolveStableCurve(
|
|
452
|
+
connection: Connection,
|
|
453
|
+
mint: PublicKey,
|
|
454
|
+
options: StableCurveOptions,
|
|
455
|
+
config: LiquidConfig
|
|
456
|
+
): Promise<CurvePreviewState> {
|
|
457
|
+
const pre = options.prefetched;
|
|
458
|
+
|
|
459
|
+
const [globalConfig, stableCurve] = await Promise.all([
|
|
460
|
+
pre?.globalConfig ?? fetchCurveGlobalConfig(connection, config),
|
|
461
|
+
pre?.bondingCurve
|
|
462
|
+
? null
|
|
463
|
+
: fetchStableBondingCurve(
|
|
464
|
+
connection,
|
|
465
|
+
mint,
|
|
466
|
+
options.quoteMint,
|
|
467
|
+
config
|
|
468
|
+
),
|
|
469
|
+
]);
|
|
470
|
+
|
|
471
|
+
const bondingCurve = pre?.bondingCurve ?? {
|
|
472
|
+
virtualQuoteReserves: stableCurve!.virtualQuoteReserves,
|
|
473
|
+
virtualTokenReserves: stableCurve!.virtualTokenReserves,
|
|
474
|
+
realTokenReserves: stableCurve!.realTokenReserves,
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
return { globalConfig, bondingCurve };
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Dispatches to {@link resolveNativeCurve} or {@link resolveStableCurve}
|
|
482
|
+
* based on whether `options.quoteMint` is present.
|
|
483
|
+
*
|
|
484
|
+
* @param connection - Solana RPC connection
|
|
485
|
+
* @param mint - Token mint address
|
|
486
|
+
* @param config - Liquid protocol configuration
|
|
487
|
+
* @param options - Discriminated curve options (native or stable)
|
|
488
|
+
* @returns Resolved curve preview state ready for pure-math preview calculations
|
|
489
|
+
*/
|
|
490
|
+
function resolveCurveState(
|
|
491
|
+
connection: Connection,
|
|
492
|
+
mint: PublicKey,
|
|
493
|
+
config: LiquidConfig,
|
|
494
|
+
options: CurveAsyncOptions
|
|
495
|
+
): Promise<CurvePreviewState> {
|
|
496
|
+
if (options.quoteMint) {
|
|
497
|
+
return resolveStableCurve(
|
|
498
|
+
connection,
|
|
499
|
+
mint,
|
|
500
|
+
options as StableCurveOptions,
|
|
501
|
+
config
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
return resolveNativeCurve(
|
|
505
|
+
connection,
|
|
506
|
+
mint,
|
|
507
|
+
options as NativeCurveOptions,
|
|
508
|
+
config
|
|
509
|
+
);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Previews a buy on the bonding curve.
|
|
514
|
+
*
|
|
515
|
+
* Auto-fetches the global config and bonding curve state from chain, then
|
|
516
|
+
* computes expected tokens out, fee breakdown, price impact, and whether the
|
|
517
|
+
* buy would complete the curve.
|
|
518
|
+
*
|
|
519
|
+
* @param connection - Solana RPC connection
|
|
520
|
+
* @param mint - Token mint address
|
|
521
|
+
* @param amountInQuote - Quote token amount to spend (lamports for SOL, smallest unit for stables)
|
|
522
|
+
* @param config - Liquid protocol configuration
|
|
523
|
+
* @param options - Native ({@link NativeCurveOptions}) or stable ({@link StableCurveOptions})
|
|
524
|
+
* @returns Buy preview with `tokensOut`, `fees`, `priceImpactBps`, `newReserves`, and `willComplete`
|
|
525
|
+
*
|
|
526
|
+
* @example Native SOL curve
|
|
527
|
+
* ```ts
|
|
528
|
+
* const preview = await previewBuyOnCurve(connection, mint, new BN(100_000_000), config, {
|
|
529
|
+
* solPriceUsd: new BN(150_000_000), // $150
|
|
530
|
+
* });
|
|
531
|
+
* ```
|
|
532
|
+
*
|
|
533
|
+
* @example Stable (USDC) curve
|
|
534
|
+
* ```ts
|
|
535
|
+
* const preview = await previewBuyOnCurve(connection, mint, new BN(10_000_000), config, {
|
|
536
|
+
* quoteMint: USDC_MINT,
|
|
537
|
+
* });
|
|
538
|
+
* ```
|
|
539
|
+
*
|
|
540
|
+
* @see {@link calculateBuyCurvePreview} for pure offline calculations with pre-built state
|
|
541
|
+
*/
|
|
542
|
+
export async function previewBuyOnCurve(
|
|
543
|
+
connection: Connection,
|
|
544
|
+
mint: PublicKey,
|
|
545
|
+
amountInQuote: BN,
|
|
546
|
+
config: LiquidConfig,
|
|
547
|
+
options: CurveAsyncOptions
|
|
548
|
+
): Promise<BuyCurvePreview> {
|
|
549
|
+
const state = await resolveCurveState(connection, mint, config, options);
|
|
550
|
+
return calculateBuyCurvePreview(amountInQuote, state, {
|
|
551
|
+
hasCreatorRef: options?.hasCreatorRef,
|
|
552
|
+
hasTraderRef: options?.hasTraderRef,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Previews a sell on the bonding curve.
|
|
558
|
+
*
|
|
559
|
+
* Auto-fetches the global config and bonding curve state from chain, then
|
|
560
|
+
* computes expected quote out, fee breakdown, and price impact.
|
|
561
|
+
*
|
|
562
|
+
* @param connection - Solana RPC connection
|
|
563
|
+
* @param mint - Token mint address
|
|
564
|
+
* @param amountInTokens - Token amount to sell (smallest unit, 6 decimals)
|
|
565
|
+
* @param config - Liquid protocol configuration
|
|
566
|
+
* @param options - Native ({@link NativeCurveOptions}) or stable ({@link StableCurveOptions})
|
|
567
|
+
* @returns Sell preview with `quoteOut`, `quoteOutNet`, `fees`, `priceImpactBps`, and `newReserves`
|
|
568
|
+
*
|
|
569
|
+
* @example Native SOL curve
|
|
570
|
+
* ```ts
|
|
571
|
+
* const preview = await previewSellOnCurve(connection, mint, new BN(5_000_000), config, {
|
|
572
|
+
* solPriceUsd: new BN(150_000_000),
|
|
573
|
+
* });
|
|
574
|
+
* ```
|
|
575
|
+
*
|
|
576
|
+
* @see {@link calculateSellCurvePreview} for pure offline calculations with pre-built state
|
|
577
|
+
*/
|
|
578
|
+
export async function previewSellOnCurve(
|
|
579
|
+
connection: Connection,
|
|
580
|
+
mint: PublicKey,
|
|
581
|
+
amountInTokens: BN,
|
|
582
|
+
config: LiquidConfig,
|
|
583
|
+
options: CurveAsyncOptions
|
|
584
|
+
): Promise<SellCurvePreview> {
|
|
585
|
+
const state = await resolveCurveState(connection, mint, config, options);
|
|
586
|
+
return calculateSellCurvePreview(amountInTokens, state, {
|
|
587
|
+
hasCreatorRef: options?.hasCreatorRef,
|
|
588
|
+
hasTraderRef: options?.hasTraderRef,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Previews a sell swap on the AMM (base -> quote).
|
|
594
|
+
*
|
|
595
|
+
* Accepts optional pre-fetched state to avoid redundant RPC calls.
|
|
596
|
+
* For tiered fee systems, you must provide either:
|
|
597
|
+
* - `prefetched.feeRates` - pre-computed fee rates for the current market cap
|
|
598
|
+
* - `solPriceUsd` parameter - to calculate fee rates from on-chain tiered config
|
|
599
|
+
*
|
|
600
|
+
* @param connection - Solana RPC connection
|
|
601
|
+
* @param baseMint - Base token mint address
|
|
602
|
+
* @param quoteMint - Quote token mint address
|
|
603
|
+
* @param amountIn - Base token amount to sell
|
|
604
|
+
* @param config - Liquid protocol configuration
|
|
605
|
+
* @param options - Optional pre-fetched AMM state and solPriceUsd for tier calculation
|
|
606
|
+
* @returns Swap preview with quote out, fees, and price impact
|
|
607
|
+
*
|
|
608
|
+
* @throws Error if neither feeRates nor solPriceUsd is provided for tiered fee systems
|
|
609
|
+
*/
|
|
610
|
+
export async function previewSwapSell(
|
|
611
|
+
connection: Connection,
|
|
612
|
+
baseMint: PublicKey,
|
|
613
|
+
quoteMint: PublicKey,
|
|
614
|
+
amountIn: BN,
|
|
615
|
+
config: LiquidConfig,
|
|
616
|
+
options?: { prefetched?: SwapPrefetchedState; solPriceUsd?: BN }
|
|
617
|
+
): Promise<SwapPreview> {
|
|
618
|
+
const pre = options?.prefetched;
|
|
619
|
+
|
|
620
|
+
// Fetch pool state if not provided
|
|
621
|
+
const poolState =
|
|
622
|
+
pre?.poolState ??
|
|
623
|
+
(await fetchPoolState(connection, baseMint, quoteMint, config));
|
|
624
|
+
|
|
625
|
+
// Get vault balances
|
|
626
|
+
let baseVaultBalance: BN;
|
|
627
|
+
let quoteVaultBalance: BN;
|
|
628
|
+
|
|
629
|
+
if (pre?.vaultBalances) {
|
|
630
|
+
({ baseVaultBalance, quoteVaultBalance } = pre.vaultBalances);
|
|
631
|
+
} else {
|
|
632
|
+
const [baseVaultAccount, quoteVaultAccount] = await Promise.all([
|
|
633
|
+
getAccount(
|
|
634
|
+
connection,
|
|
635
|
+
poolState.baseVault,
|
|
636
|
+
"confirmed",
|
|
637
|
+
poolState.baseTokenProgram
|
|
638
|
+
),
|
|
639
|
+
getAccount(
|
|
640
|
+
connection,
|
|
641
|
+
poolState.quoteVault,
|
|
642
|
+
"confirmed",
|
|
643
|
+
poolState.quoteTokenProgram
|
|
644
|
+
),
|
|
645
|
+
]);
|
|
646
|
+
baseVaultBalance = new BN(baseVaultAccount.amount.toString());
|
|
647
|
+
quoteVaultBalance = new BN(quoteVaultAccount.amount.toString());
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// Get fee rates
|
|
651
|
+
let feeRates: {
|
|
652
|
+
lpFeeRate: number;
|
|
653
|
+
creatorFeeRate: number;
|
|
654
|
+
protocolFeeRate: number;
|
|
655
|
+
};
|
|
656
|
+
|
|
657
|
+
if (pre?.feeRates) {
|
|
658
|
+
// Use pre-computed fee rates
|
|
659
|
+
feeRates = pre.feeRates;
|
|
660
|
+
} else if (options?.solPriceUsd) {
|
|
661
|
+
// Calculate fee rates from tiered config
|
|
662
|
+
const ammConfig = await fetchAmmConfig(connection, config);
|
|
663
|
+
const baseMintAccount = await getMint(
|
|
664
|
+
connection,
|
|
665
|
+
poolState.baseMint,
|
|
666
|
+
"confirmed",
|
|
667
|
+
poolState.baseTokenProgram
|
|
668
|
+
);
|
|
669
|
+
const baseTotalSupply = new BN(baseMintAccount.supply.toString());
|
|
670
|
+
|
|
671
|
+
const tiers = ammConfig.feeTiers;
|
|
672
|
+
const [lpFeeRate, protocolFeeRate, creatorFeeRate] =
|
|
673
|
+
calculateFeesForPool(
|
|
674
|
+
quoteVaultBalance,
|
|
675
|
+
baseVaultBalance,
|
|
676
|
+
baseTotalSupply,
|
|
677
|
+
options.solPriceUsd,
|
|
678
|
+
poolState.quoteMint,
|
|
679
|
+
tiers
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
feeRates = { lpFeeRate, protocolFeeRate, creatorFeeRate };
|
|
683
|
+
} else {
|
|
684
|
+
throw new Error(
|
|
685
|
+
"Either prefetched.feeRates or solPriceUsd must be provided for tiered fee calculations"
|
|
686
|
+
);
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
return calculateSwapSellPreview(amountIn, {
|
|
690
|
+
ammConfig: feeRates,
|
|
691
|
+
baseVaultBalance,
|
|
692
|
+
quoteVaultBalance,
|
|
693
|
+
});
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Previews a buy swap on the AMM (quote -> base).
|
|
698
|
+
*
|
|
699
|
+
* Accepts optional pre-fetched state to avoid redundant RPC calls.
|
|
700
|
+
* For tiered fee systems, you must provide either:
|
|
701
|
+
* - `prefetched.feeRates` - pre-computed fee rates for the current market cap
|
|
702
|
+
* - `solPriceUsd` parameter - to calculate fee rates from on-chain tiered config
|
|
703
|
+
*
|
|
704
|
+
* @param connection - Solana RPC connection
|
|
705
|
+
* @param baseMint - Base token mint address
|
|
706
|
+
* @param quoteMint - Quote token mint address
|
|
707
|
+
* @param amountOut - Desired base token amount out
|
|
708
|
+
* @param config - Liquid protocol configuration
|
|
709
|
+
* @param options - Optional pre-fetched AMM state and solPriceUsd for tier calculation
|
|
710
|
+
* @returns Swap preview with quote in, fees, and price impact
|
|
711
|
+
*
|
|
712
|
+
* @throws Error if neither feeRates nor solPriceUsd is provided for tiered fee systems
|
|
713
|
+
*/
|
|
714
|
+
export async function previewSwapBuy(
|
|
715
|
+
connection: Connection,
|
|
716
|
+
baseMint: PublicKey,
|
|
717
|
+
quoteMint: PublicKey,
|
|
718
|
+
amountOut: BN,
|
|
719
|
+
config: LiquidConfig,
|
|
720
|
+
options?: { prefetched?: SwapPrefetchedState; solPriceUsd?: BN }
|
|
721
|
+
): Promise<SwapPreview> {
|
|
722
|
+
const pre = options?.prefetched;
|
|
723
|
+
|
|
724
|
+
// Fetch pool state if not provided
|
|
725
|
+
const poolState =
|
|
726
|
+
pre?.poolState ??
|
|
727
|
+
(await fetchPoolState(connection, baseMint, quoteMint, config));
|
|
728
|
+
|
|
729
|
+
// Get vault balances
|
|
730
|
+
let baseVaultBalance: BN;
|
|
731
|
+
let quoteVaultBalance: BN;
|
|
732
|
+
|
|
733
|
+
if (pre?.vaultBalances) {
|
|
734
|
+
({ baseVaultBalance, quoteVaultBalance } = pre.vaultBalances);
|
|
735
|
+
} else {
|
|
736
|
+
const [baseVaultAccount, quoteVaultAccount] = await Promise.all([
|
|
737
|
+
getAccount(
|
|
738
|
+
connection,
|
|
739
|
+
poolState.baseVault,
|
|
740
|
+
"confirmed",
|
|
741
|
+
poolState.baseTokenProgram
|
|
742
|
+
),
|
|
743
|
+
getAccount(
|
|
744
|
+
connection,
|
|
745
|
+
poolState.quoteVault,
|
|
746
|
+
"confirmed",
|
|
747
|
+
poolState.quoteTokenProgram
|
|
748
|
+
),
|
|
749
|
+
]);
|
|
750
|
+
baseVaultBalance = new BN(baseVaultAccount.amount.toString());
|
|
751
|
+
quoteVaultBalance = new BN(quoteVaultAccount.amount.toString());
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
// Get fee rates
|
|
755
|
+
let feeRates: {
|
|
756
|
+
lpFeeRate: number;
|
|
757
|
+
creatorFeeRate: number;
|
|
758
|
+
protocolFeeRate: number;
|
|
759
|
+
};
|
|
760
|
+
|
|
761
|
+
if (pre?.feeRates) {
|
|
762
|
+
// Use pre-computed fee rates
|
|
763
|
+
feeRates = pre.feeRates;
|
|
764
|
+
} else if (options?.solPriceUsd) {
|
|
765
|
+
// Calculate fee rates from tiered config
|
|
766
|
+
const ammConfig = await fetchAmmConfig(connection, config);
|
|
767
|
+
const baseMintAccount = await getMint(
|
|
768
|
+
connection,
|
|
769
|
+
poolState.baseMint,
|
|
770
|
+
"confirmed",
|
|
771
|
+
poolState.baseTokenProgram
|
|
772
|
+
);
|
|
773
|
+
const baseTotalSupply = new BN(baseMintAccount.supply.toString());
|
|
774
|
+
|
|
775
|
+
const tiers = ammConfig.feeTiers;
|
|
776
|
+
const [lpFeeRate, protocolFeeRate, creatorFeeRate] =
|
|
777
|
+
calculateFeesForPool(
|
|
778
|
+
quoteVaultBalance,
|
|
779
|
+
baseVaultBalance,
|
|
780
|
+
baseTotalSupply,
|
|
781
|
+
options.solPriceUsd,
|
|
782
|
+
poolState.quoteMint,
|
|
783
|
+
tiers
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
feeRates = { lpFeeRate, protocolFeeRate, creatorFeeRate };
|
|
787
|
+
} else {
|
|
788
|
+
throw new Error(
|
|
789
|
+
"Either prefetched.feeRates or solPriceUsd must be provided for tiered fee calculations"
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
return calculateSwapBuyPreview(amountOut, {
|
|
794
|
+
ammConfig: feeRates,
|
|
795
|
+
baseVaultBalance,
|
|
796
|
+
quoteVaultBalance,
|
|
797
|
+
});
|
|
798
|
+
}
|