@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
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { MAX_WEIGHT, MIN_WEIGHT, WEIGHT_SCALE } from "../config";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Validate a weight vector. Must sum exactly to 10_000 (WEIGHT_SCALE) and
|
|
5
|
+
* every entry must be in [MIN_WEIGHT, MAX_WEIGHT].
|
|
6
|
+
*/
|
|
7
|
+
export function validateWeights(weights: number[]): void {
|
|
8
|
+
if (!Array.isArray(weights) || weights.length < 2) {
|
|
9
|
+
throw new Error("weightedMath: at least 2 weights required");
|
|
10
|
+
}
|
|
11
|
+
let sum = 0;
|
|
12
|
+
for (const w of weights) {
|
|
13
|
+
if (!Number.isInteger(w)) {
|
|
14
|
+
throw new Error("weightedMath: weights must be integers (bps)");
|
|
15
|
+
}
|
|
16
|
+
if (w < MIN_WEIGHT || w > MAX_WEIGHT) {
|
|
17
|
+
throw new Error(`weightedMath: weight ${w} out of [${MIN_WEIGHT}, ${MAX_WEIGHT}]`);
|
|
18
|
+
}
|
|
19
|
+
sum += w;
|
|
20
|
+
}
|
|
21
|
+
if (sum !== WEIGHT_SCALE) {
|
|
22
|
+
throw new Error(`weightedMath: weights sum to ${sum}, expected ${WEIGHT_SCALE}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Weighted spot price of token_out priced in token_in, normalised to 18
|
|
28
|
+
* decimals. Used only for display; on-chain quoting uses calcOutGivenIn.
|
|
29
|
+
*/
|
|
30
|
+
export function calcSpotPrice(params: {
|
|
31
|
+
balanceIn: bigint;
|
|
32
|
+
weightInBps: bigint;
|
|
33
|
+
balanceOut: bigint;
|
|
34
|
+
weightOutBps: bigint;
|
|
35
|
+
decimalsIn: number;
|
|
36
|
+
decimalsOut: number;
|
|
37
|
+
}): bigint {
|
|
38
|
+
const { balanceIn, weightInBps, balanceOut, weightOutBps, decimalsIn, decimalsOut } = params;
|
|
39
|
+
const scaleIn = 10n ** BigInt(18 - decimalsIn);
|
|
40
|
+
const scaleOut = 10n ** BigInt(18 - decimalsOut);
|
|
41
|
+
const bI = balanceIn * scaleIn;
|
|
42
|
+
const bO = balanceOut * scaleOut;
|
|
43
|
+
if (bO === 0n || weightOutBps === 0n) return 0n;
|
|
44
|
+
// price = (bI / wI) / (bO / wO)
|
|
45
|
+
const numer = bI * weightOutBps * 10n ** 18n;
|
|
46
|
+
const denom = bO * weightInBps;
|
|
47
|
+
return numer / denom;
|
|
48
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import BN from "bn.js";
|
|
2
|
+
import { PublicKey } from "@solana/web3.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Minimal Borsh-compatible reader. Enough to decode the Cube contract
|
|
6
|
+
* event structs (Pubkey × N, u8/u16/u32/u64/i64, bool, Vec<u64>,
|
|
7
|
+
* Vec<Pubkey>). Avoids pulling a borsh dep into the SDK's hot path.
|
|
8
|
+
*/
|
|
9
|
+
export class BorshReader {
|
|
10
|
+
private offset = 0;
|
|
11
|
+
constructor(private readonly buf: Buffer) {}
|
|
12
|
+
|
|
13
|
+
remaining(): number {
|
|
14
|
+
return this.buf.length - this.offset;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
skip(n: number): void {
|
|
18
|
+
this.offset += n;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
u8(): number {
|
|
22
|
+
const v = this.buf.readUInt8(this.offset);
|
|
23
|
+
this.offset += 1;
|
|
24
|
+
return v;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
u16(): number {
|
|
28
|
+
const v = this.buf.readUInt16LE(this.offset);
|
|
29
|
+
this.offset += 2;
|
|
30
|
+
return v;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
u32(): number {
|
|
34
|
+
const v = this.buf.readUInt32LE(this.offset);
|
|
35
|
+
this.offset += 4;
|
|
36
|
+
return v;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
u64(): BN {
|
|
40
|
+
const slice = this.buf.slice(this.offset, this.offset + 8);
|
|
41
|
+
this.offset += 8;
|
|
42
|
+
return new BN(slice, "le");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
i64(): BN {
|
|
46
|
+
const slice = this.buf.slice(this.offset, this.offset + 8);
|
|
47
|
+
this.offset += 8;
|
|
48
|
+
// i64 is two's complement little-endian — treat as signed.
|
|
49
|
+
const bn = new BN(slice, "le");
|
|
50
|
+
if (slice[7] & 0x80) {
|
|
51
|
+
// top bit set → negative: subtract 2^64
|
|
52
|
+
return bn.sub(new BN(2).pow(new BN(64)));
|
|
53
|
+
}
|
|
54
|
+
return bn;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
bool(): boolean {
|
|
58
|
+
return this.u8() !== 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
pubkey(): PublicKey {
|
|
62
|
+
const slice = this.buf.slice(this.offset, this.offset + 32);
|
|
63
|
+
this.offset += 32;
|
|
64
|
+
return new PublicKey(slice);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
vecU64(): BN[] {
|
|
68
|
+
const len = this.u32();
|
|
69
|
+
const out: BN[] = [];
|
|
70
|
+
for (let i = 0; i < len; i++) out.push(this.u64());
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
vecPubkey(): PublicKey[] {
|
|
75
|
+
const len = this.u32();
|
|
76
|
+
const out: PublicKey[] = [];
|
|
77
|
+
for (let i = 0; i < len; i++) out.push(this.pubkey());
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import { BorshReader } from "./borsh";
|
|
3
|
+
import { CubicPoolEvent } from "../types/events";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Anchor event log format (base64-encoded after `Program data:`):
|
|
7
|
+
* [ 8-byte discriminator | borsh-encoded event struct ]
|
|
8
|
+
*
|
|
9
|
+
* Discriminators for cubic-pool and single-token-liquidity are hard-coded
|
|
10
|
+
* here from the generated IDL. Never guess from event name — the Anchor
|
|
11
|
+
* macro actually uses `sha256("event:<Name>")[0..8]`, but for robustness
|
|
12
|
+
* we pin the bytes.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Cubic-pool event discriminators (from target/idl/cubic_pool.json):
|
|
16
|
+
const DISC = {
|
|
17
|
+
PoolInitialized: Buffer.from([100, 118, 173, 87, 12, 198, 254, 229]),
|
|
18
|
+
Swap: Buffer.from([81, 108, 227, 190, 205, 208, 10, 196]),
|
|
19
|
+
LiquidityAdded: Buffer.from([154, 26, 221, 108, 238, 64, 217, 161]),
|
|
20
|
+
LiquidityRemoved: Buffer.from([225, 105, 216, 39, 124, 116, 169, 189]),
|
|
21
|
+
ProtocolFeesCollected: Buffer.from([165, 34, 125, 155, 15, 86, 99, 191]),
|
|
22
|
+
SwapFeeRateUpdated: Buffer.from([101, 132, 24, 255, 91, 253, 227, 101]),
|
|
23
|
+
ProtocolFeeRateUpdated: Buffer.from([189, 56, 7, 65, 0, 95, 192, 6]),
|
|
24
|
+
PoolEnabledUpdated: Buffer.from([101, 47, 3, 240, 197, 181, 236, 142]),
|
|
25
|
+
SwapsEnabledUpdated: Buffer.from([55, 116, 118, 138, 102, 26, 227, 223]),
|
|
26
|
+
DebugLiquidityWithdrawn: Buffer.from([174, 59, 149, 22, 135, 129, 129, 83]),
|
|
27
|
+
PoolStateLog: Buffer.from([59, 254, 237, 111, 163, 10, 140, 224]),
|
|
28
|
+
PoolInfo: Buffer.from([207, 20, 87, 97, 251, 212, 234, 45]),
|
|
29
|
+
BannedExtensionsUpdated: Buffer.from([107, 126, 13, 149, 182, 108, 139, 202]),
|
|
30
|
+
// Stld:
|
|
31
|
+
SingleTokenDeposit: Buffer.from([215, 54, 137, 104, 219, 39, 164, 235]),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
type DiscName = keyof typeof DISC;
|
|
35
|
+
|
|
36
|
+
export function parseCubicPoolEvents(logs: string[]): CubicPoolEvent[] {
|
|
37
|
+
const out: CubicPoolEvent[] = [];
|
|
38
|
+
for (const line of logs) {
|
|
39
|
+
const m = line.match(/^Program data:\s+(.+)$/);
|
|
40
|
+
if (!m) continue;
|
|
41
|
+
const buf = Buffer.from(m[1], "base64");
|
|
42
|
+
if (buf.length < 8) continue;
|
|
43
|
+
const disc = buf.slice(0, 8);
|
|
44
|
+
const payload = buf.slice(8);
|
|
45
|
+
const name = matchDiscriminator(disc);
|
|
46
|
+
if (!name) {
|
|
47
|
+
out.push({ kind: "Unknown", name: "unknown", data: { disc: disc.toString("base64"), payload: payload.toString("base64") } });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const ev = decodeEvent(name, payload);
|
|
52
|
+
if (ev) out.push(ev);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
out.push({
|
|
55
|
+
kind: "Unknown",
|
|
56
|
+
name,
|
|
57
|
+
data: { error: String(e), payload: payload.toString("base64") },
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function matchDiscriminator(d: Buffer): DiscName | null {
|
|
65
|
+
for (const [name, bytes] of Object.entries(DISC)) {
|
|
66
|
+
if (bytes.equals(d)) return name as DiscName;
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function decodeEvent(name: DiscName, buf: Buffer): CubicPoolEvent | null {
|
|
72
|
+
const r = new BorshReader(buf);
|
|
73
|
+
switch (name) {
|
|
74
|
+
case "Swap": {
|
|
75
|
+
const pool = r.pubkey();
|
|
76
|
+
const user = r.pubkey();
|
|
77
|
+
const tokenIn = r.pubkey();
|
|
78
|
+
const tokenOut = r.pubkey();
|
|
79
|
+
const amountIn = r.u64();
|
|
80
|
+
const amountOut = r.u64();
|
|
81
|
+
const feeAmount = r.u64();
|
|
82
|
+
const protocolFeeAmount = r.u64();
|
|
83
|
+
const timestamp = r.i64().toNumber();
|
|
84
|
+
return { kind: "Swap", pool, user, tokenIn, tokenOut, amountIn, amountOut, feeAmount, protocolFeeAmount, timestamp };
|
|
85
|
+
}
|
|
86
|
+
case "LiquidityAdded": {
|
|
87
|
+
const pool = r.pubkey();
|
|
88
|
+
const user = r.pubkey();
|
|
89
|
+
const tokenAmounts = r.vecU64();
|
|
90
|
+
const bptAmount = r.u64();
|
|
91
|
+
const timestamp = r.i64().toNumber();
|
|
92
|
+
return { kind: "LiquidityAdded", pool, user, tokenAmounts, bptAmount, timestamp };
|
|
93
|
+
}
|
|
94
|
+
case "LiquidityRemoved": {
|
|
95
|
+
const pool = r.pubkey();
|
|
96
|
+
const user = r.pubkey();
|
|
97
|
+
const bptAmount = r.u64();
|
|
98
|
+
const tokenAmounts = r.vecU64();
|
|
99
|
+
const timestamp = r.i64().toNumber();
|
|
100
|
+
return { kind: "LiquidityRemoved", pool, user, bptAmount, tokenAmounts, timestamp };
|
|
101
|
+
}
|
|
102
|
+
case "ProtocolFeesCollected": {
|
|
103
|
+
const pool = r.pubkey();
|
|
104
|
+
const authority = r.pubkey();
|
|
105
|
+
const tokenAmounts = r.vecU64();
|
|
106
|
+
const timestamp = r.i64().toNumber();
|
|
107
|
+
return { kind: "ProtocolFeesCollected", pool, authority, tokenAmounts, timestamp };
|
|
108
|
+
}
|
|
109
|
+
case "PoolInitialized": {
|
|
110
|
+
const pool = r.pubkey();
|
|
111
|
+
const config = r.pubkey();
|
|
112
|
+
const tokenCount = r.u8();
|
|
113
|
+
const bptMint = r.pubkey();
|
|
114
|
+
const timestamp = r.i64().toNumber();
|
|
115
|
+
return { kind: "PoolInitialized", pool, config, tokenCount, bptMint, timestamp };
|
|
116
|
+
}
|
|
117
|
+
case "PoolEnabledUpdated": {
|
|
118
|
+
const pool = r.pubkey();
|
|
119
|
+
const authority = r.pubkey();
|
|
120
|
+
const oldValue = r.bool();
|
|
121
|
+
const newValue = r.bool();
|
|
122
|
+
const timestamp = r.i64().toNumber();
|
|
123
|
+
return { kind: "PoolEnabledUpdated", pool, authority, oldValue, newValue, timestamp };
|
|
124
|
+
}
|
|
125
|
+
case "SwapsEnabledUpdated": {
|
|
126
|
+
const pool = r.pubkey();
|
|
127
|
+
const authority = r.pubkey();
|
|
128
|
+
const oldValue = r.bool();
|
|
129
|
+
const newValue = r.bool();
|
|
130
|
+
const timestamp = r.i64().toNumber();
|
|
131
|
+
return { kind: "SwapsEnabledUpdated", pool, authority, oldValue, newValue, timestamp };
|
|
132
|
+
}
|
|
133
|
+
case "SingleTokenDeposit": {
|
|
134
|
+
const helper = r.pubkey();
|
|
135
|
+
const pool = r.pubkey();
|
|
136
|
+
const user = r.pubkey();
|
|
137
|
+
const tokenInIndex = r.u8();
|
|
138
|
+
const amountIn = r.u64();
|
|
139
|
+
const slippageHundredthsBps = r.u32();
|
|
140
|
+
const allocations = r.vecU64();
|
|
141
|
+
const depositedAmounts = r.vecU64();
|
|
142
|
+
const bptReceived = r.u64();
|
|
143
|
+
const dustRefunded = r.u64();
|
|
144
|
+
const timestamp = r.i64().toNumber();
|
|
145
|
+
return {
|
|
146
|
+
kind: "SingleTokenDeposit",
|
|
147
|
+
helper,
|
|
148
|
+
pool,
|
|
149
|
+
user,
|
|
150
|
+
tokenInIndex,
|
|
151
|
+
amountIn,
|
|
152
|
+
slippageHundredthsBps,
|
|
153
|
+
allocations,
|
|
154
|
+
depositedAmounts,
|
|
155
|
+
bptReceived,
|
|
156
|
+
dustRefunded,
|
|
157
|
+
timestamp,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
// Events we don't yet surface as typed — decode as Unknown so the
|
|
161
|
+
// caller gets the discriminator name and can act on it.
|
|
162
|
+
case "SwapFeeRateUpdated":
|
|
163
|
+
case "ProtocolFeeRateUpdated":
|
|
164
|
+
case "BannedExtensionsUpdated":
|
|
165
|
+
case "DebugLiquidityWithdrawn":
|
|
166
|
+
case "PoolStateLog":
|
|
167
|
+
case "PoolInfo":
|
|
168
|
+
return { kind: "Unknown", name, data: { raw: buf.toString("base64") } };
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
void PublicKey; // keep import used by types above via type inference
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SPL Token (or Token-2022) Mint parser. Reads the first 82 bytes; ignores
|
|
6
|
+
* Token-2022 extensions (we don't need them for BPT metadata).
|
|
7
|
+
*
|
|
8
|
+
* Layout:
|
|
9
|
+
* 0..36 COption<Pubkey> mintAuthority (4 bytes tag + 32 bytes pubkey)
|
|
10
|
+
* 36..44 u64 supply
|
|
11
|
+
* 44..45 u8 decimals
|
|
12
|
+
* 45..46 u8 isInitialized
|
|
13
|
+
* 46..82 COption<Pubkey> freezeAuthority
|
|
14
|
+
*/
|
|
15
|
+
export interface RawMintAccount {
|
|
16
|
+
mintAuthority: PublicKey | null;
|
|
17
|
+
supply: BN;
|
|
18
|
+
decimals: number;
|
|
19
|
+
isInitialized: boolean;
|
|
20
|
+
freezeAuthority: PublicKey | null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function decodeMintAccount(data: Buffer): RawMintAccount {
|
|
24
|
+
if (data.length < 82) {
|
|
25
|
+
throw new Error(`decodeMintAccount: data too short (${data.length})`);
|
|
26
|
+
}
|
|
27
|
+
const mintAuthTag = data.readUInt32LE(0);
|
|
28
|
+
const mintAuthority =
|
|
29
|
+
mintAuthTag === 0 ? null : new PublicKey(data.slice(4, 36));
|
|
30
|
+
const supply = new BN(data.slice(36, 44), "le");
|
|
31
|
+
const decimals = data.readUInt8(44);
|
|
32
|
+
const isInitialized = data.readUInt8(45) !== 0;
|
|
33
|
+
const freezeTag = data.readUInt32LE(46);
|
|
34
|
+
const freezeAuthority =
|
|
35
|
+
freezeTag === 0 ? null : new PublicKey(data.slice(50, 82));
|
|
36
|
+
return { mintAuthority, supply, decimals, isInitialized, freezeAuthority };
|
|
37
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Raw binary layout of CubicPool (see
|
|
6
|
+
* `contracts/programs/cubic-pool/src/state/cubic_pool.rs`). Borsh-serialised.
|
|
7
|
+
*
|
|
8
|
+
* We decode manually to avoid bundling an Anchor Program instance into the
|
|
9
|
+
* SDK: the SDK is consumed by both frontend and backend, where dragging a
|
|
10
|
+
* full Anchor runtime is heavy. The on-chain layout is stable (reserved[128]
|
|
11
|
+
* at the tail).
|
|
12
|
+
*/
|
|
13
|
+
export interface RawPoolAccount {
|
|
14
|
+
config: PublicKey;
|
|
15
|
+
bump: number;
|
|
16
|
+
tokenCount: number;
|
|
17
|
+
poolId: BN;
|
|
18
|
+
tokenMints: PublicKey[]; // length 10
|
|
19
|
+
tokenPrograms: PublicKey[]; // length 10
|
|
20
|
+
normalizedWeights: BN[]; // length 10
|
|
21
|
+
virtualBalances: BN[]; // length 10
|
|
22
|
+
actualBalances: BN[]; // length 10
|
|
23
|
+
swapFeeRate: number;
|
|
24
|
+
protocolFeeRate: number;
|
|
25
|
+
protocolFeesOwed: BN[]; // length 10
|
|
26
|
+
createdAt: BN;
|
|
27
|
+
poolEnabled: boolean;
|
|
28
|
+
swapsEnabled: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** 8-byte anchor discriminator for CubicPool. */
|
|
32
|
+
export const POOL_DISCRIMINATOR_LEN = 8;
|
|
33
|
+
const MAX_TOKENS = 10;
|
|
34
|
+
|
|
35
|
+
export function decodePoolAccount(data: Buffer): RawPoolAccount {
|
|
36
|
+
if (data.length < POOL_DISCRIMINATOR_LEN + 1000) {
|
|
37
|
+
throw new Error(`decodePoolAccount: data too short (${data.length} bytes)`);
|
|
38
|
+
}
|
|
39
|
+
let off = POOL_DISCRIMINATOR_LEN;
|
|
40
|
+
|
|
41
|
+
const config = new PublicKey(data.slice(off, off + 32));
|
|
42
|
+
off += 32;
|
|
43
|
+
const bump = data.readUInt8(off);
|
|
44
|
+
off += 1;
|
|
45
|
+
const tokenCount = data.readUInt8(off);
|
|
46
|
+
off += 1;
|
|
47
|
+
const poolId = readU64LE(data, off);
|
|
48
|
+
off += 8;
|
|
49
|
+
|
|
50
|
+
const tokenMints: PublicKey[] = [];
|
|
51
|
+
for (let i = 0; i < MAX_TOKENS; i++) {
|
|
52
|
+
tokenMints.push(new PublicKey(data.slice(off, off + 32)));
|
|
53
|
+
off += 32;
|
|
54
|
+
}
|
|
55
|
+
const tokenPrograms: PublicKey[] = [];
|
|
56
|
+
for (let i = 0; i < MAX_TOKENS; i++) {
|
|
57
|
+
tokenPrograms.push(new PublicKey(data.slice(off, off + 32)));
|
|
58
|
+
off += 32;
|
|
59
|
+
}
|
|
60
|
+
const normalizedWeights: BN[] = [];
|
|
61
|
+
for (let i = 0; i < MAX_TOKENS; i++) {
|
|
62
|
+
normalizedWeights.push(readU64LE(data, off));
|
|
63
|
+
off += 8;
|
|
64
|
+
}
|
|
65
|
+
const virtualBalances: BN[] = [];
|
|
66
|
+
for (let i = 0; i < MAX_TOKENS; i++) {
|
|
67
|
+
virtualBalances.push(readU64LE(data, off));
|
|
68
|
+
off += 8;
|
|
69
|
+
}
|
|
70
|
+
const actualBalances: BN[] = [];
|
|
71
|
+
for (let i = 0; i < MAX_TOKENS; i++) {
|
|
72
|
+
actualBalances.push(readU64LE(data, off));
|
|
73
|
+
off += 8;
|
|
74
|
+
}
|
|
75
|
+
const swapFeeRate = data.readUInt32LE(off);
|
|
76
|
+
off += 4;
|
|
77
|
+
const protocolFeeRate = data.readUInt16LE(off);
|
|
78
|
+
off += 2;
|
|
79
|
+
const protocolFeesOwed: BN[] = [];
|
|
80
|
+
for (let i = 0; i < MAX_TOKENS; i++) {
|
|
81
|
+
protocolFeesOwed.push(readU64LE(data, off));
|
|
82
|
+
off += 8;
|
|
83
|
+
}
|
|
84
|
+
const createdAt = readU64LE(data, off); // i64 — but Solana ts ≥ 0
|
|
85
|
+
off += 8;
|
|
86
|
+
const poolEnabled = data.readUInt8(off) !== 0;
|
|
87
|
+
off += 1;
|
|
88
|
+
const swapsEnabled = data.readUInt8(off) !== 0;
|
|
89
|
+
off += 1;
|
|
90
|
+
// reserved[128] trails — ignored.
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
config,
|
|
94
|
+
bump,
|
|
95
|
+
tokenCount,
|
|
96
|
+
poolId,
|
|
97
|
+
tokenMints,
|
|
98
|
+
tokenPrograms,
|
|
99
|
+
normalizedWeights,
|
|
100
|
+
virtualBalances,
|
|
101
|
+
actualBalances,
|
|
102
|
+
swapFeeRate,
|
|
103
|
+
protocolFeeRate,
|
|
104
|
+
protocolFeesOwed,
|
|
105
|
+
createdAt,
|
|
106
|
+
poolEnabled,
|
|
107
|
+
swapsEnabled,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function readU64LE(data: Buffer, off: number): BN {
|
|
112
|
+
return new BN(data.slice(off, off + 8), "le");
|
|
113
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
|
|
4
|
+
export type CubicPoolEvent =
|
|
5
|
+
| PoolInitializedEvent
|
|
6
|
+
| SwapEvent
|
|
7
|
+
| LiquidityAddedEvent
|
|
8
|
+
| LiquidityRemovedEvent
|
|
9
|
+
| ProtocolFeesCollectedEvent
|
|
10
|
+
| PoolEnabledUpdatedEvent
|
|
11
|
+
| SwapsEnabledUpdatedEvent
|
|
12
|
+
| SingleTokenDepositEvent
|
|
13
|
+
| UnknownEvent;
|
|
14
|
+
|
|
15
|
+
export interface PoolInitializedEvent {
|
|
16
|
+
kind: "PoolInitialized";
|
|
17
|
+
pool: PublicKey;
|
|
18
|
+
config: PublicKey;
|
|
19
|
+
tokenCount: number;
|
|
20
|
+
bptMint: PublicKey;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SwapEvent {
|
|
25
|
+
kind: "Swap";
|
|
26
|
+
pool: PublicKey;
|
|
27
|
+
user: PublicKey;
|
|
28
|
+
tokenIn: PublicKey;
|
|
29
|
+
tokenOut: PublicKey;
|
|
30
|
+
amountIn: BN;
|
|
31
|
+
amountOut: BN;
|
|
32
|
+
feeAmount: BN;
|
|
33
|
+
protocolFeeAmount: BN;
|
|
34
|
+
timestamp: number;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface LiquidityAddedEvent {
|
|
38
|
+
kind: "LiquidityAdded";
|
|
39
|
+
pool: PublicKey;
|
|
40
|
+
user: PublicKey;
|
|
41
|
+
tokenAmounts: BN[];
|
|
42
|
+
bptAmount: BN;
|
|
43
|
+
timestamp: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface LiquidityRemovedEvent {
|
|
47
|
+
kind: "LiquidityRemoved";
|
|
48
|
+
pool: PublicKey;
|
|
49
|
+
user: PublicKey;
|
|
50
|
+
bptAmount: BN;
|
|
51
|
+
tokenAmounts: BN[];
|
|
52
|
+
timestamp: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface ProtocolFeesCollectedEvent {
|
|
56
|
+
kind: "ProtocolFeesCollected";
|
|
57
|
+
pool: PublicKey;
|
|
58
|
+
authority: PublicKey;
|
|
59
|
+
tokenAmounts: BN[];
|
|
60
|
+
timestamp: number;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface PoolEnabledUpdatedEvent {
|
|
64
|
+
kind: "PoolEnabledUpdated";
|
|
65
|
+
pool: PublicKey;
|
|
66
|
+
authority: PublicKey;
|
|
67
|
+
oldValue: boolean;
|
|
68
|
+
newValue: boolean;
|
|
69
|
+
timestamp: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface SwapsEnabledUpdatedEvent {
|
|
73
|
+
kind: "SwapsEnabledUpdated";
|
|
74
|
+
pool: PublicKey;
|
|
75
|
+
authority: PublicKey;
|
|
76
|
+
oldValue: boolean;
|
|
77
|
+
newValue: boolean;
|
|
78
|
+
timestamp: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface SingleTokenDepositEvent {
|
|
82
|
+
kind: "SingleTokenDeposit";
|
|
83
|
+
helper: PublicKey;
|
|
84
|
+
pool: PublicKey;
|
|
85
|
+
user: PublicKey;
|
|
86
|
+
tokenInIndex: number;
|
|
87
|
+
amountIn: BN;
|
|
88
|
+
slippageHundredthsBps: number;
|
|
89
|
+
allocations: BN[];
|
|
90
|
+
depositedAmounts: BN[];
|
|
91
|
+
bptReceived: BN;
|
|
92
|
+
dustRefunded: BN;
|
|
93
|
+
timestamp: number;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface UnknownEvent {
|
|
97
|
+
kind: "Unknown";
|
|
98
|
+
name: string;
|
|
99
|
+
data: Record<string, unknown>;
|
|
100
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
import { TokenInfo } from "../config/tokens";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* A single token slot inside a Cubic Pool, enriched with off-chain metadata
|
|
7
|
+
* (ticker, logo, coingeckoId) when available.
|
|
8
|
+
*/
|
|
9
|
+
export interface PoolTokenInfo {
|
|
10
|
+
index: number;
|
|
11
|
+
mint: PublicKey;
|
|
12
|
+
tokenProgram: PublicKey;
|
|
13
|
+
decimals: number;
|
|
14
|
+
/** Basis points, sum across all tokens == 10_000. */
|
|
15
|
+
weightBps: number;
|
|
16
|
+
/** In native token units (u64). */
|
|
17
|
+
virtualBalance: BN;
|
|
18
|
+
actualBalance: BN;
|
|
19
|
+
protocolFeesOwed: BN;
|
|
20
|
+
/** PDA: ATA(pool, mint, tokenProgram). */
|
|
21
|
+
vault: PublicKey;
|
|
22
|
+
/** Cached off-chain metadata; `undefined` if no registry hit. */
|
|
23
|
+
metadata?: TokenInfo;
|
|
24
|
+
/** factBalance / virtBalance as a float, for display/math. */
|
|
25
|
+
concentration: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Fully parsed pool state. Returned by `CubicPoolClient.sync()`.
|
|
30
|
+
* All raw on-chain numerics plus a few convenience derivations.
|
|
31
|
+
*/
|
|
32
|
+
export interface PoolInfo {
|
|
33
|
+
/** Pool PDA address. */
|
|
34
|
+
address: PublicKey;
|
|
35
|
+
/** Pool config account referenced by the pool. */
|
|
36
|
+
config: PublicKey;
|
|
37
|
+
/** `Account<CubicPool>.bump`. */
|
|
38
|
+
bump: number;
|
|
39
|
+
/** Integer identifier salt. Pools under the same config differ by pool_id. */
|
|
40
|
+
poolId: BN;
|
|
41
|
+
tokenCount: number;
|
|
42
|
+
tokens: PoolTokenInfo[];
|
|
43
|
+
bptMint: PublicKey;
|
|
44
|
+
/** Total supply of BPT tokens at snapshot time. */
|
|
45
|
+
bptTotalSupply: BN;
|
|
46
|
+
/** Hundredths-of-basis-point units (1_000_000 == 100 %). */
|
|
47
|
+
swapFeeRate: number;
|
|
48
|
+
/** Basis points (10_000 == 100 %). */
|
|
49
|
+
protocolFeeRate: number;
|
|
50
|
+
poolEnabled: boolean;
|
|
51
|
+
swapsEnabled: boolean;
|
|
52
|
+
createdAt: number;
|
|
53
|
+
/** Unix timestamp (ms) when sync() ran. Useful for staleness checks. */
|
|
54
|
+
syncedAt: number;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface PoolSummary {
|
|
58
|
+
address: string;
|
|
59
|
+
tvlUsd?: number;
|
|
60
|
+
volume24hUsd?: number;
|
|
61
|
+
feeApr7d?: number;
|
|
62
|
+
poolEnabled: boolean;
|
|
63
|
+
tokens: Array<{ mint: string; symbol?: string; weightBps: number }>;
|
|
64
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every public SDK method returns a SdkResult<T>. Callers never need try/catch.
|
|
3
|
+
* Use `if (res.ok) { ... } else { ... }` to branch.
|
|
4
|
+
*/
|
|
5
|
+
export type SdkResult<T> =
|
|
6
|
+
| { ok: true; data: T }
|
|
7
|
+
| { ok: false; error: SdkError };
|
|
8
|
+
|
|
9
|
+
export interface SdkError {
|
|
10
|
+
/** Stable machine-readable code. */
|
|
11
|
+
code: SdkErrorCode;
|
|
12
|
+
/** Human-readable message, safe to show to end users. */
|
|
13
|
+
humanMessage: string;
|
|
14
|
+
/** Original error for debugging; never render this to end users. */
|
|
15
|
+
cause?: unknown;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type SdkErrorCode =
|
|
19
|
+
| "rpc_unavailable"
|
|
20
|
+
| "rpc_timeout"
|
|
21
|
+
| "rpc_rate_limited"
|
|
22
|
+
| "account_not_found"
|
|
23
|
+
| "invalid_input"
|
|
24
|
+
| "math_overflow"
|
|
25
|
+
| "parse_failure"
|
|
26
|
+
| "backend_unavailable"
|
|
27
|
+
| "backend_invalid_response"
|
|
28
|
+
| "insufficient_funds"
|
|
29
|
+
| "pool_disabled"
|
|
30
|
+
| "swaps_disabled"
|
|
31
|
+
| "unsupported_pool_state"
|
|
32
|
+
| "slippage_exceeded"
|
|
33
|
+
| "simulation_failed"
|
|
34
|
+
| "tx_build_failed"
|
|
35
|
+
| "unknown";
|
|
36
|
+
|
|
37
|
+
export const ok = <T>(data: T): SdkResult<T> => ({ ok: true, data });
|
|
38
|
+
export const err = (
|
|
39
|
+
code: SdkErrorCode,
|
|
40
|
+
humanMessage: string,
|
|
41
|
+
cause?: unknown
|
|
42
|
+
): SdkResult<never> => ({
|
|
43
|
+
ok: false,
|
|
44
|
+
error: { code, humanMessage, cause },
|
|
45
|
+
});
|