@cubee_ee/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/LICENSE +21 -0
- package/README.md +110 -0
- package/dist/clients/AdminClient.d.ts +61 -0
- package/dist/clients/AdminClient.d.ts.map +1 -0
- package/dist/clients/AdminClient.js +196 -0
- package/dist/clients/AdminClient.js.map +1 -0
- package/dist/clients/CubeBackendClient.d.ts +67 -0
- package/dist/clients/CubeBackendClient.d.ts.map +1 -0
- package/dist/clients/CubeBackendClient.js +126 -0
- package/dist/clients/CubeBackendClient.js.map +1 -0
- package/dist/clients/CubicPoolClient.d.ts +73 -0
- package/dist/clients/CubicPoolClient.d.ts.map +1 -0
- package/dist/clients/CubicPoolClient.js +425 -0
- package/dist/clients/CubicPoolClient.js.map +1 -0
- package/dist/clients/PoolFactoryClient.d.ts +45 -0
- package/dist/clients/PoolFactoryClient.d.ts.map +1 -0
- package/dist/clients/PoolFactoryClient.js +83 -0
- package/dist/clients/PoolFactoryClient.js.map +1 -0
- package/dist/clients/RpcClient.d.ts +28 -0
- package/dist/clients/RpcClient.d.ts.map +1 -0
- package/dist/clients/RpcClient.js +58 -0
- package/dist/clients/RpcClient.js.map +1 -0
- package/dist/clients/SingleTokenDepositClient.d.ts +45 -0
- package/dist/clients/SingleTokenDepositClient.d.ts.map +1 -0
- package/dist/clients/SingleTokenDepositClient.js +63 -0
- package/dist/clients/SingleTokenDepositClient.js.map +1 -0
- package/dist/clients/index.d.ts +8 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +24 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/tx-builders.d.ts +32 -0
- package/dist/clients/tx-builders.d.ts.map +1 -0
- package/dist/clients/tx-builders.js +398 -0
- package/dist/clients/tx-builders.js.map +1 -0
- package/dist/config/index.d.ts +60 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +59 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/networks.d.ts +10 -0
- package/dist/config/networks.d.ts.map +1 -0
- package/dist/config/networks.js +31 -0
- package/dist/config/networks.js.map +1 -0
- package/dist/config/tokens.d.ts +19 -0
- package/dist/config/tokens.d.ts.map +1 -0
- package/dist/config/tokens.js +43 -0
- package/dist/config/tokens.js.map +1 -0
- package/dist/idl/cubic_pool.json +2497 -0
- package/dist/idl/index.d.ts +975 -0
- package/dist/idl/index.d.ts.map +1 -0
- package/dist/idl/index.js +18 -0
- package/dist/idl/index.js.map +1 -0
- package/dist/idl/protocol_admin.json +1816 -0
- package/dist/idl/single_token_liquidity.json +745 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +136 -0
- package/dist/index.js.map +1 -0
- package/dist/math/cubicMath.d.ts +40 -0
- package/dist/math/cubicMath.d.ts.map +1 -0
- package/dist/math/cubicMath.js +75 -0
- package/dist/math/cubicMath.js.map +1 -0
- package/dist/math/fixedPoint.d.ts +14 -0
- package/dist/math/fixedPoint.d.ts.map +1 -0
- package/dist/math/fixedPoint.js +46 -0
- package/dist/math/fixedPoint.js.map +1 -0
- package/dist/math/index.d.ts +7 -0
- package/dist/math/index.d.ts.map +1 -0
- package/dist/math/index.js +23 -0
- package/dist/math/index.js.map +1 -0
- package/dist/math/logExp.d.ts +15 -0
- package/dist/math/logExp.d.ts.map +1 -0
- package/dist/math/logExp.js +91 -0
- package/dist/math/logExp.js.map +1 -0
- package/dist/math/singleToken.d.ts +53 -0
- package/dist/math/singleToken.d.ts.map +1 -0
- package/dist/math/singleToken.js +185 -0
- package/dist/math/singleToken.js.map +1 -0
- package/dist/math/slippage.d.ts +24 -0
- package/dist/math/slippage.d.ts.map +1 -0
- package/dist/math/slippage.js +54 -0
- package/dist/math/slippage.js.map +1 -0
- package/dist/math/weightedMath.d.ts +18 -0
- package/dist/math/weightedMath.d.ts.map +1 -0
- package/dist/math/weightedMath.js +45 -0
- package/dist/math/weightedMath.js.map +1 -0
- package/dist/parsers/borsh.d.ts +24 -0
- package/dist/parsers/borsh.d.ts.map +1 -0
- package/dist/parsers/borsh.js +80 -0
- package/dist/parsers/borsh.js.map +1 -0
- package/dist/parsers/events.d.ts +3 -0
- package/dist/parsers/events.d.ts.map +1 -0
- package/dist/parsers/events.js +172 -0
- package/dist/parsers/events.js.map +1 -0
- package/dist/parsers/index.d.ts +5 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +21 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/mintAccount.d.ts +22 -0
- package/dist/parsers/mintAccount.d.ts.map +1 -0
- package/dist/parsers/mintAccount.js +22 -0
- package/dist/parsers/mintAccount.js.map +1 -0
- package/dist/parsers/poolAccount.d.ts +32 -0
- package/dist/parsers/poolAccount.d.ts.map +1 -0
- package/dist/parsers/poolAccount.js +88 -0
- package/dist/parsers/poolAccount.js.map +1 -0
- package/dist/types/events.d.ts +82 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +3 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +21 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/pool.d.ts +66 -0
- package/dist/types/pool.d.ts.map +1 -0
- package/dist/types/pool.js +3 -0
- package/dist/types/pool.js.map +1 -0
- package/dist/types/result.d.ts +23 -0
- package/dist/types/result.d.ts.map +1 -0
- package/dist/types/result.js +11 -0
- package/dist/types/result.js.map +1 -0
- package/dist/types/tx.d.ts +80 -0
- package/dist/types/tx.d.ts.map +1 -0
- package/dist/types/tx.js +3 -0
- package/dist/types/tx.js.map +1 -0
- package/dist/utils/errors.d.ts +8 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +83 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +20 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/pda.d.ts +8 -0
- package/dist/utils/pda.d.ts.map +1 -0
- package/dist/utils/pda.js +27 -0
- package/dist/utils/pda.js.map +1 -0
- package/dist/utils/retry.d.ts +22 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +62 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +54 -0
- package/src/clients/AdminClient.ts +254 -0
- package/src/clients/CubeBackendClient.ts +193 -0
- package/src/clients/CubicPoolClient.ts +492 -0
- package/src/clients/PoolFactoryClient.ts +102 -0
- package/src/clients/RpcClient.ts +78 -0
- package/src/clients/SingleTokenDepositClient.ts +91 -0
- package/src/clients/index.ts +7 -0
- package/src/clients/tx-builders.ts +538 -0
- package/src/config/index.ts +82 -0
- package/src/config/networks.ts +49 -0
- package/src/config/tokens.ts +52 -0
- package/src/idl/cubic_pool.json +2497 -0
- package/src/idl/index.ts +13 -0
- package/src/idl/protocol_admin.json +1816 -0
- package/src/idl/single_token_liquidity.json +745 -0
- package/src/index.ts +154 -0
- package/src/math/cubicMath.ts +89 -0
- package/src/math/fixedPoint.ts +39 -0
- package/src/math/index.ts +6 -0
- package/src/math/logExp.ts +82 -0
- package/src/math/singleToken.ts +250 -0
- package/src/math/slippage.ts +49 -0
- package/src/math/weightedMath.ts +48 -0
- package/src/parsers/borsh.ts +80 -0
- package/src/parsers/events.ts +172 -0
- package/src/parsers/index.ts +4 -0
- package/src/parsers/mintAccount.ts +37 -0
- package/src/parsers/poolAccount.ts +113 -0
- package/src/types/events.ts +100 -0
- package/src/types/index.ts +4 -0
- package/src/types/pool.ts +64 -0
- package/src/types/result.ts +45 -0
- package/src/types/tx.ts +87 -0
- package/src/utils/errors.ts +78 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/pda.ts +58 -0
- package/src/utils/retry.ts +85 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) 2026 Endgame AMM DEX Foundation.
|
|
3
|
+
// See LICENSE at the repository root.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @cube/sdk — client library for the Cubic Pool AMM on Solana.
|
|
7
|
+
*
|
|
8
|
+
* Entry point barrel. Most consumers will want to import from this root:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { CubicPoolClient, CubeBackendClient, getConfig } from "@cube/sdk";
|
|
12
|
+
*
|
|
13
|
+
* const cfg = getConfig("mainnet", { backendEndpoint: "https://api.cube.fi" });
|
|
14
|
+
* const pool = new CubicPoolClient({ config: cfg, poolAddress, rpc: { endpoint: cfg.defaults.rpcEndpoint } });
|
|
15
|
+
* const info = await pool.sync();
|
|
16
|
+
* if (info.ok) console.log(info.data.tokens.map(t => t.metadata?.symbol));
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
export * from "./config";
|
|
21
|
+
export * from "./types";
|
|
22
|
+
export * from "./utils";
|
|
23
|
+
export * from "./math";
|
|
24
|
+
export * from "./parsers";
|
|
25
|
+
export * from "./clients";
|
|
26
|
+
export * from "./idl";
|
|
27
|
+
|
|
28
|
+
// Explicit runtime re-exports keep CommonJS output statically discoverable for
|
|
29
|
+
// Vite/Rollup consumers that import named exports from the package root.
|
|
30
|
+
export {
|
|
31
|
+
getConfig,
|
|
32
|
+
programId,
|
|
33
|
+
CUBIC_POOL_SEED,
|
|
34
|
+
BPT_MINT_SEED,
|
|
35
|
+
STLD_HELPER_SEED,
|
|
36
|
+
TREASURY_SEED,
|
|
37
|
+
WEIGHT_SCALE,
|
|
38
|
+
MIN_WEIGHT,
|
|
39
|
+
MAX_WEIGHT,
|
|
40
|
+
MIN_TOKENS,
|
|
41
|
+
MAX_TOKENS,
|
|
42
|
+
BPT_DECIMALS,
|
|
43
|
+
SWAP_FEE_PRECISION,
|
|
44
|
+
PROTOCOL_FEE_PRECISION,
|
|
45
|
+
MAX_SWAP_FEE_RATE,
|
|
46
|
+
MAX_PROTOCOL_FEE_RATE,
|
|
47
|
+
MINIMUM_INITIAL_BPT,
|
|
48
|
+
SLIPPAGE_PRECISION,
|
|
49
|
+
MIN_SLIPPAGE_HBPS,
|
|
50
|
+
} from "./config";
|
|
51
|
+
export { NETWORK_PROGRAMS, DEFAULT_RPC_ENDPOINT } from "./config/networks";
|
|
52
|
+
export { KNOWN_TOKENS, resolveKnownToken } from "./config/tokens";
|
|
53
|
+
export { ok, err } from "./types/result";
|
|
54
|
+
export {
|
|
55
|
+
ONE,
|
|
56
|
+
mulDown,
|
|
57
|
+
mulUp,
|
|
58
|
+
divDown,
|
|
59
|
+
divUp,
|
|
60
|
+
complement,
|
|
61
|
+
weightToFp,
|
|
62
|
+
} from "./math/fixedPoint";
|
|
63
|
+
export { lnFp, expFp, powFp } from "./math/logExp";
|
|
64
|
+
export {
|
|
65
|
+
calcOutGivenIn,
|
|
66
|
+
calcBptOutGivenExactTokensIn,
|
|
67
|
+
calcTokensOutGivenBptIn,
|
|
68
|
+
calcSpotOut,
|
|
69
|
+
} from "./math/cubicMath";
|
|
70
|
+
export { validateWeights, calcSpotPrice } from "./math/weightedMath";
|
|
71
|
+
export {
|
|
72
|
+
capDepositAmountsToLpRatio,
|
|
73
|
+
computeAllocations,
|
|
74
|
+
computeTwoTokenOptimalAllocations,
|
|
75
|
+
} from "./math/singleToken";
|
|
76
|
+
export { applySlippage, applySwapFee, lpBalances, priceImpactHbps } from "./math/slippage";
|
|
77
|
+
export { RpcClient } from "./clients/RpcClient";
|
|
78
|
+
export { CubeBackendClient } from "./clients/CubeBackendClient";
|
|
79
|
+
export { CubicPoolClient } from "./clients/CubicPoolClient";
|
|
80
|
+
export { SingleTokenDepositClient } from "./clients/SingleTokenDepositClient";
|
|
81
|
+
export { PoolFactoryClient } from "./clients/PoolFactoryClient";
|
|
82
|
+
export { AdminClient } from "./clients/AdminClient";
|
|
83
|
+
export {
|
|
84
|
+
buildSwapIx,
|
|
85
|
+
buildSwapTx,
|
|
86
|
+
buildAddLiquidityIx,
|
|
87
|
+
buildAddLiquidityTx,
|
|
88
|
+
buildRemoveLiquidityIx,
|
|
89
|
+
buildRemoveLiquidityTx,
|
|
90
|
+
buildSingleTokenDepositIx,
|
|
91
|
+
buildSingleTokenDepositTx,
|
|
92
|
+
buildInitializeConfigIx,
|
|
93
|
+
buildInitializeCubicPoolIx,
|
|
94
|
+
buildDeployPoolTx,
|
|
95
|
+
} from "./clients/tx-builders";
|
|
96
|
+
export { decodePoolAccount, POOL_DISCRIMINATOR_LEN } from "./parsers/poolAccount";
|
|
97
|
+
export { decodeMintAccount } from "./parsers/mintAccount";
|
|
98
|
+
export { parseCubicPoolEvents } from "./parsers/events";
|
|
99
|
+
export { BorshReader } from "./parsers/borsh";
|
|
100
|
+
|
|
101
|
+
export type {
|
|
102
|
+
CubeConfig,
|
|
103
|
+
CubeConfigOverrides,
|
|
104
|
+
ProgramIdKind,
|
|
105
|
+
Network,
|
|
106
|
+
NetworkPrograms,
|
|
107
|
+
TokenInfo,
|
|
108
|
+
} from "./config";
|
|
109
|
+
export type {
|
|
110
|
+
SdkResult,
|
|
111
|
+
SdkError,
|
|
112
|
+
SdkErrorCode,
|
|
113
|
+
PoolTokenInfo,
|
|
114
|
+
PoolInfo,
|
|
115
|
+
PoolSummary,
|
|
116
|
+
SwapParams,
|
|
117
|
+
SwapQuote,
|
|
118
|
+
AddLiquidityParams,
|
|
119
|
+
RemoveLiquidityParams,
|
|
120
|
+
SingleTokenDepositParams,
|
|
121
|
+
SingleTokenDepositQuote,
|
|
122
|
+
BuiltTx,
|
|
123
|
+
DeployPoolParams,
|
|
124
|
+
CubicPoolEvent,
|
|
125
|
+
PoolInitializedEvent,
|
|
126
|
+
SwapEvent,
|
|
127
|
+
LiquidityAddedEvent,
|
|
128
|
+
LiquidityRemovedEvent,
|
|
129
|
+
ProtocolFeesCollectedEvent,
|
|
130
|
+
PoolEnabledUpdatedEvent,
|
|
131
|
+
SwapsEnabledUpdatedEvent,
|
|
132
|
+
SingleTokenDepositEvent,
|
|
133
|
+
UnknownEvent,
|
|
134
|
+
} from "./types";
|
|
135
|
+
export type {
|
|
136
|
+
AllocationResult,
|
|
137
|
+
} from "./math/singleToken";
|
|
138
|
+
export type {
|
|
139
|
+
RawPoolAccount,
|
|
140
|
+
RawMintAccount,
|
|
141
|
+
} from "./parsers";
|
|
142
|
+
export type {
|
|
143
|
+
RpcClientParams,
|
|
144
|
+
CubeBackendClientParams,
|
|
145
|
+
StatsKind,
|
|
146
|
+
StatsWindow,
|
|
147
|
+
StatsSeriesPoint,
|
|
148
|
+
StatsSeries,
|
|
149
|
+
PriceMap,
|
|
150
|
+
CubicPoolClientParams,
|
|
151
|
+
SingleTokenDepositClientParams,
|
|
152
|
+
PoolFactoryClientParams,
|
|
153
|
+
InitializeConfigParams,
|
|
154
|
+
} from "./clients";
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { complement, divDown, divUp, mulDown, ONE, weightToFp } from "./fixedPoint";
|
|
2
|
+
import { powFp } from "./logExp";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Port of `CubicMath::calc_out_given_in` from
|
|
6
|
+
* `contracts/programs/cubic-pool/src/math/cubic_math.rs`.
|
|
7
|
+
*
|
|
8
|
+
* Formula: aO = bO * (1 - (bI / (bI + aI))^(wI / wO))
|
|
9
|
+
*
|
|
10
|
+
* Inputs are u64 raw amounts (bigint here to avoid overflow). Decimals
|
|
11
|
+
* cancel in the ratio bI/(bI+aI), so no scaling needed.
|
|
12
|
+
*/
|
|
13
|
+
export function calcOutGivenIn(params: {
|
|
14
|
+
virtualBalanceIn: bigint;
|
|
15
|
+
weightInBps: bigint;
|
|
16
|
+
virtualBalanceOut: bigint;
|
|
17
|
+
weightOutBps: bigint;
|
|
18
|
+
amountIn: bigint;
|
|
19
|
+
actualBalanceOut: bigint;
|
|
20
|
+
}): bigint {
|
|
21
|
+
const { virtualBalanceIn, weightInBps, virtualBalanceOut, weightOutBps, amountIn, actualBalanceOut } =
|
|
22
|
+
params;
|
|
23
|
+
const denom = virtualBalanceIn + amountIn;
|
|
24
|
+
const base = divUp(virtualBalanceIn, denom);
|
|
25
|
+
const exp = divDown(weightToFp(weightInBps), weightToFp(weightOutBps));
|
|
26
|
+
let power = powFp(base, exp);
|
|
27
|
+
// Match Rust's +1 bias + clamp to ONE.
|
|
28
|
+
power = power + 1n;
|
|
29
|
+
if (power > ONE) power = ONE;
|
|
30
|
+
const comp = complement(power);
|
|
31
|
+
const out = mulDown(virtualBalanceOut, comp);
|
|
32
|
+
if (out > actualBalanceOut) {
|
|
33
|
+
throw new Error("cubicMath: amount out exceeds actual balance");
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Port of `CubicMath::calc_bpt_out_given_exact_tokens_in`.
|
|
40
|
+
* Proportional join: `bpt = total_supply * min(amount_i / actual_balance_i)`
|
|
41
|
+
* across tokens with `actual_balance_i > 0`.
|
|
42
|
+
*/
|
|
43
|
+
export function calcBptOutGivenExactTokensIn(
|
|
44
|
+
actualBalances: bigint[],
|
|
45
|
+
amountsIn: bigint[],
|
|
46
|
+
bptTotalSupply: bigint
|
|
47
|
+
): bigint {
|
|
48
|
+
if (actualBalances.length !== amountsIn.length) {
|
|
49
|
+
throw new Error("cubicMath: balances/amounts length mismatch");
|
|
50
|
+
}
|
|
51
|
+
let ratioMin: bigint | null = null;
|
|
52
|
+
for (let i = 0; i < actualBalances.length; i++) {
|
|
53
|
+
if (actualBalances[i] === 0n) continue;
|
|
54
|
+
const ratio = divDown(amountsIn[i], actualBalances[i]);
|
|
55
|
+
ratioMin = ratioMin === null || ratio < ratioMin ? ratio : ratioMin;
|
|
56
|
+
}
|
|
57
|
+
if (ratioMin === null) throw new Error("cubicMath: no live tokens");
|
|
58
|
+
return (bptTotalSupply * ratioMin) / ONE;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Port of `CubicMath::calc_tokens_out_given_bpt_in`. Proportional exit.
|
|
63
|
+
*/
|
|
64
|
+
export function calcTokensOutGivenBptIn(
|
|
65
|
+
actualBalances: bigint[],
|
|
66
|
+
bptAmount: bigint,
|
|
67
|
+
bptTotalSupply: bigint
|
|
68
|
+
): bigint[] {
|
|
69
|
+
if (bptTotalSupply === 0n) throw new Error("cubicMath: zero total supply");
|
|
70
|
+
const ratio = divDown(bptAmount, bptTotalSupply);
|
|
71
|
+
return actualBalances.map((bal) => mulDown(bal, ratio));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Spot amount-out: aO = aI * (bO * wI) / (bI * wO). Always ≥
|
|
76
|
+
* `calcOutGivenIn` for the same inputs (curve slippage drags the real
|
|
77
|
+
* value below spot). Useful for UI "price impact" calculations.
|
|
78
|
+
*/
|
|
79
|
+
export function calcSpotOut(params: {
|
|
80
|
+
virtualBalanceIn: bigint;
|
|
81
|
+
weightInBps: bigint;
|
|
82
|
+
virtualBalanceOut: bigint;
|
|
83
|
+
weightOutBps: bigint;
|
|
84
|
+
amountIn: bigint;
|
|
85
|
+
}): bigint {
|
|
86
|
+
const { virtualBalanceIn, weightInBps, virtualBalanceOut, weightOutBps, amountIn } = params;
|
|
87
|
+
if (virtualBalanceIn === 0n || weightOutBps === 0n) return 0n;
|
|
88
|
+
return (amountIn * virtualBalanceOut * weightInBps) / (virtualBalanceIn * weightOutBps);
|
|
89
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed-point arithmetic with 1e18 precision. Port of
|
|
3
|
+
* `contracts/programs/cubic-pool/src/math/fixed_point.rs`. All functions
|
|
4
|
+
* operate on bigint to match the Rust u128/i128 semantics exactly.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export const ONE = 1_000_000_000_000_000_000n; // 1e18
|
|
8
|
+
|
|
9
|
+
export function mulDown(a: bigint, b: bigint): bigint {
|
|
10
|
+
const product = a * b;
|
|
11
|
+
return product / ONE;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function mulUp(a: bigint, b: bigint): bigint {
|
|
15
|
+
const product = a * b;
|
|
16
|
+
if (product === 0n) return 0n;
|
|
17
|
+
return (product - 1n) / ONE + 1n;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function divDown(a: bigint, b: bigint): bigint {
|
|
21
|
+
if (b === 0n) throw new Error("fixedPoint.divDown: divide by zero");
|
|
22
|
+
return (a * ONE) / b;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function divUp(a: bigint, b: bigint): bigint {
|
|
26
|
+
if (b === 0n) throw new Error("fixedPoint.divUp: divide by zero");
|
|
27
|
+
if (a === 0n) return 0n;
|
|
28
|
+
return (a * ONE - 1n) / b + 1n;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function complement(x: bigint): bigint {
|
|
32
|
+
return x < ONE ? ONE - x : 0n;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Convert bps → 1e18 fixed point. 5000 bps → 5e17. */
|
|
36
|
+
export function weightToFp(weightBps: bigint | number): bigint {
|
|
37
|
+
const w = typeof weightBps === "number" ? BigInt(weightBps) : weightBps;
|
|
38
|
+
return (w * ONE) / 10_000n;
|
|
39
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fixed-point ln/exp/pow over 1e18 precision. Port of
|
|
3
|
+
* `contracts/programs/cubic-pool/src/math/log_exp_math.rs` via bigint —
|
|
4
|
+
* results match the Rust version modulo identical rounding.
|
|
5
|
+
*
|
|
6
|
+
* Intended for off-chain quoting; on-chain calls go through cubic-pool's
|
|
7
|
+
* own implementation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const ONE = 1_000_000_000_000_000_000n;
|
|
11
|
+
const TWO = 2n * ONE;
|
|
12
|
+
const LN2 = 693_147_180_559_945_309n; // ln(2) * 1e18
|
|
13
|
+
|
|
14
|
+
/** Natural logarithm. Accepts x > 0 in 1e18 fp, returns signed i128 in 1e18 fp. */
|
|
15
|
+
export function lnFp(x: bigint): bigint {
|
|
16
|
+
if (x <= 0n) throw new Error("logExp.ln: x must be positive");
|
|
17
|
+
if (x === ONE) return 0n;
|
|
18
|
+
if (x < ONE) {
|
|
19
|
+
const inv = (ONE * ONE + x - 1n) / x;
|
|
20
|
+
return -lnPos(inv);
|
|
21
|
+
}
|
|
22
|
+
return lnPos(x);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function lnPos(x: bigint): bigint {
|
|
26
|
+
let val = x;
|
|
27
|
+
let acc = 0n;
|
|
28
|
+
while (val >= TWO) {
|
|
29
|
+
acc += LN2;
|
|
30
|
+
val /= 2n;
|
|
31
|
+
}
|
|
32
|
+
const y = val - ONE;
|
|
33
|
+
if (y === 0n) return acc;
|
|
34
|
+
|
|
35
|
+
// Taylor series: ln(1+y) = y - y^2/2 + y^3/3 - ...
|
|
36
|
+
let term = y;
|
|
37
|
+
let sum = y;
|
|
38
|
+
let sign = -1n;
|
|
39
|
+
for (let k = 2n; k <= 30n; k++) {
|
|
40
|
+
term = (term * y) / ONE;
|
|
41
|
+
const tk = term / k;
|
|
42
|
+
sum = sign < 0n ? sum - tk : sum + tk;
|
|
43
|
+
sign = -sign;
|
|
44
|
+
if (tk === 0n) break;
|
|
45
|
+
}
|
|
46
|
+
return acc + sum;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** e^x. Accepts signed i128 1e18 fp, returns u128 1e18 fp. */
|
|
50
|
+
export function expFp(x: bigint): bigint {
|
|
51
|
+
if (x === 0n) return ONE;
|
|
52
|
+
if (x < 0n) {
|
|
53
|
+
const pos = expFp(-x);
|
|
54
|
+
return (ONE * ONE) / pos;
|
|
55
|
+
}
|
|
56
|
+
let ux = x;
|
|
57
|
+
let k = 0n;
|
|
58
|
+
while (ux >= LN2) {
|
|
59
|
+
ux -= LN2;
|
|
60
|
+
k += 1n;
|
|
61
|
+
}
|
|
62
|
+
// Taylor series: e^r = 1 + r + r^2/2! + ...
|
|
63
|
+
let sum = ONE;
|
|
64
|
+
let term = ONE;
|
|
65
|
+
for (let i = 1n; i <= 30n; i++) {
|
|
66
|
+
term = (term * ux) / ONE;
|
|
67
|
+
term = term / i;
|
|
68
|
+
sum += term;
|
|
69
|
+
if (term === 0n) break;
|
|
70
|
+
}
|
|
71
|
+
return sum << BigInt(k);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Fixed-point pow: x^y where both are 1e18 fp. */
|
|
75
|
+
export function powFp(base: bigint, exponent: bigint): bigint {
|
|
76
|
+
if (exponent === 0n) return ONE;
|
|
77
|
+
if (base === ONE) return ONE;
|
|
78
|
+
if (base === 0n) return 0n;
|
|
79
|
+
const l = lnFp(base);
|
|
80
|
+
const prod = (exponent * (l < 0n ? -l : l)) / ONE;
|
|
81
|
+
return l < 0n ? expFp(-prod) : expFp(prod);
|
|
82
|
+
}
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { ONE } from "./fixedPoint";
|
|
2
|
+
import { calcOutGivenIn } from "./cubicMath";
|
|
3
|
+
import { lpBalances } from "./slippage";
|
|
4
|
+
import { PROTOCOL_FEE_PRECISION, SWAP_FEE_PRECISION } from "../config";
|
|
5
|
+
|
|
6
|
+
export interface AllocationResult {
|
|
7
|
+
/** Per-token integer amount allocations; sum = amountIn. */
|
|
8
|
+
allocations: bigint[];
|
|
9
|
+
/** Scaled W values, for inspection/debugging. */
|
|
10
|
+
wScaled: bigint[];
|
|
11
|
+
/** Sum of wScaled. */
|
|
12
|
+
sumW: bigint;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Port of stld `compute_allocations` from
|
|
17
|
+
* `contracts/programs/single-token-liquidity/src/math.rs`.
|
|
18
|
+
*
|
|
19
|
+
* W_i = weight_i * factBalance_i / virtBalance_i (dimensionless concentration).
|
|
20
|
+
* share_i = W_i / ΣW. amount_alloc[i] = amountIn * share_i.
|
|
21
|
+
*
|
|
22
|
+
* Integer truncation of per-token amounts routes the remainder to the
|
|
23
|
+
* input-token slot so Σ allocations == amountIn exactly.
|
|
24
|
+
*/
|
|
25
|
+
export function computeAllocations(params: {
|
|
26
|
+
actualBalances: bigint[];
|
|
27
|
+
virtualBalances: bigint[];
|
|
28
|
+
weightsBps: number[];
|
|
29
|
+
amountIn: bigint;
|
|
30
|
+
tokenInIndex: number;
|
|
31
|
+
}): AllocationResult {
|
|
32
|
+
const { actualBalances, virtualBalances, weightsBps, amountIn, tokenInIndex } = params;
|
|
33
|
+
const n = actualBalances.length;
|
|
34
|
+
if (n !== virtualBalances.length || n !== weightsBps.length) {
|
|
35
|
+
throw new Error("computeAllocations: length mismatch");
|
|
36
|
+
}
|
|
37
|
+
if (tokenInIndex < 0 || tokenInIndex >= n) {
|
|
38
|
+
throw new Error("computeAllocations: tokenInIndex out of range");
|
|
39
|
+
}
|
|
40
|
+
const wScaled: bigint[] = [];
|
|
41
|
+
for (let i = 0; i < n; i++) {
|
|
42
|
+
if (virtualBalances[i] <= 0n) {
|
|
43
|
+
throw new Error(`computeAllocations: virtualBalance[${i}] must be positive`);
|
|
44
|
+
}
|
|
45
|
+
const w = (BigInt(weightsBps[i]) * actualBalances[i] * ONE) / (virtualBalances[i] * 10_000n);
|
|
46
|
+
wScaled.push(w);
|
|
47
|
+
}
|
|
48
|
+
const sumW = wScaled.reduce((a, b) => a + b, 0n);
|
|
49
|
+
if (sumW === 0n) {
|
|
50
|
+
throw new Error("computeAllocations: pool not seeded (ΣW = 0)");
|
|
51
|
+
}
|
|
52
|
+
const allocations: bigint[] = wScaled.map((w) => (amountIn * w) / sumW);
|
|
53
|
+
const sumAlloc = allocations.reduce((a, b) => a + b, 0n);
|
|
54
|
+
if (sumAlloc < amountIn) {
|
|
55
|
+
allocations[tokenInIndex] += amountIn - sumAlloc;
|
|
56
|
+
}
|
|
57
|
+
return { allocations, wScaled, sumW };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function swapFeeAmount(amount: bigint, swapFeeRate: number): bigint {
|
|
61
|
+
return (amount * BigInt(swapFeeRate)) / BigInt(SWAP_FEE_PRECISION);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function amountAfterSwapFee(amount: bigint, swapFeeRate: number): bigint {
|
|
65
|
+
const fee = swapFeeAmount(amount, swapFeeRate);
|
|
66
|
+
if (swapFeeRate > 0 && fee === 0n) {
|
|
67
|
+
throw new Error("computeTwoTokenOptimalAllocations: swap amount too small, fee rounds to zero");
|
|
68
|
+
}
|
|
69
|
+
return amount - fee;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function protocolFeeAmount(swapFee: bigint, protocolFeeRate: number): bigint {
|
|
73
|
+
return (swapFee * BigInt(protocolFeeRate)) / BigInt(PROTOCOL_FEE_PRECISION);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function scoreTwoTokenSwap(params: {
|
|
77
|
+
actualBalances: [bigint, bigint];
|
|
78
|
+
virtualBalances: [bigint, bigint];
|
|
79
|
+
protocolFeesOwed: [bigint, bigint];
|
|
80
|
+
weightsBps: [number, number];
|
|
81
|
+
amountIn: bigint;
|
|
82
|
+
tokenInIndex: 0 | 1;
|
|
83
|
+
swapToOther: bigint;
|
|
84
|
+
swapFeeRate: number;
|
|
85
|
+
protocolFeeRate: number;
|
|
86
|
+
}): { score: bigint; amountOut: bigint } {
|
|
87
|
+
const {
|
|
88
|
+
actualBalances,
|
|
89
|
+
virtualBalances,
|
|
90
|
+
protocolFeesOwed,
|
|
91
|
+
weightsBps,
|
|
92
|
+
amountIn,
|
|
93
|
+
tokenInIndex,
|
|
94
|
+
swapToOther,
|
|
95
|
+
swapFeeRate,
|
|
96
|
+
protocolFeeRate,
|
|
97
|
+
} = params;
|
|
98
|
+
const other = tokenInIndex === 0 ? 1 : 0;
|
|
99
|
+
const remainingInput = amountIn - swapToOther;
|
|
100
|
+
if (swapToOther <= 0n || remainingInput <= 0n) return { score: 0n, amountOut: 0n };
|
|
101
|
+
|
|
102
|
+
let afterFee: bigint;
|
|
103
|
+
try {
|
|
104
|
+
afterFee = amountAfterSwapFee(swapToOther, swapFeeRate);
|
|
105
|
+
} catch {
|
|
106
|
+
return { score: 0n, amountOut: 0n };
|
|
107
|
+
}
|
|
108
|
+
const swapFee = swapToOther - afterFee;
|
|
109
|
+
const protocolFee = protocolFeeAmount(swapFee, protocolFeeRate);
|
|
110
|
+
|
|
111
|
+
const inLp = lpBalances(
|
|
112
|
+
actualBalances[tokenInIndex],
|
|
113
|
+
virtualBalances[tokenInIndex],
|
|
114
|
+
protocolFeesOwed[tokenInIndex]
|
|
115
|
+
);
|
|
116
|
+
const outLp = lpBalances(
|
|
117
|
+
actualBalances[other],
|
|
118
|
+
virtualBalances[other],
|
|
119
|
+
protocolFeesOwed[other]
|
|
120
|
+
);
|
|
121
|
+
|
|
122
|
+
let amountOut: bigint;
|
|
123
|
+
try {
|
|
124
|
+
amountOut = calcOutGivenIn({
|
|
125
|
+
virtualBalanceIn: inLp.lpVirtual,
|
|
126
|
+
weightInBps: BigInt(weightsBps[tokenInIndex]),
|
|
127
|
+
virtualBalanceOut: outLp.lpVirtual,
|
|
128
|
+
weightOutBps: BigInt(weightsBps[other]),
|
|
129
|
+
amountIn: afterFee,
|
|
130
|
+
actualBalanceOut: outLp.lpActual,
|
|
131
|
+
});
|
|
132
|
+
} catch {
|
|
133
|
+
return { score: 0n, amountOut: 0n };
|
|
134
|
+
}
|
|
135
|
+
if (amountOut <= 0n || amountOut >= outLp.lpActual) return { score: 0n, amountOut };
|
|
136
|
+
|
|
137
|
+
const lpInAfter = inLp.lpActual + swapToOther - protocolFee;
|
|
138
|
+
const lpOutAfter = outLp.lpActual - amountOut;
|
|
139
|
+
if (lpInAfter <= 0n || lpOutAfter <= 0n) return { score: 0n, amountOut };
|
|
140
|
+
|
|
141
|
+
const ratioIn = (remainingInput * ONE) / lpInAfter;
|
|
142
|
+
const ratioOut = (amountOut * ONE) / lpOutAfter;
|
|
143
|
+
return { score: ratioIn < ratioOut ? ratioIn : ratioOut, amountOut };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function computeTwoTokenOptimalAllocations(params: {
|
|
147
|
+
actualBalances: [bigint, bigint];
|
|
148
|
+
virtualBalances: [bigint, bigint];
|
|
149
|
+
protocolFeesOwed: [bigint, bigint];
|
|
150
|
+
weightsBps: [number, number];
|
|
151
|
+
amountIn: bigint;
|
|
152
|
+
tokenInIndex: 0 | 1;
|
|
153
|
+
swapFeeRate: number;
|
|
154
|
+
protocolFeeRate: number;
|
|
155
|
+
}): AllocationResult {
|
|
156
|
+
const { amountIn, tokenInIndex } = params;
|
|
157
|
+
if (amountIn <= 1n) {
|
|
158
|
+
throw new Error("computeTwoTokenOptimalAllocations: amountIn too small");
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
let low = 1n;
|
|
162
|
+
let high = amountIn - 1n;
|
|
163
|
+
for (let i = 0; i < 32 && low < high; i++) {
|
|
164
|
+
const mid = low + (high - low) / 2n;
|
|
165
|
+
const current = scoreTwoTokenSwap({ ...params, swapToOther: mid }).score;
|
|
166
|
+
const next = scoreTwoTokenSwap({
|
|
167
|
+
...params,
|
|
168
|
+
swapToOther: mid + 1n < amountIn ? mid + 1n : amountIn - 1n,
|
|
169
|
+
}).score;
|
|
170
|
+
if (next >= current) low = mid + 1n;
|
|
171
|
+
else high = mid;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const center = low < 1n ? 1n : low >= amountIn ? amountIn - 1n : low;
|
|
175
|
+
let bestSwap = center;
|
|
176
|
+
let bestScore = 0n;
|
|
177
|
+
const start = center > 8n ? center - 8n : 1n;
|
|
178
|
+
const end = center + 8n < amountIn ? center + 8n : amountIn - 1n;
|
|
179
|
+
for (let swapToOther = start; swapToOther <= end; swapToOther++) {
|
|
180
|
+
const { score, amountOut } = scoreTwoTokenSwap({ ...params, swapToOther });
|
|
181
|
+
if (amountOut > 0n && score > bestScore) {
|
|
182
|
+
bestScore = score;
|
|
183
|
+
bestSwap = swapToOther;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (bestScore === 0n) {
|
|
187
|
+
throw new Error("computeTwoTokenOptimalAllocations: amount too small");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const allocations: [bigint, bigint] = [0n, 0n];
|
|
191
|
+
const other = tokenInIndex === 0 ? 1 : 0;
|
|
192
|
+
allocations[tokenInIndex] = amountIn - bestSwap;
|
|
193
|
+
allocations[other] = bestSwap;
|
|
194
|
+
return { allocations, wScaled: [], sumW: bestScore };
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Mirror of stld `cap_deposit_amounts_to_lp_ratio`.
|
|
199
|
+
*
|
|
200
|
+
* `add_liquidity` mints BPT from the minimum ratio against LP-accessible
|
|
201
|
+
* balances (`actual - protocolFeesOwed`). Any helper-held amount above that
|
|
202
|
+
* common ratio would be donated, so the helper deposits the capped basket and
|
|
203
|
+
* refunds the rest.
|
|
204
|
+
*/
|
|
205
|
+
export function capDepositAmountsToLpRatio(params: {
|
|
206
|
+
helperBalances: bigint[];
|
|
207
|
+
actualBalances: bigint[];
|
|
208
|
+
protocolFeesOwed: bigint[];
|
|
209
|
+
}): { depositAmounts: bigint[]; refundAmounts: bigint[]; lpBalancesForAdd: bigint[] } {
|
|
210
|
+
const { helperBalances, actualBalances, protocolFeesOwed } = params;
|
|
211
|
+
const n = helperBalances.length;
|
|
212
|
+
if (n !== actualBalances.length || n !== protocolFeesOwed.length) {
|
|
213
|
+
throw new Error("capDepositAmountsToLpRatio: length mismatch");
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const lpBalancesForAdd = actualBalances.map((actual, i) =>
|
|
217
|
+
actual > protocolFeesOwed[i] ? actual - protocolFeesOwed[i] : 0n
|
|
218
|
+
);
|
|
219
|
+
let ratioMin: bigint | null = null;
|
|
220
|
+
|
|
221
|
+
for (let i = 0; i < n; i++) {
|
|
222
|
+
if (actualBalances[i] > 0n && lpBalancesForAdd[i] === 0n) {
|
|
223
|
+
throw new Error("capDepositAmountsToLpRatio: live token has no LP claim");
|
|
224
|
+
}
|
|
225
|
+
if (lpBalancesForAdd[i] === 0n) continue;
|
|
226
|
+
if (helperBalances[i] <= 0n) {
|
|
227
|
+
throw new Error("capDepositAmountsToLpRatio: amount too small");
|
|
228
|
+
}
|
|
229
|
+
const ratio = (helperBalances[i] * ONE) / lpBalancesForAdd[i];
|
|
230
|
+
ratioMin = ratioMin === null || ratio < ratioMin ? ratio : ratioMin;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (ratioMin === null || ratioMin === 0n) {
|
|
234
|
+
throw new Error("capDepositAmountsToLpRatio: amount too small");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const depositAmounts = lpBalancesForAdd.map((lp, i) => {
|
|
238
|
+
if (lp === 0n) return 0n;
|
|
239
|
+
const amount = (lp * ratioMin!) / ONE;
|
|
240
|
+
if (amount <= 0n) {
|
|
241
|
+
throw new Error("capDepositAmountsToLpRatio: amount too small");
|
|
242
|
+
}
|
|
243
|
+
return amount > helperBalances[i] ? helperBalances[i] : amount;
|
|
244
|
+
});
|
|
245
|
+
const refundAmounts = helperBalances.map((balance, i) =>
|
|
246
|
+
balance > depositAmounts[i] ? balance - depositAmounts[i] : 0n
|
|
247
|
+
);
|
|
248
|
+
|
|
249
|
+
return { depositAmounts, refundAmounts, lpBalancesForAdd };
|
|
250
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { SLIPPAGE_PRECISION, SWAP_FEE_PRECISION } from "../config";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* min_out = expected * (SLIPPAGE_PRECISION - slippage_hbps) / SLIPPAGE_PRECISION.
|
|
5
|
+
* Matches `apply_slippage` in stld's math.rs.
|
|
6
|
+
*/
|
|
7
|
+
export function applySlippage(expected: bigint, slippageHbps: number): bigint {
|
|
8
|
+
if (slippageHbps < 0 || slippageHbps > SLIPPAGE_PRECISION) {
|
|
9
|
+
throw new Error(`slippage: ${slippageHbps} out of [0, ${SLIPPAGE_PRECISION}]`);
|
|
10
|
+
}
|
|
11
|
+
const keep = BigInt(SLIPPAGE_PRECISION - slippageHbps);
|
|
12
|
+
return (expected * keep) / BigInt(SLIPPAGE_PRECISION);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* fee = amount * rate / SWAP_FEE_PRECISION. Returns amount - fee.
|
|
17
|
+
* Matches `apply_swap_fee`.
|
|
18
|
+
*/
|
|
19
|
+
export function applySwapFee(amount: bigint, swapFeeRate: number): bigint {
|
|
20
|
+
if (swapFeeRate < 0) throw new Error("swapFee: negative rate");
|
|
21
|
+
const fee = (amount * BigInt(swapFeeRate)) / BigInt(SWAP_FEE_PRECISION);
|
|
22
|
+
if (swapFeeRate > 0 && fee === 0n) {
|
|
23
|
+
throw new Error("swapFee: amount too small, fee rounds to zero");
|
|
24
|
+
}
|
|
25
|
+
return amount - fee;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* LP-accessible balances: lp_actual = actual - pfo; lp_virtual scaled to
|
|
30
|
+
* match. Used by the off-chain single-token quote in `math/singleToken`.
|
|
31
|
+
*/
|
|
32
|
+
export function lpBalances(actual: bigint, virtualBal: bigint, pfo: bigint): { lpActual: bigint; lpVirtual: bigint } {
|
|
33
|
+
const lpActual = actual >= pfo ? actual - pfo : 0n;
|
|
34
|
+
const lpVirtual =
|
|
35
|
+
actual > 0n ? (virtualBal * lpActual) / actual : virtualBal;
|
|
36
|
+
return { lpActual, lpVirtual };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Price impact in hundredths of basis point: (spot - actual) / spot × 1_000_000.
|
|
41
|
+
* 0 if spot is 0.
|
|
42
|
+
*/
|
|
43
|
+
export function priceImpactHbps(spot: bigint, actual: bigint): number {
|
|
44
|
+
if (spot <= 0n) return 0;
|
|
45
|
+
if (actual >= spot) return 0;
|
|
46
|
+
const diff = spot - actual;
|
|
47
|
+
const hbps = (diff * BigInt(SLIPPAGE_PRECISION)) / spot;
|
|
48
|
+
return Number(hbps);
|
|
49
|
+
}
|