@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.
Files changed (225) hide show
  1. package/README.md +776 -0
  2. package/dist/accounts/index.d.ts +5 -0
  3. package/dist/accounts/index.d.ts.map +1 -0
  4. package/dist/accounts/index.js +5 -0
  5. package/dist/accounts/index.js.map +1 -0
  6. package/dist/accounts/liquid-fees.d.ts +63 -0
  7. package/dist/accounts/liquid-fees.d.ts.map +1 -0
  8. package/dist/accounts/liquid-fees.js +27 -0
  9. package/dist/accounts/liquid-fees.js.map +1 -0
  10. package/dist/accounts/liquid-state.d.ts +112 -0
  11. package/dist/accounts/liquid-state.d.ts.map +1 -0
  12. package/dist/accounts/liquid-state.js +92 -0
  13. package/dist/accounts/liquid-state.js.map +1 -0
  14. package/dist/accounts/liquid-swap.d.ts +97 -0
  15. package/dist/accounts/liquid-swap.d.ts.map +1 -0
  16. package/dist/accounts/liquid-swap.js +54 -0
  17. package/dist/accounts/liquid-swap.js.map +1 -0
  18. package/dist/accounts/liquid.d.ts +175 -0
  19. package/dist/accounts/liquid.d.ts.map +1 -0
  20. package/dist/accounts/liquid.js +66 -0
  21. package/dist/accounts/liquid.js.map +1 -0
  22. package/dist/client.d.ts +621 -0
  23. package/dist/client.d.ts.map +1 -0
  24. package/dist/client.js +511 -0
  25. package/dist/client.js.map +1 -0
  26. package/dist/config.d.ts +56 -0
  27. package/dist/config.d.ts.map +1 -0
  28. package/dist/config.js +44 -0
  29. package/dist/config.js.map +1 -0
  30. package/dist/errors.d.ts +39 -0
  31. package/dist/errors.d.ts.map +1 -0
  32. package/dist/errors.js +63 -0
  33. package/dist/errors.js.map +1 -0
  34. package/dist/events/index.d.ts +4 -0
  35. package/dist/events/index.d.ts.map +1 -0
  36. package/dist/events/index.js +2 -0
  37. package/dist/events/index.js.map +1 -0
  38. package/dist/events/parser.d.ts +40 -0
  39. package/dist/events/parser.d.ts.map +1 -0
  40. package/dist/events/parser.js +67 -0
  41. package/dist/events/parser.js.map +1 -0
  42. package/dist/events/types.d.ts +286 -0
  43. package/dist/events/types.d.ts.map +1 -0
  44. package/dist/events/types.js +2 -0
  45. package/dist/events/types.js.map +1 -0
  46. package/dist/helpers/index.d.ts +4 -0
  47. package/dist/helpers/index.d.ts.map +1 -0
  48. package/dist/helpers/index.js +3 -0
  49. package/dist/helpers/index.js.map +1 -0
  50. package/dist/helpers/preview.d.ts +259 -0
  51. package/dist/helpers/preview.d.ts.map +1 -0
  52. package/dist/helpers/preview.js +458 -0
  53. package/dist/helpers/preview.js.map +1 -0
  54. package/dist/helpers/user.d.ts +11 -0
  55. package/dist/helpers/user.d.ts.map +1 -0
  56. package/dist/helpers/user.js +20 -0
  57. package/dist/helpers/user.js.map +1 -0
  58. package/dist/idl/index.d.ts +53 -0
  59. package/dist/idl/index.d.ts.map +1 -0
  60. package/dist/idl/index.js +64 -0
  61. package/dist/idl/index.js.map +1 -0
  62. package/dist/idl/liquid.d.ts +10523 -0
  63. package/dist/idl/liquid.d.ts.map +1 -0
  64. package/dist/idl/liquid.js +2 -0
  65. package/dist/idl/liquid.js.map +1 -0
  66. package/dist/idl/liquid.json +10516 -0
  67. package/dist/idl/liquid_fees.d.ts +1520 -0
  68. package/dist/idl/liquid_fees.d.ts.map +1 -0
  69. package/dist/idl/liquid_fees.js +2 -0
  70. package/dist/idl/liquid_fees.js.map +1 -0
  71. package/dist/idl/liquid_fees.json +1513 -0
  72. package/dist/idl/liquid_state.d.ts +2936 -0
  73. package/dist/idl/liquid_state.d.ts.map +1 -0
  74. package/dist/idl/liquid_state.js +2 -0
  75. package/dist/idl/liquid_state.js.map +1 -0
  76. package/dist/idl/liquid_state.json +2929 -0
  77. package/dist/idl/liquid_swap.d.ts +5849 -0
  78. package/dist/idl/liquid_swap.d.ts.map +1 -0
  79. package/dist/idl/liquid_swap.js +2 -0
  80. package/dist/idl/liquid_swap.js.map +1 -0
  81. package/dist/idl/liquid_swap.json +5842 -0
  82. package/dist/index.d.ts +19 -0
  83. package/dist/index.d.ts.map +1 -0
  84. package/dist/index.js +15 -0
  85. package/dist/index.js.map +1 -0
  86. package/dist/instructions/index.d.ts +9 -0
  87. package/dist/instructions/index.d.ts.map +1 -0
  88. package/dist/instructions/index.js +9 -0
  89. package/dist/instructions/index.js.map +1 -0
  90. package/dist/instructions/liquid-fees.d.ts +111 -0
  91. package/dist/instructions/liquid-fees.d.ts.map +1 -0
  92. package/dist/instructions/liquid-fees.js +169 -0
  93. package/dist/instructions/liquid-fees.js.map +1 -0
  94. package/dist/instructions/liquid-state.d.ts +173 -0
  95. package/dist/instructions/liquid-state.d.ts.map +1 -0
  96. package/dist/instructions/liquid-state.js +194 -0
  97. package/dist/instructions/liquid-state.js.map +1 -0
  98. package/dist/instructions/liquid-swap.d.ts +122 -0
  99. package/dist/instructions/liquid-swap.d.ts.map +1 -0
  100. package/dist/instructions/liquid-swap.js +167 -0
  101. package/dist/instructions/liquid-swap.js.map +1 -0
  102. package/dist/instructions/liquid.d.ts +297 -0
  103. package/dist/instructions/liquid.d.ts.map +1 -0
  104. package/dist/instructions/liquid.js +483 -0
  105. package/dist/instructions/liquid.js.map +1 -0
  106. package/dist/instructions/program-cache.d.ts +35 -0
  107. package/dist/instructions/program-cache.d.ts.map +1 -0
  108. package/dist/instructions/program-cache.js +68 -0
  109. package/dist/instructions/program-cache.js.map +1 -0
  110. package/dist/math/amm.d.ts +42 -0
  111. package/dist/math/amm.d.ts.map +1 -0
  112. package/dist/math/amm.js +109 -0
  113. package/dist/math/amm.js.map +1 -0
  114. package/dist/math/bonding-curve.d.ts +34 -0
  115. package/dist/math/bonding-curve.d.ts.map +1 -0
  116. package/dist/math/bonding-curve.js +80 -0
  117. package/dist/math/bonding-curve.js.map +1 -0
  118. package/dist/math/constants.d.ts +14 -0
  119. package/dist/math/constants.d.ts.map +1 -0
  120. package/dist/math/constants.js +14 -0
  121. package/dist/math/constants.js.map +1 -0
  122. package/dist/math/fees.d.ts +88 -0
  123. package/dist/math/fees.d.ts.map +1 -0
  124. package/dist/math/fees.js +135 -0
  125. package/dist/math/fees.js.map +1 -0
  126. package/dist/math/index.d.ts +8 -0
  127. package/dist/math/index.d.ts.map +1 -0
  128. package/dist/math/index.js +6 -0
  129. package/dist/math/index.js.map +1 -0
  130. package/dist/math/tiered-fees.d.ts +80 -0
  131. package/dist/math/tiered-fees.d.ts.map +1 -0
  132. package/dist/math/tiered-fees.js +129 -0
  133. package/dist/math/tiered-fees.js.map +1 -0
  134. package/dist/oracle.d.ts +53 -0
  135. package/dist/oracle.d.ts.map +1 -0
  136. package/dist/oracle.js +70 -0
  137. package/dist/oracle.js.map +1 -0
  138. package/dist/pda/index.d.ts +89 -0
  139. package/dist/pda/index.d.ts.map +1 -0
  140. package/dist/pda/index.js +127 -0
  141. package/dist/pda/index.js.map +1 -0
  142. package/dist/pda/liquid-fees.d.ts +27 -0
  143. package/dist/pda/liquid-fees.d.ts.map +1 -0
  144. package/dist/pda/liquid-fees.js +36 -0
  145. package/dist/pda/liquid-fees.js.map +1 -0
  146. package/dist/pda/liquid-state.d.ts +56 -0
  147. package/dist/pda/liquid-state.d.ts.map +1 -0
  148. package/dist/pda/liquid-state.js +79 -0
  149. package/dist/pda/liquid-state.js.map +1 -0
  150. package/dist/pda/liquid-swap.d.ts +76 -0
  151. package/dist/pda/liquid-swap.d.ts.map +1 -0
  152. package/dist/pda/liquid-swap.js +103 -0
  153. package/dist/pda/liquid-swap.js.map +1 -0
  154. package/dist/pda/liquid.d.ts +67 -0
  155. package/dist/pda/liquid.d.ts.map +1 -0
  156. package/dist/pda/liquid.js +91 -0
  157. package/dist/pda/liquid.js.map +1 -0
  158. package/dist/provider.d.ts +26 -0
  159. package/dist/provider.d.ts.map +1 -0
  160. package/dist/provider.js +47 -0
  161. package/dist/provider.js.map +1 -0
  162. package/dist/transaction/builder.d.ts +30 -0
  163. package/dist/transaction/builder.d.ts.map +1 -0
  164. package/dist/transaction/builder.js +48 -0
  165. package/dist/transaction/builder.js.map +1 -0
  166. package/dist/transaction/index.d.ts +3 -0
  167. package/dist/transaction/index.d.ts.map +1 -0
  168. package/dist/transaction/index.js +3 -0
  169. package/dist/transaction/index.js.map +1 -0
  170. package/dist/transaction/send.d.ts +25 -0
  171. package/dist/transaction/send.d.ts.map +1 -0
  172. package/dist/transaction/send.js +52 -0
  173. package/dist/transaction/send.js.map +1 -0
  174. package/dist/types.d.ts +311 -0
  175. package/dist/types.d.ts.map +1 -0
  176. package/dist/types.js +46 -0
  177. package/dist/types.js.map +1 -0
  178. package/package.json +112 -0
  179. package/src/accounts/index.ts +26 -0
  180. package/src/accounts/liquid-fees.ts +38 -0
  181. package/src/accounts/liquid-state.ts +134 -0
  182. package/src/accounts/liquid-swap.ts +79 -0
  183. package/src/accounts/liquid.ts +100 -0
  184. package/src/client.ts +1001 -0
  185. package/src/config.ts +91 -0
  186. package/src/errors.ts +94 -0
  187. package/src/events/index.ts +42 -0
  188. package/src/events/parser.ts +90 -0
  189. package/src/events/types.ts +310 -0
  190. package/src/helpers/index.ts +23 -0
  191. package/src/helpers/preview.ts +798 -0
  192. package/src/helpers/user.ts +24 -0
  193. package/src/idl/index.ts +94 -0
  194. package/src/idl/liquid.json +10516 -0
  195. package/src/idl/liquid.ts +10522 -0
  196. package/src/idl/liquid_fees.json +1513 -0
  197. package/src/idl/liquid_fees.ts +1519 -0
  198. package/src/idl/liquid_state.json +2929 -0
  199. package/src/idl/liquid_state.ts +2935 -0
  200. package/src/idl/liquid_swap.json +5842 -0
  201. package/src/idl/liquid_swap.ts +5848 -0
  202. package/src/index.ts +98 -0
  203. package/src/instructions/index.ts +109 -0
  204. package/src/instructions/liquid-fees.ts +289 -0
  205. package/src/instructions/liquid-state.ts +336 -0
  206. package/src/instructions/liquid-swap.ts +414 -0
  207. package/src/instructions/liquid.ts +884 -0
  208. package/src/instructions/program-cache.ts +106 -0
  209. package/src/math/amm.ts +146 -0
  210. package/src/math/bonding-curve.ts +122 -0
  211. package/src/math/constants.ts +19 -0
  212. package/src/math/fees.ts +191 -0
  213. package/src/math/index.ts +40 -0
  214. package/src/math/tiered-fees.ts +165 -0
  215. package/src/oracle.ts +97 -0
  216. package/src/pda/index.ts +331 -0
  217. package/src/pda/liquid-fees.ts +58 -0
  218. package/src/pda/liquid-state.ts +123 -0
  219. package/src/pda/liquid-swap.ts +162 -0
  220. package/src/pda/liquid.ts +152 -0
  221. package/src/provider.ts +60 -0
  222. package/src/transaction/builder.ts +80 -0
  223. package/src/transaction/index.ts +6 -0
  224. package/src/transaction/send.ts +72 -0
  225. 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
+ }