@coffer_so/sdk 1.0.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 +283 -0
- package/dist/clients/AdminClient.d.ts +194 -0
- package/dist/clients/AdminClient.d.ts.map +1 -0
- package/dist/clients/AdminClient.js +482 -0
- package/dist/clients/AdminClient.js.map +1 -0
- package/dist/clients/CofferBackendClient.d.ts +877 -0
- package/dist/clients/CofferBackendClient.d.ts.map +1 -0
- package/dist/clients/CofferBackendClient.js +536 -0
- package/dist/clients/CofferBackendClient.js.map +1 -0
- package/dist/clients/CubeBackendClient.d.ts +877 -0
- package/dist/clients/CubeBackendClient.d.ts.map +1 -0
- package/dist/clients/CubeBackendClient.js +536 -0
- package/dist/clients/CubeBackendClient.js.map +1 -0
- package/dist/clients/CubicPoolClient.d.ts +120 -0
- package/dist/clients/CubicPoolClient.d.ts.map +1 -0
- package/dist/clients/CubicPoolClient.js +687 -0
- package/dist/clients/CubicPoolClient.js.map +1 -0
- package/dist/clients/PoolFactoryClient.d.ts +53 -0
- package/dist/clients/PoolFactoryClient.d.ts.map +1 -0
- package/dist/clients/PoolFactoryClient.js +91 -0
- package/dist/clients/PoolFactoryClient.js.map +1 -0
- package/dist/clients/RpcClient.d.ts +49 -0
- package/dist/clients/RpcClient.d.ts.map +1 -0
- package/dist/clients/RpcClient.js +135 -0
- package/dist/clients/RpcClient.js.map +1 -0
- package/dist/clients/SingleTokenDepositClient.d.ts +67 -0
- package/dist/clients/SingleTokenDepositClient.d.ts.map +1 -0
- package/dist/clients/SingleTokenDepositClient.js +105 -0
- package/dist/clients/SingleTokenDepositClient.js.map +1 -0
- package/dist/clients/contract-instructions.d.ts +24 -0
- package/dist/clients/contract-instructions.d.ts.map +1 -0
- package/dist/clients/contract-instructions.js +130 -0
- package/dist/clients/contract-instructions.js.map +1 -0
- package/dist/clients/index.d.ts +10 -0
- package/dist/clients/index.d.ts.map +1 -0
- package/dist/clients/index.js +26 -0
- package/dist/clients/index.js.map +1 -0
- package/dist/clients/quote-math.d.ts +15 -0
- package/dist/clients/quote-math.d.ts.map +1 -0
- package/dist/clients/quote-math.js +98 -0
- package/dist/clients/quote-math.js.map +1 -0
- package/dist/clients/tx-builders.d.ts +231 -0
- package/dist/clients/tx-builders.d.ts.map +1 -0
- package/dist/clients/tx-builders.js +861 -0
- package/dist/clients/tx-builders.js.map +1 -0
- package/dist/clients/versioned.d.ts +34 -0
- package/dist/clients/versioned.d.ts.map +1 -0
- package/dist/clients/versioned.js +55 -0
- package/dist/clients/versioned.js.map +1 -0
- package/dist/config/index.d.ts +65 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +75 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/networks.d.ts +12 -0
- package/dist/config/networks.d.ts.map +1 -0
- package/dist/config/networks.js +51 -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 +4203 -0
- package/dist/idl/index.d.ts +1769 -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 +2615 -0
- package/dist/idl/single_token_liquidity.json +1121 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +206 -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 +82 -0
- package/dist/math/cubicMath.js.map +1 -0
- package/dist/math/fixedPoint.d.ts +19 -0
- package/dist/math/fixedPoint.d.ts.map +1 -0
- package/dist/math/fixedPoint.js +86 -0
- package/dist/math/fixedPoint.js.map +1 -0
- package/dist/math/index.d.ts +9 -0
- package/dist/math/index.d.ts.map +1 -0
- package/dist/math/index.js +30 -0
- package/dist/math/index.js.map +1 -0
- package/dist/math/logExp.d.ts +6 -0
- package/dist/math/logExp.d.ts.map +1 -0
- package/dist/math/logExp.js +68 -0
- package/dist/math/logExp.js.map +1 -0
- package/dist/math/lpLoss.d.ts +44 -0
- package/dist/math/lpLoss.d.ts.map +1 -0
- package/dist/math/lpLoss.js +103 -0
- package/dist/math/lpLoss.js.map +1 -0
- package/dist/math/maxSelloff.d.ts +164 -0
- package/dist/math/maxSelloff.d.ts.map +1 -0
- package/dist/math/maxSelloff.js +397 -0
- package/dist/math/maxSelloff.js.map +1 -0
- package/dist/math/singleToken.d.ts +54 -0
- package/dist/math/singleToken.d.ts.map +1 -0
- package/dist/math/singleToken.js +188 -0
- package/dist/math/singleToken.js.map +1 -0
- package/dist/math/slippage.d.ts +13 -0
- package/dist/math/slippage.d.ts.map +1 -0
- package/dist/math/slippage.js +44 -0
- package/dist/math/slippage.js.map +1 -0
- package/dist/math/surgeFee.d.ts +24 -0
- package/dist/math/surgeFee.d.ts.map +1 -0
- package/dist/math/surgeFee.js +85 -0
- package/dist/math/surgeFee.js.map +1 -0
- package/dist/math/weightedMath.d.ts +20 -0
- package/dist/math/weightedMath.d.ts.map +1 -0
- package/dist/math/weightedMath.js +66 -0
- package/dist/math/weightedMath.js.map +1 -0
- package/dist/parsers/borsh.d.ts +28 -0
- package/dist/parsers/borsh.d.ts.map +1 -0
- package/dist/parsers/borsh.js +104 -0
- package/dist/parsers/borsh.js.map +1 -0
- package/dist/parsers/contracts.d.ts +26 -0
- package/dist/parsers/contracts.d.ts.map +1 -0
- package/dist/parsers/contracts.js +62 -0
- package/dist/parsers/contracts.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 +281 -0
- package/dist/parsers/events.js.map +1 -0
- package/dist/parsers/index.d.ts +6 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +22 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/parsers/mintAccount.d.ts +24 -0
- package/dist/parsers/mintAccount.d.ts.map +1 -0
- package/dist/parsers/mintAccount.js +27 -0
- package/dist/parsers/mintAccount.js.map +1 -0
- package/dist/parsers/poolAccount.d.ts +141 -0
- package/dist/parsers/poolAccount.d.ts.map +1 -0
- package/dist/parsers/poolAccount.js +224 -0
- package/dist/parsers/poolAccount.js.map +1 -0
- package/dist/types/contracts.d.ts +1340 -0
- package/dist/types/contracts.d.ts.map +1 -0
- package/dist/types/contracts.js +3 -0
- package/dist/types/contracts.js.map +1 -0
- package/dist/types/events.d.ts +172 -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 +6 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +22 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/pool.d.ts +149 -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 +24 -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 +261 -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 +44 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +192 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/extensions.d.ts +98 -0
- package/dist/utils/extensions.d.ts.map +1 -0
- package/dist/utils/extensions.js +187 -0
- package/dist/utils/extensions.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +21 -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 +608 -0
- package/src/clients/CofferBackendClient.ts +1413 -0
- package/src/clients/CubicPoolClient.ts +719 -0
- package/src/clients/PoolFactoryClient.ts +110 -0
- package/src/clients/RpcClient.ts +195 -0
- package/src/clients/SingleTokenDepositClient.ts +143 -0
- package/src/clients/contract-instructions.ts +115 -0
- package/src/clients/index.ts +9 -0
- package/src/clients/quote-math.ts +81 -0
- package/src/clients/tx-builders.ts +1096 -0
- package/src/clients/versioned.ts +80 -0
- package/src/config/index.ts +111 -0
- package/src/config/networks.ts +71 -0
- package/src/config/tokens.ts +52 -0
- package/src/idl/cubic_pool.json +4203 -0
- package/src/idl/index.ts +13 -0
- package/src/idl/protocol_admin.json +2615 -0
- package/src/idl/single_token_liquidity.json +1121 -0
- package/src/index.ts +262 -0
- package/src/math/cubicMath.ts +92 -0
- package/src/math/fixedPoint.ts +71 -0
- package/src/math/index.ts +13 -0
- package/src/math/logExp.ts +61 -0
- package/src/math/lpLoss.ts +160 -0
- package/src/math/maxSelloff.ts +564 -0
- package/src/math/singleToken.ts +251 -0
- package/src/math/slippage.ts +38 -0
- package/src/math/surgeFee.ts +101 -0
- package/src/math/weightedMath.ts +64 -0
- package/src/parsers/borsh.ts +106 -0
- package/src/parsers/contracts.ts +64 -0
- package/src/parsers/events.ts +275 -0
- package/src/parsers/index.ts +5 -0
- package/src/parsers/mintAccount.ts +44 -0
- package/src/parsers/poolAccount.ts +372 -0
- package/src/types/contracts.ts +238 -0
- package/src/types/events.ts +198 -0
- package/src/types/index.ts +5 -0
- package/src/types/pool.ts +147 -0
- package/src/types/result.ts +51 -0
- package/src/types/tx.ts +278 -0
- package/src/utils/errors.ts +210 -0
- package/src/utils/extensions.ts +196 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/pda.ts +58 -0
- package/src/utils/retry.ts +85 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
import { BorshReader } from "./borsh";
|
|
4
|
+
import { decodeContractEvent, ContractEvent } from "./contracts";
|
|
5
|
+
import { CubicPoolEvent, AdditionalContractEvent } from "../types/events";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Anchor event log format (base64-encoded after `Program data:`):
|
|
9
|
+
* [ 8-byte discriminator | borsh-encoded event struct ]
|
|
10
|
+
*
|
|
11
|
+
* Discriminators for cubic-pool and single-token-liquidity are hard-coded
|
|
12
|
+
* here from the generated IDL. Never guess from event name — the Anchor
|
|
13
|
+
* macro actually uses `sha256("event:<Name>")[0..8]`, but for robustness
|
|
14
|
+
* we pin the bytes.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// Cubic-pool event discriminators (from target/idl/cubic_pool.json):
|
|
18
|
+
const DISC = {
|
|
19
|
+
PoolInitialized: Buffer.from([100, 118, 173, 87, 12, 198, 254, 229]),
|
|
20
|
+
Swap: Buffer.from([81, 108, 227, 190, 205, 208, 10, 196]),
|
|
21
|
+
LiquidityAdded: Buffer.from([154, 26, 221, 108, 238, 64, 217, 161]),
|
|
22
|
+
LiquidityRemoved: Buffer.from([225, 105, 216, 39, 124, 116, 169, 189]),
|
|
23
|
+
ProtocolFeesCollected: Buffer.from([165, 34, 125, 155, 15, 86, 99, 191]),
|
|
24
|
+
SwapFeeRateUpdated: Buffer.from([101, 132, 24, 255, 91, 253, 227, 101]),
|
|
25
|
+
ProtocolFeeRateUpdated: Buffer.from([189, 56, 7, 65, 0, 95, 192, 6]),
|
|
26
|
+
PoolEnabledUpdated: Buffer.from([101, 47, 3, 240, 197, 181, 236, 142]),
|
|
27
|
+
SwapsEnabledUpdated: Buffer.from([55, 116, 118, 138, 102, 26, 227, 223]),
|
|
28
|
+
DebugLiquidityWithdrawn: Buffer.from([174, 59, 149, 22, 135, 129, 129, 83]),
|
|
29
|
+
PoolStateLog: Buffer.from([59, 254, 237, 111, 163, 10, 140, 224]),
|
|
30
|
+
PoolInfo: Buffer.from([207, 20, 87, 97, 251, 212, 234, 45]),
|
|
31
|
+
BannedExtensionsUpdated: Buffer.from([107, 126, 13, 149, 182, 108, 139, 202]),
|
|
32
|
+
MaxSelloffWindowAdvanced: Buffer.from([229, 227, 163, 30, 22, 183, 78, 57]),
|
|
33
|
+
// Stld:
|
|
34
|
+
SingleTokenDeposit: Buffer.from([215, 54, 137, 104, 219, 39, 164, 235]),
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
type DiscName = keyof typeof DISC;
|
|
38
|
+
|
|
39
|
+
export function parseCubicPoolEvents(logs: string[]): CubicPoolEvent[] {
|
|
40
|
+
const out: CubicPoolEvent[] = [];
|
|
41
|
+
for (const line of logs) {
|
|
42
|
+
const m = line.match(/^Program data:\s+(.+)$/);
|
|
43
|
+
if (!m) continue;
|
|
44
|
+
const buf = Buffer.from(m[1], "base64");
|
|
45
|
+
if (buf.length < 8) continue;
|
|
46
|
+
const disc = buf.slice(0, 8);
|
|
47
|
+
const payload = buf.slice(8);
|
|
48
|
+
const name = matchDiscriminator(disc);
|
|
49
|
+
if (!name) {
|
|
50
|
+
const current = decodeContractEvent(m[1]);
|
|
51
|
+
if (current) {
|
|
52
|
+
try { out.push(camelEvent(current)); }
|
|
53
|
+
catch (error) { out.push({ kind: "Unknown", name: current.kind, data: { error: String(error), payload: payload.toString("base64") } }); }
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
out.push({ kind: "Unknown", name: "unknown", data: { disc: disc.toString("base64"), payload: payload.toString("base64") } });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const ev = decodeEvent(name, payload);
|
|
61
|
+
if (ev) out.push(ev);
|
|
62
|
+
} catch (e) {
|
|
63
|
+
out.push({
|
|
64
|
+
kind: "Unknown",
|
|
65
|
+
name,
|
|
66
|
+
data: { error: String(e), payload: payload.toString("base64") },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function matchDiscriminator(d: Buffer): DiscName | null {
|
|
74
|
+
for (const [name, bytes] of Object.entries(DISC)) {
|
|
75
|
+
if (bytes.equals(d)) return name as DiscName;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function decodeEvent(name: DiscName, buf: Buffer): CubicPoolEvent | null {
|
|
81
|
+
const r = new BorshReader(buf);
|
|
82
|
+
switch (name) {
|
|
83
|
+
case "Swap": {
|
|
84
|
+
// The deployed without-SF event ends with timestamp, surge_fee_amount.
|
|
85
|
+
// Optional trailing transfer-fee fields are retained only for decoding
|
|
86
|
+
// logs from other historical builds; they do not imply mint support.
|
|
87
|
+
const pool = r.pubkey();
|
|
88
|
+
const user = r.pubkey();
|
|
89
|
+
const tokenIn = r.pubkey();
|
|
90
|
+
const tokenOut = r.pubkey();
|
|
91
|
+
const amountIn = r.u64();
|
|
92
|
+
const amountOut = r.u64();
|
|
93
|
+
const feeAmount = r.u64();
|
|
94
|
+
const protocolFeeAmount = r.u64();
|
|
95
|
+
const timestamp = r.i64().toNumber();
|
|
96
|
+
const surgeFeeAmount = r.u64();
|
|
97
|
+
// Absent in the deployed without-SF ABI: expose zero for compatibility.
|
|
98
|
+
const transferFeeIn = r.remaining() >= 8 ? r.u64() : new BN(0);
|
|
99
|
+
const transferFeeOut = r.remaining() >= 8 ? r.u64() : new BN(0);
|
|
100
|
+
return {
|
|
101
|
+
kind: "Swap",
|
|
102
|
+
pool,
|
|
103
|
+
user,
|
|
104
|
+
tokenIn,
|
|
105
|
+
tokenOut,
|
|
106
|
+
amountIn,
|
|
107
|
+
amountOut,
|
|
108
|
+
feeAmount,
|
|
109
|
+
protocolFeeAmount,
|
|
110
|
+
surgeFeeAmount,
|
|
111
|
+
timestamp,
|
|
112
|
+
transferFeeIn,
|
|
113
|
+
transferFeeOut,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
case "LiquidityAdded": {
|
|
117
|
+
const pool = r.pubkey();
|
|
118
|
+
const user = r.pubkey();
|
|
119
|
+
const tokenAmounts = r.vecU64();
|
|
120
|
+
const bptAmount = r.u64();
|
|
121
|
+
const timestamp = r.i64().toNumber();
|
|
122
|
+
return { kind: "LiquidityAdded", pool, user, tokenAmounts, bptAmount, timestamp };
|
|
123
|
+
}
|
|
124
|
+
case "LiquidityRemoved": {
|
|
125
|
+
const pool = r.pubkey();
|
|
126
|
+
const user = r.pubkey();
|
|
127
|
+
const bptAmount = r.u64();
|
|
128
|
+
const tokenAmounts = r.vecU64();
|
|
129
|
+
const timestamp = r.i64().toNumber();
|
|
130
|
+
return { kind: "LiquidityRemoved", pool, user, bptAmount, tokenAmounts, timestamp };
|
|
131
|
+
}
|
|
132
|
+
case "ProtocolFeesCollected": {
|
|
133
|
+
const pool = r.pubkey();
|
|
134
|
+
const authority = r.pubkey();
|
|
135
|
+
const tokenAmounts = r.vecU64();
|
|
136
|
+
const timestamp = r.i64().toNumber();
|
|
137
|
+
return { kind: "ProtocolFeesCollected", pool, authority, tokenAmounts, timestamp };
|
|
138
|
+
}
|
|
139
|
+
case "PoolInitialized": {
|
|
140
|
+
const pool = r.pubkey();
|
|
141
|
+
const config = r.pubkey();
|
|
142
|
+
const tokenCount = r.u8();
|
|
143
|
+
const bptMint = r.pubkey();
|
|
144
|
+
const timestamp = r.i64().toNumber();
|
|
145
|
+
// Appended: the effective banned-extensions bitmap the pool's tokens
|
|
146
|
+
// were vetted against at creation. Guarded for older logs.
|
|
147
|
+
const bannedExtensions = r.remaining() >= 8 ? r.u64() : new BN(0);
|
|
148
|
+
return { kind: "PoolInitialized", pool, config, tokenCount, bptMint, timestamp, bannedExtensions };
|
|
149
|
+
}
|
|
150
|
+
case "PoolEnabledUpdated": {
|
|
151
|
+
const pool = r.pubkey();
|
|
152
|
+
const authority = r.pubkey();
|
|
153
|
+
const oldValue = r.bool();
|
|
154
|
+
const newValue = r.bool();
|
|
155
|
+
const timestamp = r.i64().toNumber();
|
|
156
|
+
return { kind: "PoolEnabledUpdated", pool, authority, oldValue, newValue, timestamp };
|
|
157
|
+
}
|
|
158
|
+
case "SwapsEnabledUpdated": {
|
|
159
|
+
const pool = r.pubkey();
|
|
160
|
+
const authority = r.pubkey();
|
|
161
|
+
const oldValue = r.bool();
|
|
162
|
+
const newValue = r.bool();
|
|
163
|
+
const timestamp = r.i64().toNumber();
|
|
164
|
+
return { kind: "SwapsEnabledUpdated", pool, authority, oldValue, newValue, timestamp };
|
|
165
|
+
}
|
|
166
|
+
case "SingleTokenDeposit": {
|
|
167
|
+
// Verified against `SingleTokenDeposit` in
|
|
168
|
+
// src/idl/single_token_liquidity.json:
|
|
169
|
+
// helper, pool, user, token_in_index, amount_in, allocations,
|
|
170
|
+
// deposited_amounts, bpt_received, dust_refunded, timestamp
|
|
171
|
+
//
|
|
172
|
+
// Two fixes vs. the previous decoder:
|
|
173
|
+
// 1. There is NO `slippage_hundredths_bps: u32` field. The per-call
|
|
174
|
+
// slippage argument was removed from `deposit_single_token` (the
|
|
175
|
+
// single `minimum_bpt_amount` guard replaced it) and the event
|
|
176
|
+
// field went with it. Reading a phantom u32 here shifted every
|
|
177
|
+
// later field by 4 bytes.
|
|
178
|
+
// 2. `dust_refunded` is `Vec<u64>` (index-aligned with the pool's
|
|
179
|
+
// tokens), not a scalar `u64`.
|
|
180
|
+
const helper = r.pubkey();
|
|
181
|
+
const pool = r.pubkey();
|
|
182
|
+
const user = r.pubkey();
|
|
183
|
+
const tokenInIndex = r.u8();
|
|
184
|
+
const amountIn = r.u64();
|
|
185
|
+
const allocations = r.vecU64();
|
|
186
|
+
const depositedAmounts = r.vecU64();
|
|
187
|
+
const bptReceived = r.u64();
|
|
188
|
+
const dustRefunded = r.vecU64();
|
|
189
|
+
const timestamp = r.i64().toNumber();
|
|
190
|
+
return {
|
|
191
|
+
kind: "SingleTokenDeposit",
|
|
192
|
+
helper,
|
|
193
|
+
pool,
|
|
194
|
+
user,
|
|
195
|
+
tokenInIndex,
|
|
196
|
+
amountIn,
|
|
197
|
+
allocations,
|
|
198
|
+
depositedAmounts,
|
|
199
|
+
bptReceived,
|
|
200
|
+
dustRefunded,
|
|
201
|
+
timestamp,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
case "PoolStateLog": {
|
|
205
|
+
const pool = r.pubkey();
|
|
206
|
+
const virtualBalances = r.vecU64();
|
|
207
|
+
const actualBalances = r.vecU64();
|
|
208
|
+
const protocolFeesOwed = r.vecU64();
|
|
209
|
+
const timestamp = r.i64().toNumber();
|
|
210
|
+
return { kind: "PoolStateLog", pool, virtualBalances, actualBalances, protocolFeesOwed, timestamp };
|
|
211
|
+
}
|
|
212
|
+
case "MaxSelloffWindowAdvanced": {
|
|
213
|
+
const pool = r.pubkey();
|
|
214
|
+
const tokenIndex = r.u8();
|
|
215
|
+
const effectiveSelloff = r.u64();
|
|
216
|
+
const maxSelloffCap = r.u64();
|
|
217
|
+
const vbSnapshot = r.u64();
|
|
218
|
+
const previousSelloff = r.u64();
|
|
219
|
+
const currentSelloff = r.u64();
|
|
220
|
+
const windowStartTimestamp = r.i64().toNumber();
|
|
221
|
+
const timestamp = r.i64().toNumber();
|
|
222
|
+
return {
|
|
223
|
+
kind: "MaxSelloffWindowAdvanced",
|
|
224
|
+
pool,
|
|
225
|
+
tokenIndex,
|
|
226
|
+
effectiveSelloff,
|
|
227
|
+
maxSelloffCap,
|
|
228
|
+
vbSnapshot,
|
|
229
|
+
previousSelloff,
|
|
230
|
+
currentSelloff,
|
|
231
|
+
windowStartTimestamp,
|
|
232
|
+
timestamp,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
case "BannedExtensionsUpdated": {
|
|
236
|
+
const config = r.pubkey();
|
|
237
|
+
const authority = r.pubkey();
|
|
238
|
+
const oldValue = r.u64();
|
|
239
|
+
const newValue = r.u64();
|
|
240
|
+
const timestamp = r.i64().toNumber();
|
|
241
|
+
// Appended in v5.1 alongside `set_banned_extensions`' new
|
|
242
|
+
// `hard_banned_extensions` argument. Guarded for older logs.
|
|
243
|
+
const oldHardValue = r.remaining() >= 8 ? r.u64() : new BN(0);
|
|
244
|
+
const newHardValue = r.remaining() >= 8 ? r.u64() : new BN(0);
|
|
245
|
+
return {
|
|
246
|
+
kind: "BannedExtensionsUpdated",
|
|
247
|
+
config,
|
|
248
|
+
authority,
|
|
249
|
+
oldValue,
|
|
250
|
+
newValue,
|
|
251
|
+
timestamp,
|
|
252
|
+
oldHardValue,
|
|
253
|
+
newHardValue,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
// Decode the remaining current events through the complete shipped ABI.
|
|
257
|
+
case "SwapFeeRateUpdated":
|
|
258
|
+
case "ProtocolFeeRateUpdated":
|
|
259
|
+
case "DebugLiquidityWithdrawn":
|
|
260
|
+
case "PoolInfo":
|
|
261
|
+
const current = decodeContractEvent(Buffer.concat([DISC[name], buf]).toString("base64"));
|
|
262
|
+
return current ? camelEvent(current) : { kind: "Unknown", name, data: { raw: buf.toString("base64") } };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
void PublicKey; // keep import used by types above via type inference
|
|
267
|
+
|
|
268
|
+
function camelEvent(event: ContractEvent): AdditionalContractEvent {
|
|
269
|
+
const fields: Record<string, unknown> = { kind: event.kind };
|
|
270
|
+
for (const [key, value] of Object.entries(event.data)) {
|
|
271
|
+
const name = key.replace(/_([a-z])/g, (_, letter: string) => letter.toUpperCase());
|
|
272
|
+
fields[name] = key === "timestamp" && BN.isBN(value) ? value.toNumber() : value;
|
|
273
|
+
}
|
|
274
|
+
return fields as AdditionalContractEvent;
|
|
275
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
import { parseMintExtensions } from "../utils/extensions";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* SPL Token (or Token-2022) Mint parser. Reads the 82-byte base layout and,
|
|
7
|
+
* for Token-2022 mints, the TLV extension list (discriminants only).
|
|
8
|
+
*
|
|
9
|
+
* Layout:
|
|
10
|
+
* 0..36 COption<Pubkey> mintAuthority (4 bytes tag + 32 bytes pubkey)
|
|
11
|
+
* 36..44 u64 supply
|
|
12
|
+
* 44..45 u8 decimals
|
|
13
|
+
* 45..46 u8 isInitialized
|
|
14
|
+
* 46..82 COption<Pubkey> freezeAuthority
|
|
15
|
+
*/
|
|
16
|
+
export interface RawMintAccount {
|
|
17
|
+
mintAuthority: PublicKey | null;
|
|
18
|
+
supply: BN;
|
|
19
|
+
decimals: number;
|
|
20
|
+
isInitialized: boolean;
|
|
21
|
+
freezeAuthority: PublicKey | null;
|
|
22
|
+
/** Token-2022 extension discriminants; `[]` for classic SPL mints. */
|
|
23
|
+
extensions: number[];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function decodeMintAccount(data: Buffer): RawMintAccount {
|
|
27
|
+
const extensions = parseMintExtensions(data);
|
|
28
|
+
const mintAuthTag = data.readUInt32LE(0);
|
|
29
|
+
if (mintAuthTag !== 0 && mintAuthTag !== 1) {
|
|
30
|
+
throw new Error(`decodeMintAccount: invalid mint-authority option ${mintAuthTag}`);
|
|
31
|
+
}
|
|
32
|
+
const mintAuthority =
|
|
33
|
+
mintAuthTag === 0 ? null : new PublicKey(data.slice(4, 36));
|
|
34
|
+
const supply = new BN(data.slice(36, 44), "le");
|
|
35
|
+
const decimals = data.readUInt8(44);
|
|
36
|
+
const isInitialized = data.readUInt8(45) === 1;
|
|
37
|
+
const freezeTag = data.readUInt32LE(46);
|
|
38
|
+
if (freezeTag !== 0 && freezeTag !== 1) {
|
|
39
|
+
throw new Error(`decodeMintAccount: invalid freeze-authority option ${freezeTag}`);
|
|
40
|
+
}
|
|
41
|
+
const freezeAuthority =
|
|
42
|
+
freezeTag === 0 ? null : new PublicKey(data.slice(50, 82));
|
|
43
|
+
return { mintAuthority, supply, decimals, isInitialized, freezeAuthority, extensions };
|
|
44
|
+
}
|
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { PublicKey } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Raw binary layout of CubicPool **v5** (see
|
|
6
|
+
* `contracts/programs/cubic-pool/src/state/cubic_pool.rs`).
|
|
7
|
+
*
|
|
8
|
+
* v4 reorganised per-token data from six parallel arrays into a single
|
|
9
|
+
* `tokens: [TokenSlot; 10]` array (each slot = `AssetConfig` +
|
|
10
|
+
* `AssetDynamics`). For backwards compatibility with downstream
|
|
11
|
+
* consumers (`CubicPoolClient.sync()` and similar), this decoder still
|
|
12
|
+
* exposes the parallel-array shape, plus the new max-selloff and admin
|
|
13
|
+
* fields.
|
|
14
|
+
*
|
|
15
|
+
* v5.1 layout deltas (all carved out of `reserved`, total size unchanged
|
|
16
|
+
* at 1683 — every pre-existing field keeps its byte offset):
|
|
17
|
+
* - `AssetConfig.reserved[3]` → `variable_fee_slope_mid_pct: u16` +
|
|
18
|
+
* `variable_fee_kink_pct: u8`. Slot stays 88 bytes.
|
|
19
|
+
* - `CubicPool`: `range_manager_max_leverage_bps: u32` and
|
|
20
|
+
* `range_manager_min_leverage_bps: u32` appended after
|
|
21
|
+
* `banned_extensions`; trailing `reserved` shrank [u8;24] → [u8;16].
|
|
22
|
+
*
|
|
23
|
+
* Pools written by the pre-v5.1 program decode these new fields as `0`,
|
|
24
|
+
* which is the back-compatible reading (curve has no kink, leverage band
|
|
25
|
+
* disabled).
|
|
26
|
+
*
|
|
27
|
+
* We decode manually to avoid bundling an Anchor `Program` instance into
|
|
28
|
+
* the SDK — both frontend and backend consume the SDK, and pulling the
|
|
29
|
+
* full Anchor runtime is heavy.
|
|
30
|
+
*/
|
|
31
|
+
export interface RawPoolAccount {
|
|
32
|
+
config: PublicKey;
|
|
33
|
+
bump: number;
|
|
34
|
+
tokenCount: number;
|
|
35
|
+
poolId: BN;
|
|
36
|
+
swapFeeRate: number;
|
|
37
|
+
protocolFeeRate: number;
|
|
38
|
+
createdAt: BN;
|
|
39
|
+
poolEnabled: boolean;
|
|
40
|
+
swapsEnabled: boolean;
|
|
41
|
+
poolAdmin: PublicKey;
|
|
42
|
+
pendingPoolAdmin: PublicKey;
|
|
43
|
+
|
|
44
|
+
rangeManager: PublicKey;
|
|
45
|
+
rangeManagerEnabled: boolean;
|
|
46
|
+
/** Percent-of-current-value cap, `PERCENT_SCALE` units (10_000 = 100%). */
|
|
47
|
+
rangeManagerMaxVbChangePct: number;
|
|
48
|
+
rangeManagerMaxWeightChangePct: number;
|
|
49
|
+
rangeManagerMinUpdateIntervalSecs: number;
|
|
50
|
+
rangeManagerLastUpdated: BN;
|
|
51
|
+
/**
|
|
52
|
+
* Absolute UPPER bound on `virtual_balance / actual_balance` the range
|
|
53
|
+
* manager may leave a token at, in basis points (10_000 = 1.0×).
|
|
54
|
+
* `0` ⇒ band disabled. Pools written before v5.1 read `0`.
|
|
55
|
+
*
|
|
56
|
+
* The per-update percentage caps *velocity*; this caps *distance*.
|
|
57
|
+
* Breaching it reverts with `RangeManagerLeverageBandExceeded` (6062).
|
|
58
|
+
*/
|
|
59
|
+
rangeManagerMaxLeverageBps: number;
|
|
60
|
+
/**
|
|
61
|
+
* Absolute LOWER bound on `virtual_balance / actual_balance`, same units.
|
|
62
|
+
* `0` ⇒ floor disabled. Pools written before v5.1 read `0`.
|
|
63
|
+
*/
|
|
64
|
+
rangeManagerMinLeverageBps: number;
|
|
65
|
+
|
|
66
|
+
// Per-token (length 10 each; values past `tokenCount` are zeroed).
|
|
67
|
+
tokenMints: PublicKey[];
|
|
68
|
+
tokenPrograms: PublicKey[];
|
|
69
|
+
normalizedWeights: BN[];
|
|
70
|
+
/**
|
|
71
|
+
* Max cumulative sell volume per window, as a percent of the token's
|
|
72
|
+
* virtual balance (`PERCENT_SCALE` units; 10_000 = 100%). `0` disables.
|
|
73
|
+
*/
|
|
74
|
+
maxSelloffPct: number[];
|
|
75
|
+
maxSelloffPeriodLength: number[];
|
|
76
|
+
/** Variable surge-fee curve, per token (`PERCENT_SCALE` units). */
|
|
77
|
+
variableFeeThresholdPct: number[];
|
|
78
|
+
variableFeeSlopeLowPct: number[];
|
|
79
|
+
variableFeeSlopeHighPct: number[];
|
|
80
|
+
/**
|
|
81
|
+
* Surge fee at the KINK — the middle control point of the three-point
|
|
82
|
+
* curve (`PERCENT_SCALE` units). Between `variableFeeThresholdPct` and
|
|
83
|
+
* the kink the fee runs linearly from `variableFeeSlopeLowPct` to this
|
|
84
|
+
* value; beyond it, from this value to `variableFeeSlopeHighPct` at full
|
|
85
|
+
* window fill. Contract enforces `low <= mid <= high`.
|
|
86
|
+
*
|
|
87
|
+
* New in v5.1 (carved out of `AssetConfig.reserved`). Pools written by
|
|
88
|
+
* the older program read `0`.
|
|
89
|
+
*/
|
|
90
|
+
variableFeeSlopeMidPct: number[];
|
|
91
|
+
/**
|
|
92
|
+
* Window fill at which the surge curve kinks, in WHOLE PERCENT (0..=100)
|
|
93
|
+
* — NOT `PERCENT_SCALE` units. `0` ⇒ no kink: the curve degenerates to a
|
|
94
|
+
* single straight line from `low` at the threshold to `high` at full fill,
|
|
95
|
+
* which is exactly how every pre-v5.1 pool decodes.
|
|
96
|
+
*
|
|
97
|
+
* New in v5.1 (carved out of `AssetConfig.reserved`).
|
|
98
|
+
*/
|
|
99
|
+
variableFeeKinkPct: number[];
|
|
100
|
+
/**
|
|
101
|
+
* Per-token input kill switch. `false` ⇒ swaps with this token as INPUT
|
|
102
|
+
* revert (`TokenInactive`). Defaults to `true`.
|
|
103
|
+
*/
|
|
104
|
+
isActive: boolean[];
|
|
105
|
+
|
|
106
|
+
virtualBalances: BN[];
|
|
107
|
+
actualBalances: BN[];
|
|
108
|
+
protocolFeesOwed: BN[];
|
|
109
|
+
previousSelloff: BN[];
|
|
110
|
+
currentSelloff: BN[];
|
|
111
|
+
windowStartTimestamp: BN[];
|
|
112
|
+
/** Virtual-balance snapshot taken when the max-selloff window opened. */
|
|
113
|
+
selloffVbSnapshot: BN[];
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Per-pool Address Lookup Table. `PublicKey.default` means the pool's
|
|
117
|
+
* ALT has not been provisioned yet (`initialize_pool_alt` not called).
|
|
118
|
+
* SDK uses this to choose between v0 (with ALT) and legacy tx-building
|
|
119
|
+
* paths.
|
|
120
|
+
*/
|
|
121
|
+
lookupTable: PublicKey;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Effective Token-2022 banned-extensions bitmap this pool's tokens were
|
|
125
|
+
* vetted against at creation (creator override, or the config default).
|
|
126
|
+
* `0` ⇒ no extensions banned. Pre-upgrade pools read `0`. A permissive
|
|
127
|
+
* value is a rug-risk — surface it before depositing.
|
|
128
|
+
*/
|
|
129
|
+
bannedExtensions: BN;
|
|
130
|
+
/** The exact trailing ABI padding, preserved for complete state inspection. */
|
|
131
|
+
reserved: Buffer;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** 8-byte anchor discriminator for CubicPool. */
|
|
135
|
+
export const POOL_DISCRIMINATOR_LEN = 8;
|
|
136
|
+
const POOL_DISCRIMINATOR = Buffer.from([137, 210, 42, 22, 209, 156, 43, 78]);
|
|
137
|
+
const MAX_TOKENS = 10;
|
|
138
|
+
/**
|
|
139
|
+
* Total on-chain size of a v4/v5 CubicPool (includes the 8-byte
|
|
140
|
+
* discriminator). v5.1 carved its new fields out of `reserved`, so the
|
|
141
|
+
* size is deliberately identical to v4 — size alone cannot tell the two
|
|
142
|
+
* apart. Decoding interprets the current layout; it does not prove an older
|
|
143
|
+
* account has been migrated.
|
|
144
|
+
*/
|
|
145
|
+
export const POOL_V4_LEN = 1683;
|
|
146
|
+
/** Alias for {@link POOL_V4_LEN}; the layout is shared by v4 and v5. */
|
|
147
|
+
export const POOL_LEN = POOL_V4_LEN;
|
|
148
|
+
/** Per-token `TokenSlot` = `AssetConfig` (88) + `AssetDynamics` (56). */
|
|
149
|
+
const ASSET_CONFIG_LEN = 88;
|
|
150
|
+
const ASSET_DYNAMICS_LEN = 56;
|
|
151
|
+
const TOKEN_SLOT_LEN = ASSET_CONFIG_LEN + ASSET_DYNAMICS_LEN;
|
|
152
|
+
/** Pre-v4 size — legacy accounts are unsupported by the current v4-to-v5 migration. */
|
|
153
|
+
export const POOL_V3_LEN = 1154;
|
|
154
|
+
|
|
155
|
+
export function decodePoolAccount(data: Buffer): RawPoolAccount {
|
|
156
|
+
if (data.length === POOL_V3_LEN) {
|
|
157
|
+
throw new Error(
|
|
158
|
+
`decodePoolAccount: account is at v3 size (${POOL_V3_LEN}). ` +
|
|
159
|
+
`Legacy v3 accounts require a separate migration; migrate_to_v5 supports only 1683-byte v4 accounts.`,
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
if (data.length !== POOL_V4_LEN) {
|
|
163
|
+
throw new Error(
|
|
164
|
+
`decodePoolAccount: unexpected data length ${data.length} ` +
|
|
165
|
+
`(expected ${POOL_V4_LEN} for v4).`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
if (!data.subarray(0, POOL_DISCRIMINATOR_LEN).equals(POOL_DISCRIMINATOR)) {
|
|
169
|
+
throw new Error("decodePoolAccount: invalid CubicPool discriminator");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let off = POOL_DISCRIMINATOR_LEN;
|
|
173
|
+
|
|
174
|
+
const config = readPubkey(data, off);
|
|
175
|
+
off += 32;
|
|
176
|
+
const bump = data.readUInt8(off);
|
|
177
|
+
off += 1;
|
|
178
|
+
const tokenCount = data.readUInt8(off);
|
|
179
|
+
off += 1;
|
|
180
|
+
if (tokenCount > MAX_TOKENS) {
|
|
181
|
+
throw new Error(`decodePoolAccount: token_count ${tokenCount} exceeds ${MAX_TOKENS}`);
|
|
182
|
+
}
|
|
183
|
+
const poolId = readU64LE(data, off);
|
|
184
|
+
off += 8;
|
|
185
|
+
const swapFeeRate = data.readUInt32LE(off);
|
|
186
|
+
off += 4;
|
|
187
|
+
const protocolFeeRate = data.readUInt16LE(off);
|
|
188
|
+
off += 2;
|
|
189
|
+
const createdAt = readI64LE(data, off);
|
|
190
|
+
off += 8;
|
|
191
|
+
const poolEnabled = readBool(data, off);
|
|
192
|
+
off += 1;
|
|
193
|
+
const swapsEnabled = readBool(data, off);
|
|
194
|
+
off += 1;
|
|
195
|
+
const poolAdmin = readPubkey(data, off);
|
|
196
|
+
off += 32;
|
|
197
|
+
const pendingPoolAdmin = readPubkey(data, off);
|
|
198
|
+
off += 32;
|
|
199
|
+
|
|
200
|
+
const rangeManager = readPubkey(data, off);
|
|
201
|
+
off += 32;
|
|
202
|
+
const rangeManagerEnabled = readBool(data, off);
|
|
203
|
+
off += 1;
|
|
204
|
+
const rangeManagerMaxVbChangePct = data.readUInt16LE(off);
|
|
205
|
+
off += 2;
|
|
206
|
+
const rangeManagerMaxWeightChangePct = data.readUInt16LE(off);
|
|
207
|
+
off += 2;
|
|
208
|
+
const rangeManagerMinUpdateIntervalSecs = data.readUInt32LE(off);
|
|
209
|
+
off += 4;
|
|
210
|
+
const rangeManagerLastUpdated = readI64LE(data, off);
|
|
211
|
+
off += 8;
|
|
212
|
+
|
|
213
|
+
// Per-token AoS — 10 slots, each 144 bytes.
|
|
214
|
+
const tokenMints: PublicKey[] = [];
|
|
215
|
+
const tokenPrograms: PublicKey[] = [];
|
|
216
|
+
const normalizedWeights: BN[] = [];
|
|
217
|
+
const maxSelloffPct: number[] = [];
|
|
218
|
+
const maxSelloffPeriodLength: number[] = [];
|
|
219
|
+
const variableFeeThresholdPct: number[] = [];
|
|
220
|
+
const variableFeeSlopeLowPct: number[] = [];
|
|
221
|
+
const variableFeeSlopeHighPct: number[] = [];
|
|
222
|
+
const variableFeeSlopeMidPct: number[] = [];
|
|
223
|
+
const variableFeeKinkPct: number[] = [];
|
|
224
|
+
const isActive: boolean[] = [];
|
|
225
|
+
const virtualBalances: BN[] = [];
|
|
226
|
+
const actualBalances: BN[] = [];
|
|
227
|
+
const protocolFeesOwed: BN[] = [];
|
|
228
|
+
const previousSelloff: BN[] = [];
|
|
229
|
+
const currentSelloff: BN[] = [];
|
|
230
|
+
const windowStartTimestamp: BN[] = [];
|
|
231
|
+
const selloffVbSnapshot: BN[] = [];
|
|
232
|
+
|
|
233
|
+
for (let i = 0; i < MAX_TOKENS; i++) {
|
|
234
|
+
const slotStart = off;
|
|
235
|
+
|
|
236
|
+
// AssetConfig — 88 bytes
|
|
237
|
+
tokenMints.push(readPubkey(data, off));
|
|
238
|
+
off += 32;
|
|
239
|
+
tokenPrograms.push(readPubkey(data, off));
|
|
240
|
+
off += 32;
|
|
241
|
+
normalizedWeights.push(readU64LE(data, off));
|
|
242
|
+
off += 8;
|
|
243
|
+
maxSelloffPct.push(data.readUInt16LE(off));
|
|
244
|
+
off += 2;
|
|
245
|
+
maxSelloffPeriodLength.push(data.readUInt32LE(off));
|
|
246
|
+
off += 4;
|
|
247
|
+
variableFeeThresholdPct.push(data.readUInt16LE(off));
|
|
248
|
+
off += 2;
|
|
249
|
+
variableFeeSlopeLowPct.push(data.readUInt16LE(off));
|
|
250
|
+
off += 2;
|
|
251
|
+
variableFeeSlopeHighPct.push(data.readUInt16LE(off));
|
|
252
|
+
off += 2;
|
|
253
|
+
isActive.push(readBool(data, off));
|
|
254
|
+
off += 1;
|
|
255
|
+
// v5.1: the trailing `AssetConfig.reserved[3]` became these two fields.
|
|
256
|
+
// Byte-for-byte replacement — the slot is still 88 bytes and every
|
|
257
|
+
// preceding field keeps its offset, so a v4 account decodes as 0/0.
|
|
258
|
+
variableFeeSlopeMidPct.push(data.readUInt16LE(off));
|
|
259
|
+
off += 2;
|
|
260
|
+
variableFeeKinkPct.push(data.readUInt8(off));
|
|
261
|
+
off += 1;
|
|
262
|
+
|
|
263
|
+
// AssetDynamics — 56 bytes
|
|
264
|
+
virtualBalances.push(readU64LE(data, off));
|
|
265
|
+
off += 8;
|
|
266
|
+
actualBalances.push(readU64LE(data, off));
|
|
267
|
+
off += 8;
|
|
268
|
+
protocolFeesOwed.push(readU64LE(data, off));
|
|
269
|
+
off += 8;
|
|
270
|
+
previousSelloff.push(readU64LE(data, off));
|
|
271
|
+
off += 8;
|
|
272
|
+
currentSelloff.push(readU64LE(data, off));
|
|
273
|
+
off += 8;
|
|
274
|
+
windowStartTimestamp.push(readI64LE(data, off));
|
|
275
|
+
off += 8;
|
|
276
|
+
selloffVbSnapshot.push(readU64LE(data, off));
|
|
277
|
+
off += 8; // AssetDynamics.selloff_vb_snapshot
|
|
278
|
+
|
|
279
|
+
// Fail loudly rather than silently sliding every later field if the
|
|
280
|
+
// slot layout ever drifts again.
|
|
281
|
+
if (off - slotStart !== TOKEN_SLOT_LEN) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`decodePoolAccount: TokenSlot ${i} consumed ${off - slotStart} bytes, expected ${TOKEN_SLOT_LEN}`,
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const lookupTable = readPubkey(data, off);
|
|
289
|
+
off += 32;
|
|
290
|
+
|
|
291
|
+
// Effective per-pool Token-2022 banned-extensions bitmap (carved out of
|
|
292
|
+
// the old reserved[32]; pre-upgrade pools read 0).
|
|
293
|
+
const bannedExtensions = readU64LE(data, off);
|
|
294
|
+
off += 8;
|
|
295
|
+
|
|
296
|
+
// v5.1: two u32s carved out of the trailing reserved blob, which shrank
|
|
297
|
+
// [u8;24] → [u8;16]. Pre-v5.1 pools read 0 (band disabled).
|
|
298
|
+
const rangeManagerMaxLeverageBps = data.readUInt32LE(off);
|
|
299
|
+
off += 4;
|
|
300
|
+
const rangeManagerMinLeverageBps = data.readUInt32LE(off);
|
|
301
|
+
off += 4;
|
|
302
|
+
|
|
303
|
+
// Preserve the trailing ABI padding after checking its exact offset.
|
|
304
|
+
if (off + 16 !== POOL_V4_LEN) {
|
|
305
|
+
throw new Error(
|
|
306
|
+
`decodePoolAccount: consumed ${off} bytes before reserved[16], ` +
|
|
307
|
+
`expected ${POOL_V4_LEN - 16}. Layout drift — regenerate this decoder.`,
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
const reserved = Buffer.from(data.subarray(off, off + 16));
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
config,
|
|
314
|
+
bump,
|
|
315
|
+
tokenCount,
|
|
316
|
+
poolId,
|
|
317
|
+
swapFeeRate,
|
|
318
|
+
protocolFeeRate,
|
|
319
|
+
createdAt,
|
|
320
|
+
poolEnabled,
|
|
321
|
+
swapsEnabled,
|
|
322
|
+
poolAdmin,
|
|
323
|
+
pendingPoolAdmin,
|
|
324
|
+
rangeManager,
|
|
325
|
+
rangeManagerEnabled,
|
|
326
|
+
rangeManagerMaxVbChangePct,
|
|
327
|
+
rangeManagerMaxWeightChangePct,
|
|
328
|
+
rangeManagerMinUpdateIntervalSecs,
|
|
329
|
+
rangeManagerLastUpdated,
|
|
330
|
+
rangeManagerMaxLeverageBps,
|
|
331
|
+
rangeManagerMinLeverageBps,
|
|
332
|
+
tokenMints,
|
|
333
|
+
tokenPrograms,
|
|
334
|
+
normalizedWeights,
|
|
335
|
+
maxSelloffPct,
|
|
336
|
+
maxSelloffPeriodLength,
|
|
337
|
+
variableFeeThresholdPct,
|
|
338
|
+
variableFeeSlopeLowPct,
|
|
339
|
+
variableFeeSlopeHighPct,
|
|
340
|
+
variableFeeSlopeMidPct,
|
|
341
|
+
variableFeeKinkPct,
|
|
342
|
+
isActive,
|
|
343
|
+
virtualBalances,
|
|
344
|
+
actualBalances,
|
|
345
|
+
protocolFeesOwed,
|
|
346
|
+
previousSelloff,
|
|
347
|
+
currentSelloff,
|
|
348
|
+
windowStartTimestamp,
|
|
349
|
+
selloffVbSnapshot,
|
|
350
|
+
lookupTable,
|
|
351
|
+
bannedExtensions,
|
|
352
|
+
reserved,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function readPubkey(data: Buffer, off: number): PublicKey {
|
|
357
|
+
return new PublicKey(data.slice(off, off + 32));
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function readU64LE(data: Buffer, off: number): BN {
|
|
361
|
+
return new BN(data.slice(off, off + 8), "le");
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function readI64LE(data: Buffer, off: number): BN {
|
|
365
|
+
return new BN(data.subarray(off, off + 8), "le").fromTwos(64);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function readBool(data: Buffer, off: number): boolean {
|
|
369
|
+
const value = data.readUInt8(off);
|
|
370
|
+
if (value > 1) throw new Error(`decodePoolAccount: invalid Borsh bool ${value} at offset ${off}`);
|
|
371
|
+
return value === 1;
|
|
372
|
+
}
|