@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,106 @@
1
+ import { Program } from "@coral-xyz/anchor";
2
+ import type { LiquidConfig } from "../config.js";
3
+ import { createStubProvider } from "../provider.js";
4
+
5
+ import liquidIdl from "../idl/liquid.json" with { type: "json" };
6
+ import liquidSwapIdl from "../idl/liquid_swap.json" with { type: "json" };
7
+ import liquidFeesIdl from "../idl/liquid_fees.json" with { type: "json" };
8
+ import liquidStateIdl from "../idl/liquid_state.json" with { type: "json" };
9
+
10
+ import type { Liquid } from "../idl/liquid.js";
11
+ import type { LiquidSwap } from "../idl/liquid_swap.js";
12
+ import type { LiquidFees } from "../idl/liquid_fees.js";
13
+ import type { LiquidState } from "../idl/liquid_state.js";
14
+
15
+ /**
16
+ * Cached stub Program instances for instruction building.
17
+ * Instruction building via `.instruction()` never hits the network,
18
+ * so these programs use a shared stub provider with no real Connection.
19
+ */
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ const cache = new Map<string, Program<any>>();
22
+
23
+ /**
24
+ * Returns a cached Program instance or creates one if it doesn't exist.
25
+ *
26
+ * @param key - Cache key prefix (e.g., "liquid", "swap")
27
+ * @param idl - Raw IDL JSON object
28
+ * @param address - Program address as a base58 string
29
+ * @returns Cached or newly created Program instance
30
+ */
31
+ function getOrCreate<T extends Liquid | LiquidSwap | LiquidFees | LiquidState>(
32
+ key: string,
33
+ idl: Record<string, unknown>,
34
+ address: string
35
+ ): Program<T> {
36
+ const cacheKey = `${key}:${address}`;
37
+ let prog = cache.get(cacheKey) as Program<T> | undefined;
38
+ if (!prog) {
39
+ const patchedIdl = { ...idl, address };
40
+ prog = new Program(patchedIdl as T, createStubProvider());
41
+ cache.set(cacheKey, prog);
42
+ }
43
+ return prog;
44
+ }
45
+
46
+ /**
47
+ * Returns a cached Liquid bonding curve program instance for instruction building.
48
+ *
49
+ * @param config - Liquid protocol configuration
50
+ * @returns Cached Program instance typed as Liquid
51
+ */
52
+ export function getCachedLiquidProgram(config: LiquidConfig): Program<Liquid> {
53
+ return getOrCreate<Liquid>(
54
+ "liquid",
55
+ liquidIdl,
56
+ config.liquidProgramId.toBase58()
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Returns a cached Liquid Swap AMM program instance for instruction building.
62
+ *
63
+ * @param config - Liquid protocol configuration
64
+ * @returns Cached Program instance typed as LiquidSwap
65
+ */
66
+ export function getCachedSwapProgram(
67
+ config: LiquidConfig
68
+ ): Program<LiquidSwap> {
69
+ return getOrCreate<LiquidSwap>(
70
+ "swap",
71
+ liquidSwapIdl,
72
+ config.liquidSwapProgramId.toBase58()
73
+ );
74
+ }
75
+
76
+ /**
77
+ * Returns a cached Liquid Fees program instance for instruction building.
78
+ *
79
+ * @param config - Liquid protocol configuration
80
+ * @returns Cached Program instance typed as LiquidFees
81
+ */
82
+ export function getCachedFeesProgram(
83
+ config: LiquidConfig
84
+ ): Program<LiquidFees> {
85
+ return getOrCreate<LiquidFees>(
86
+ "fees",
87
+ liquidFeesIdl,
88
+ config.liquidFeesProgramId.toBase58()
89
+ );
90
+ }
91
+
92
+ /**
93
+ * Returns a cached Liquid State program instance for instruction building.
94
+ *
95
+ * @param config - Liquid protocol configuration
96
+ * @returns Cached Program instance typed as LiquidState
97
+ */
98
+ export function getCachedStateProgram(
99
+ config: LiquidConfig
100
+ ): Program<LiquidState> {
101
+ return getOrCreate<LiquidState>(
102
+ "state",
103
+ liquidStateIdl,
104
+ config.liquidStateProgramId.toBase58()
105
+ );
106
+ }
@@ -0,0 +1,146 @@
1
+ import BN from "bn.js";
2
+ import { FEE_RATE_DENOMINATOR } from "./constants.js";
3
+ import type { AmmSellOutput, AmmBuyInput, LpTokenAmounts } from "../types.js";
4
+
5
+ /** AMM fee rates in basis points */
6
+ export interface AmmFeeRates {
7
+ lpFeeRate: number;
8
+ creatorFeeRate: number;
9
+ protocolFeeRate: number;
10
+ }
11
+
12
+ /**
13
+ * Calculates expected output for selling base tokens on the AMM.
14
+ * Uses constant product formula: quoteOut = (amountIn * quoteVault) / (baseVault + amountIn)
15
+ *
16
+ * @param amountIn - Base tokens to sell
17
+ * @param baseVault - Current base vault balance
18
+ * @param quoteVault - Current quote vault balance
19
+ * @param fees - Fee rates in basis points
20
+ * @returns Sell output with gross/net quote amounts and fee breakdown
21
+ */
22
+ export const calculateAmmSellOutput = (
23
+ amountIn: BN,
24
+ baseVault: BN,
25
+ quoteVault: BN,
26
+ fees: AmmFeeRates
27
+ ): AmmSellOutput => {
28
+ // Gross output: (amountIn * quoteVault) / (baseVault + amountIn)
29
+ const grossQuoteOut = amountIn.mul(quoteVault).div(baseVault.add(amountIn));
30
+
31
+ const denom = new BN(FEE_RATE_DENOMINATOR);
32
+ const lpFee = grossQuoteOut.mul(new BN(fees.lpFeeRate)).div(denom);
33
+ const creatorFee = grossQuoteOut
34
+ .mul(new BN(fees.creatorFeeRate))
35
+ .div(denom);
36
+ const protocolFee = grossQuoteOut
37
+ .mul(new BN(fees.protocolFeeRate))
38
+ .div(denom);
39
+ const totalFees = lpFee.add(creatorFee).add(protocolFee);
40
+
41
+ const quoteOutNet = grossQuoteOut.sub(totalFees);
42
+
43
+ return {
44
+ quoteOut: grossQuoteOut,
45
+ quoteOutNet,
46
+ lpFee,
47
+ creatorFee,
48
+ protocolFee,
49
+ totalFees,
50
+ };
51
+ };
52
+
53
+ /**
54
+ * Calculates required quote input for buying base tokens on the AMM.
55
+ * Reverse calculation: how much quote is needed for the desired base output.
56
+ *
57
+ * @param amountOut - Base tokens to buy
58
+ * @param baseVault - Current base vault balance
59
+ * @param quoteVault - Current quote vault balance
60
+ * @param fees - Fee rates in basis points
61
+ * @returns Buy input with gross/net quote amounts and fee breakdown
62
+ */
63
+ export const calculateAmmBuyInput = (
64
+ amountOut: BN,
65
+ baseVault: BN,
66
+ quoteVault: BN,
67
+ fees: AmmFeeRates
68
+ ): AmmBuyInput => {
69
+ const denom = new BN(FEE_RATE_DENOMINATOR);
70
+
71
+ // Quote needed for swap (without fees), ceiling division
72
+ const quoteForSwap = quoteVault
73
+ .mul(amountOut)
74
+ .div(baseVault.sub(amountOut))
75
+ .add(new BN(1));
76
+
77
+ // Total fee rate
78
+ const totalFeeRate =
79
+ fees.lpFeeRate + fees.creatorFeeRate + fees.protocolFeeRate;
80
+
81
+ // Gross quote needed (including fees), ceiling division
82
+ const quoteInGross = quoteForSwap
83
+ .mul(denom)
84
+ .div(new BN(FEE_RATE_DENOMINATOR - totalFeeRate))
85
+ .add(new BN(1));
86
+
87
+ // Calculate individual fees
88
+ const lpFee = quoteInGross.mul(new BN(fees.lpFeeRate)).div(denom);
89
+ const creatorFee = quoteInGross.mul(new BN(fees.creatorFeeRate)).div(denom);
90
+ const protocolFee = quoteInGross
91
+ .mul(new BN(fees.protocolFeeRate))
92
+ .div(denom);
93
+ const totalFees = lpFee.add(creatorFee).add(protocolFee);
94
+
95
+ const quoteIn = quoteInGross.sub(totalFees);
96
+
97
+ return {
98
+ quoteIn,
99
+ quoteInGross,
100
+ lpFee,
101
+ creatorFee,
102
+ protocolFee,
103
+ totalFees,
104
+ };
105
+ };
106
+
107
+ /**
108
+ * Converts LP tokens to the equivalent base and quote token amounts.
109
+ *
110
+ * @param lpAmount - LP tokens to convert
111
+ * @param lpSupply - Total LP supply
112
+ * @param baseVault - Base vault balance
113
+ * @param quoteVault - Quote vault balance
114
+ * @param roundUp - Use ceiling division (for deposits) vs floor division (for withdrawals)
115
+ * @returns Equivalent base and quote token amounts for the given LP tokens
116
+ */
117
+ export const calculateLpToTokens = (
118
+ lpAmount: BN,
119
+ lpSupply: BN,
120
+ baseVault: BN,
121
+ quoteVault: BN,
122
+ roundUp: boolean = false
123
+ ): LpTokenAmounts => {
124
+ let baseAmount: BN;
125
+ let quoteAmount: BN;
126
+
127
+ if (roundUp) {
128
+ // Ceiling division for deposits (require slightly more from user)
129
+ baseAmount = lpAmount
130
+ .mul(baseVault)
131
+ .add(lpSupply)
132
+ .sub(new BN(1))
133
+ .div(lpSupply);
134
+ quoteAmount = lpAmount
135
+ .mul(quoteVault)
136
+ .add(lpSupply)
137
+ .sub(new BN(1))
138
+ .div(lpSupply);
139
+ } else {
140
+ // Floor division for withdrawals (give slightly less to user)
141
+ baseAmount = lpAmount.mul(baseVault).div(lpSupply);
142
+ quoteAmount = lpAmount.mul(quoteVault).div(lpSupply);
143
+ }
144
+
145
+ return { baseAmount, quoteAmount };
146
+ };
@@ -0,0 +1,122 @@
1
+ import BN from "bn.js";
2
+ import type { BondingCurveBuyResult, BondingCurveSellResult } from "../types.js";
3
+ import { calculateFees, type FeeConfig } from "./fees.js";
4
+
5
+ /**
6
+ * Calculates expected output for a buy transaction on the bonding curve.
7
+ * Uses constant product formula: k = virtualQuote * virtualToken
8
+ *
9
+ * @param amountInGross - Total quote amount the user is spending (before fees)
10
+ * @param curveState - Current curve reserves
11
+ * @param config - Fee configuration
12
+ * @param hasCreatorRef - Whether the creator has a referrer
13
+ * @param hasTraderRef - Whether the trader has a referrer
14
+ * @returns Buy result with tokens out, new reserves, net amount, and fee breakdown
15
+ */
16
+ export const calculateBuyExpectation = (
17
+ amountInGross: BN,
18
+ curveState: { virtualQuoteReserves: BN; virtualTokenReserves: BN },
19
+ config: FeeConfig,
20
+ hasCreatorRef: boolean,
21
+ hasTraderRef: boolean
22
+ ): BondingCurveBuyResult => {
23
+ const fees = calculateFees(
24
+ amountInGross,
25
+ config,
26
+ hasCreatorRef,
27
+ hasTraderRef
28
+ );
29
+
30
+ // Net quote amount that goes into the curve after fees
31
+ const amountInNet = amountInGross.sub(fees.totalFees);
32
+
33
+ // Constant product: k = x * y
34
+ const k = curveState.virtualQuoteReserves.mul(
35
+ curveState.virtualTokenReserves
36
+ );
37
+
38
+ // New quote reserves after adding net amount
39
+ const newVirtualQuote = curveState.virtualQuoteReserves.add(amountInNet);
40
+
41
+ // Solve for new token reserves: newToken = k / newQuote + 1 (ceiling division)
42
+ const newVirtualToken = k.div(newVirtualQuote).add(new BN(1));
43
+
44
+ // Tokens out = difference in token reserves
45
+ const tokensOut = curveState.virtualTokenReserves.sub(newVirtualToken);
46
+
47
+ // On-chain, protocol fee is only reduced by referral amounts that are
48
+ // actually transferred. These helpers assume referrals succeed, so
49
+ // subtract them to match the expected on-chain protocol fee.
50
+ const effectiveProtocolFee = fees.protocolFee
51
+ .sub(fees.creatorReferralFee)
52
+ .sub(fees.traderReferralFee);
53
+
54
+ return {
55
+ ...fees,
56
+ protocolFee: effectiveProtocolFee,
57
+ tokensOut,
58
+ newVirtualQuote,
59
+ newVirtualToken,
60
+ amountInNet,
61
+ };
62
+ };
63
+
64
+ /**
65
+ * Calculates expected output for a sell transaction on the bonding curve.
66
+ * Uses constant product formula: k = virtualQuote * virtualToken
67
+ *
68
+ * @param amountInTokens - Amount of tokens to sell
69
+ * @param curveState - Current curve reserves
70
+ * @param config - Fee configuration
71
+ * @param hasCreatorRef - Whether the creator has a referrer
72
+ * @param hasTraderRef - Whether the trader has a referrer
73
+ * @returns Sell result with quote out (gross/net), new reserves, and fee breakdown
74
+ */
75
+ export const calculateSellExpectation = (
76
+ amountInTokens: BN,
77
+ curveState: { virtualQuoteReserves: BN; virtualTokenReserves: BN },
78
+ config: FeeConfig,
79
+ hasCreatorRef: boolean,
80
+ hasTraderRef: boolean
81
+ ): BondingCurveSellResult => {
82
+ // Constant product: k = x * y
83
+ const k = curveState.virtualQuoteReserves.mul(
84
+ curveState.virtualTokenReserves
85
+ );
86
+
87
+ // New token reserves after adding sold tokens
88
+ const newVirtualToken = curveState.virtualTokenReserves.add(amountInTokens);
89
+
90
+ // Solve for new quote reserves: newQuote = k / newToken + 1 (ceiling division)
91
+ const newVirtualQuote = k.div(newVirtualToken).add(new BN(1));
92
+
93
+ // Gross quote out (before fees)
94
+ const quoteOutGross = curveState.virtualQuoteReserves.sub(newVirtualQuote);
95
+
96
+ // Calculate fees on gross amount
97
+ const fees = calculateFees(
98
+ quoteOutGross,
99
+ config,
100
+ hasCreatorRef,
101
+ hasTraderRef
102
+ );
103
+
104
+ // Net quote amount user receives
105
+ const quoteOutNet = quoteOutGross.sub(fees.totalFees);
106
+
107
+ // On-chain, protocol fee is only reduced by referral amounts that are
108
+ // actually transferred. These helpers assume referrals succeed, so
109
+ // subtract them to match the expected on-chain protocol fee.
110
+ const effectiveProtocolFee = fees.protocolFee
111
+ .sub(fees.creatorReferralFee)
112
+ .sub(fees.traderReferralFee);
113
+
114
+ return {
115
+ ...fees,
116
+ protocolFee: effectiveProtocolFee,
117
+ quoteOutGross,
118
+ quoteOutNet,
119
+ newVirtualQuote,
120
+ newVirtualToken,
121
+ };
122
+ };
@@ -0,0 +1,19 @@
1
+ import BN from "bn.js";
2
+
3
+ /** Basis points denominator (10,000 = 100%) */
4
+ export const BPS_DENOMINATOR = 10_000;
5
+
6
+ /** Fee rate denominator for AMM (same as BPS) */
7
+ export const FEE_RATE_DENOMINATOR = 10_000;
8
+
9
+ /** Q32 constant for fixed-point oracle math (2^32) */
10
+ export const Q32 = new BN(1).shln(32);
11
+
12
+ /** Number of oracle observations stored */
13
+ export const OBSERVATION_NUM = 100;
14
+
15
+ /** Default oracle update duration in seconds */
16
+ export const OBSERVATION_UPDATE_DURATION_DEFAULT = 15;
17
+
18
+ /** Minimum rent-exempt balance for a 0-byte account */
19
+ export const MIN_VAULT_BALANCE = 890_880;
@@ -0,0 +1,191 @@
1
+ import BN from "bn.js";
2
+ import type { PublicKey } from "@solana/web3.js";
3
+ import { BPS_DENOMINATOR, MIN_VAULT_BALANCE } from "./constants.js";
4
+ import type {
5
+ FeeDistribution,
6
+ FeeRecipient,
7
+ DistributionResult,
8
+ } from "../types.js";
9
+
10
+ /**
11
+ * Calculates basis points of an amount.
12
+ * result = (amount * bps) / 10000
13
+ *
14
+ * @param amount - The amount to calculate basis points of
15
+ * @param bps - Basis points (1 bps = 0.01%)
16
+ * @returns The calculated portion
17
+ */
18
+ export const calcBps = (amount: BN, bps: number): BN =>
19
+ amount.mul(new BN(bps)).div(new BN(BPS_DENOMINATOR));
20
+
21
+ /**
22
+ * Fee configuration for bonding curve trades.
23
+ */
24
+ export interface FeeConfig {
25
+ protocolFeeBps: number;
26
+ creatorFeeBps: number;
27
+ creatorReferralBps: number;
28
+ traderReferralBps: number;
29
+ }
30
+
31
+ /**
32
+ * Calculates fee distribution for a given trade amount.
33
+ * Referral fees are carved from the protocol fee, not the trade amount.
34
+ *
35
+ * @param amount - Gross trade amount (before fees)
36
+ * @param config - Fee configuration in basis points
37
+ * @param hasCreatorRef - Whether the creator has a referrer
38
+ * @param hasTraderRef - Whether the trader has a referrer
39
+ * @returns Fee distribution breakdown
40
+ */
41
+ export const calculateFees = (
42
+ amount: BN,
43
+ config: FeeConfig,
44
+ hasCreatorRef: boolean,
45
+ hasTraderRef: boolean
46
+ ): FeeDistribution => {
47
+ const baseProtocolFee = calcBps(amount, config.protocolFeeBps);
48
+ const creatorFee = calcBps(amount, config.creatorFeeBps);
49
+
50
+ // Referral amounts are BPS of the protocol fee (not trade amount)
51
+ const creatorReferralFee = hasCreatorRef
52
+ ? calcBps(baseProtocolFee, config.creatorReferralBps)
53
+ : new BN(0);
54
+ const traderReferralFee = hasTraderRef
55
+ ? calcBps(baseProtocolFee, config.traderReferralBps)
56
+ : new BN(0);
57
+
58
+ // Protocol fee starts at base amount. On-chain, it is only reduced
59
+ // when referral transfers actually succeed.
60
+ const protocolFee = baseProtocolFee;
61
+ // Referrals are carved from protocol fee (not additive), so total = protocol + creator.
62
+ const totalFees = protocolFee.add(creatorFee);
63
+
64
+ return {
65
+ protocolFee,
66
+ creatorFee,
67
+ creatorReferralFee,
68
+ traderReferralFee,
69
+ totalFees,
70
+ };
71
+ };
72
+
73
+ /**
74
+ * Calculates expected distribution amounts for each fee recipient.
75
+ * Distributable = vaultBalance - MIN_VAULT_BALANCE (rent).
76
+ *
77
+ * @param vaultBalance - Current vault balance in lamports
78
+ * @param recipients - Fee recipients with basis point allocations
79
+ * @returns Distribution amounts per recipient
80
+ */
81
+ export const calculateDistribution = (
82
+ vaultBalance: number,
83
+ recipients: FeeRecipient[]
84
+ ): DistributionResult[] => {
85
+ const distributable = Math.max(0, vaultBalance - MIN_VAULT_BALANCE);
86
+ return recipients.map((r) => ({
87
+ pubkey: r.pubkey,
88
+ amount: Math.floor((distributable * r.basisPoints) / BPS_DENOMINATOR),
89
+ }));
90
+ };
91
+
92
+ /**
93
+ * Calculates total distribution (sum of all recipient amounts).
94
+ *
95
+ * @param vaultBalance - Current vault balance in lamports
96
+ * @param recipients - Fee recipients with basis point allocations
97
+ * @returns Total amount distributed across all recipients
98
+ */
99
+ export const calculateTotalDistribution = (
100
+ vaultBalance: number,
101
+ recipients: FeeRecipient[]
102
+ ): number => {
103
+ return calculateDistribution(vaultBalance, recipients).reduce(
104
+ (sum, r) => sum + r.amount,
105
+ 0
106
+ );
107
+ };
108
+
109
+ /**
110
+ * Calculates dust remaining after distribution (due to integer rounding).
111
+ *
112
+ * @param vaultBalance - Current vault balance in lamports
113
+ * @param recipients - Fee recipients with basis point allocations
114
+ * @returns Undistributed lamports remaining in the vault
115
+ */
116
+ export const calculateDistributionDust = (
117
+ vaultBalance: number,
118
+ recipients: FeeRecipient[]
119
+ ): number => {
120
+ const distributable = Math.max(0, vaultBalance - MIN_VAULT_BALANCE);
121
+ const totalDistributed = calculateTotalDistribution(
122
+ vaultBalance,
123
+ recipients
124
+ );
125
+ return distributable - totalDistributed;
126
+ };
127
+
128
+ /**
129
+ * Creates a recipients array from pubkeys and basis points.
130
+ *
131
+ * @param pubkeys - Recipient public keys
132
+ * @param bpsArray - Basis point allocations (must match pubkeys length)
133
+ * @returns Array of fee recipients
134
+ */
135
+ export const createRecipients = (
136
+ pubkeys: PublicKey[],
137
+ bpsArray: number[]
138
+ ): FeeRecipient[] => {
139
+ if (pubkeys.length !== bpsArray.length) {
140
+ throw new Error("pubkeys and bpsArray must have the same length");
141
+ }
142
+ return pubkeys.map((pubkey, i) => ({
143
+ pubkey,
144
+ basisPoints: bpsArray[i],
145
+ }));
146
+ };
147
+
148
+ /**
149
+ * Creates equal-share recipients (auto-calculates BPS to sum to 10,000).
150
+ *
151
+ * @param count - Number of recipients
152
+ * @param pubkeys - Recipient public keys (must have at least `count` entries)
153
+ * @returns Array of fee recipients with equal allocations
154
+ */
155
+ export const createEqualRecipients = (
156
+ count: number,
157
+ pubkeys: PublicKey[]
158
+ ): FeeRecipient[] => {
159
+ if (pubkeys.length < count) {
160
+ throw new Error(
161
+ `Need at least ${count} pubkeys, got ${pubkeys.length}`
162
+ );
163
+ }
164
+ const bpsPerRecipient = Math.floor(BPS_DENOMINATOR / count);
165
+ const remainder = BPS_DENOMINATOR % count;
166
+ return pubkeys.slice(0, count).map((pubkey, i) => ({
167
+ pubkey,
168
+ basisPoints: i === 0 ? bpsPerRecipient + remainder : bpsPerRecipient,
169
+ }));
170
+ };
171
+
172
+ /**
173
+ * Creates a single recipient with 100% allocation.
174
+ *
175
+ * @param pubkey - Recipient public key
176
+ * @returns Array with a single fee recipient at 10,000 BPS
177
+ */
178
+ export const createSingleRecipient = (pubkey: PublicKey): FeeRecipient[] => {
179
+ return [{ pubkey, basisPoints: BPS_DENOMINATOR }];
180
+ };
181
+
182
+ /**
183
+ * Validates that recipients sum to exactly 10,000 BPS.
184
+ *
185
+ * @param recipients - Fee recipients to validate
186
+ * @returns True if basis points sum to exactly 10,000
187
+ */
188
+ export const validateRecipientsSum = (recipients: FeeRecipient[]): boolean => {
189
+ const sum = recipients.reduce((acc, r) => acc + r.basisPoints, 0);
190
+ return sum === BPS_DENOMINATOR;
191
+ };
@@ -0,0 +1,40 @@
1
+ export {
2
+ BPS_DENOMINATOR,
3
+ FEE_RATE_DENOMINATOR,
4
+ Q32,
5
+ OBSERVATION_NUM,
6
+ OBSERVATION_UPDATE_DURATION_DEFAULT,
7
+ MIN_VAULT_BALANCE,
8
+ } from "./constants.js";
9
+
10
+ export {
11
+ calcBps,
12
+ calculateFees,
13
+ calculateDistribution,
14
+ calculateTotalDistribution,
15
+ calculateDistributionDust,
16
+ createRecipients,
17
+ createEqualRecipients,
18
+ createSingleRecipient,
19
+ validateRecipientsSum,
20
+ } from "./fees.js";
21
+ export type { FeeConfig } from "./fees.js";
22
+
23
+ export {
24
+ calculateBuyExpectation,
25
+ calculateSellExpectation,
26
+ } from "./bonding-curve.js";
27
+
28
+ export {
29
+ calculateAmmSellOutput,
30
+ calculateAmmBuyInput,
31
+ calculateLpToTokens,
32
+ } from "./amm.js";
33
+ export type { AmmFeeRates } from "./amm.js";
34
+
35
+ export {
36
+ calculatePoolMarketCap,
37
+ lookupFeeTier,
38
+ calculateFeesForPool,
39
+ WSOL_MINT,
40
+ } from "./tiered-fees.js";