@wireio/stake 0.2.3 → 0.2.4

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.
@@ -1,5 +1,3 @@
1
- // src/networks/solana/utils.ts
2
-
3
1
  import { Program, BN, AnchorProvider } from '@coral-xyz/anchor';
4
2
 
5
3
  import {
@@ -52,7 +50,213 @@ import {
52
50
 
53
51
  import liqsolCoreIDL from '../../assets/solana/idl/liqsol_core.json';
54
52
  import { LiqsolCore } from '../../assets/solana/types/liqsol_core';
55
- import { TrancheState } from './types';
53
+ import { GlobalState, TrancheState } from './types';
54
+ import { TrancheLadderItem, TrancheSnapshot } from '../../types';
55
+ import { ChainID } from '@wireio/core';
56
+
57
+
58
+ // -----------------------------------------------------------------------------
59
+ // Tranche Support
60
+ // -----------------------------------------------------------------------------
61
+
62
+
63
+ const INDEX_SCALE = BigInt(1_000_000_000_000); // 1e12
64
+ const USD_SCALE = BigInt(100_000_000); // 1e8
65
+ const BPS = BigInt(10_000);
66
+
67
+ /** BN | bigint -> bigint helper (keeps code readable) */
68
+ export function toBigint(x: any): bigint {
69
+ if (typeof x === 'bigint') return x;
70
+ if (typeof x === 'number') return BigInt(x);
71
+ // Anchor BN
72
+ return BigInt(x.toString());
73
+ }
74
+
75
+ /**
76
+ * Convert token amount -> shares using the same rule as on-chain tests:
77
+ * shares = ceil(amount * INDEX_SCALE / currentIndex)
78
+ */
79
+ export function tokensToShares(amount: bigint, currentIndex: bigint): bigint {
80
+ if (amount === BigInt(0)) return BigInt(0);
81
+ const num = amount * INDEX_SCALE;
82
+ const q = num / currentIndex;
83
+ const r = num % currentIndex;
84
+ return r === BigInt(0) ? q : q + BigInt(1);
85
+ }
86
+
87
+ /**
88
+ * Apply one forward growth step: value * (BPS + growthBps) / BPS.
89
+ * Simple integer round-half-up.
90
+ */
91
+ function growOnce(value: bigint, growthBps: number): bigint {
92
+ const g = BigInt(growthBps);
93
+ return (value * (BPS + g) + BPS / BigInt(2)) / BPS;
94
+ }
95
+
96
+ /**
97
+ * Apply one backward step: value * BPS / (BPS + growthBps).
98
+ * Also integer round-half-up.
99
+ */
100
+ function shrinkOnce(value: bigint, growthBps: number): bigint {
101
+ const g = BigInt(growthBps);
102
+ return (value * BPS + (BPS + g) / BigInt(2)) / (BPS + g);
103
+ }
104
+
105
+ /**
106
+ * Build a local tranche ladder around the current tranche
107
+ * using only on-chain config + current state.
108
+ *
109
+ * Rules (from liqsol_core tests):
110
+ * - past tranches are fully sold
111
+ * - current tranche has (initial - currentSupply) sold
112
+ * - future tranches start with 0 sold
113
+ * - supply/price grow by supplyGrowthBps/priceGrowthBps per tranche
114
+ */
115
+ export function buildSolanaTrancheLadder(options: {
116
+ currentTranche: number;
117
+ initialTrancheSupply: bigint;
118
+ currentTrancheSupply: bigint;
119
+ totalWarrantsSold: bigint; // informational only
120
+ currentPriceUsd: bigint;
121
+ supplyGrowthBps: number;
122
+ priceGrowthBps: number;
123
+ windowBefore?: number;
124
+ windowAfter?: number;
125
+ }): TrancheLadderItem[] {
126
+ const {
127
+ currentTranche,
128
+ initialTrancheSupply,
129
+ currentTrancheSupply,
130
+ currentPriceUsd,
131
+ supplyGrowthBps,
132
+ priceGrowthBps,
133
+ windowBefore = 5,
134
+ windowAfter = 5,
135
+ } = options;
136
+
137
+ const startId = Math.max(0, currentTranche - windowBefore);
138
+ const endId = currentTranche + windowAfter;
139
+
140
+ const capacity = new Map<number, bigint>();
141
+ const price = new Map<number, bigint>();
142
+
143
+ // Seed current
144
+ capacity.set(currentTranche, initialTrancheSupply);
145
+ price.set(currentTranche, currentPriceUsd);
146
+
147
+ // Forward (future tranches)
148
+ for (let id = currentTranche + 1; id <= endId; id++) {
149
+ const prevCap = capacity.get(id - 1)!;
150
+ const prevPrice = price.get(id - 1)!;
151
+ capacity.set(id, growOnce(prevCap, supplyGrowthBps));
152
+ price.set(id, growOnce(prevPrice, priceGrowthBps));
153
+ }
154
+
155
+ // Backward (past tranches)
156
+ for (let id = currentTranche - 1; id >= startId; id--) {
157
+ const nextCap = capacity.get(id + 1)!;
158
+ const nextPrice = price.get(id + 1)!;
159
+ capacity.set(id, shrinkOnce(nextCap, supplyGrowthBps));
160
+ price.set(id, shrinkOnce(nextPrice, priceGrowthBps));
161
+ }
162
+
163
+ const ladder: TrancheLadderItem[] = [];
164
+ for (let id = startId; id <= endId; id++) {
165
+ const cap = capacity.get(id)!;
166
+ let sold: bigint;
167
+ if (id < currentTranche) {
168
+ sold = cap;
169
+ } else if (id === currentTranche) {
170
+ sold = cap - currentTrancheSupply;
171
+ } else {
172
+ sold = BigInt(0);
173
+ }
174
+
175
+ ladder.push({
176
+ id,
177
+ capacity: cap,
178
+ sold,
179
+ remaining: cap - sold,
180
+ priceUsd: price.get(id)!,
181
+ });
182
+ }
183
+
184
+ return ladder;
185
+ }
186
+
187
+ /**
188
+ * Turn raw liqsol_core accounts into a chain-agnostic TrancheSnapshot for SOL.
189
+ * All math stays here; TokenClient just wires accounts + connection.
190
+ */
191
+ export function buildSolanaTrancheSnapshot(options: {
192
+ chainID: ChainID;
193
+ globalState: GlobalState;
194
+ trancheState: TrancheState;
195
+ solPriceUsd?: bigint;
196
+ nativePriceTimestamp?: number;
197
+ ladderWindowBefore?: number;
198
+ ladderWindowAfter?: number;
199
+ }): TrancheSnapshot {
200
+ const {
201
+ chainID,
202
+ globalState,
203
+ trancheState,
204
+ solPriceUsd,
205
+ nativePriceTimestamp,
206
+ ladderWindowBefore,
207
+ ladderWindowAfter,
208
+ } = options;
209
+
210
+ const currentIndex = toBigint(globalState.currentIndex);
211
+ const totalShares = toBigint(globalState.totalShares);
212
+
213
+ const currentTranche = trancheState.currentTrancheNumber.toNumber();
214
+ const currentTrancheSupply = toBigint(trancheState.currentTrancheSupply);
215
+ const initialTrancheSupply = toBigint(trancheState.initialTrancheSupply);
216
+ const totalWarrantsSold = toBigint(trancheState.totalWarrantsSold);
217
+ const currentPriceUsd = toBigint(trancheState.currentTranchePriceUsd);
218
+
219
+ const supplyGrowthBps = trancheState.supplyGrowthBps;
220
+ const priceGrowthBps = trancheState.priceGrowthBps;
221
+
222
+ const minPriceUsd = trancheState.minPriceUsd
223
+ ? toBigint(trancheState.minPriceUsd)
224
+ : undefined;
225
+ const maxPriceUsd = trancheState.maxPriceUsd
226
+ ? toBigint(trancheState.maxPriceUsd)
227
+ : undefined;
228
+
229
+ const ladder = buildSolanaTrancheLadder({
230
+ currentTranche,
231
+ initialTrancheSupply,
232
+ currentTrancheSupply,
233
+ totalWarrantsSold,
234
+ currentPriceUsd,
235
+ supplyGrowthBps,
236
+ priceGrowthBps,
237
+ windowBefore: ladderWindowBefore,
238
+ windowAfter: ladderWindowAfter,
239
+ });
240
+
241
+ return {
242
+ chainID,
243
+ currentIndex,
244
+ totalShares,
245
+ currentTranche,
246
+ currentPriceUsd,
247
+ minPriceUsd,
248
+ maxPriceUsd,
249
+ supplyGrowthBps,
250
+ priceGrowthBps,
251
+ currentTrancheSupply,
252
+ initialTrancheSupply,
253
+ totalWarrantsSold,
254
+ nativePriceUsd: solPriceUsd,
255
+ nativePriceTimestamp,
256
+ ladder,
257
+ };
258
+ }
259
+
56
260
 
57
261
  // -----------------------------------------------------------------------------
58
262
  // Read-only liqsol_core Program helper
@@ -367,10 +571,10 @@ export async function buildOutpostAccounts(
367
571
  // Chainlink program feeds
368
572
  let chainLinkFeed = CHAINLINK_FEED;
369
573
  let chainLinkProgram = CHAINLINK_PROGRAM
370
-
574
+
371
575
  try {
372
576
  const program = getLiqsolCoreProgram(connection);
373
- const ts : TrancheState = await program.account.trancheState.fetch(trancheState);
577
+ const ts: TrancheState = await program.account.trancheState.fetch(trancheState);
374
578
  if (ts.chainlinkFeed && ts.chainlinkProgram) {
375
579
  chainLinkFeed = ts.chainlinkFeed as PublicKey;
376
580
  chainLinkProgram = ts.chainlinkProgram as PublicKey;
package/src/types.ts CHANGED
@@ -30,7 +30,11 @@ export interface IStakingClient {
30
30
  * - `TrancheSnapshot` when the chain supports pretoken/tranches
31
31
  * - `null` if this chain has no WIRE/pretoken integration
32
32
  */
33
- getTrancheSnapshot(): Promise<TrancheSnapshot | null>;
33
+ getTrancheSnapshot(options?: {
34
+ chainID?: ChainID;
35
+ windowBefore?: number;
36
+ windowAfter?: number;
37
+ }): Promise<TrancheSnapshot | null>;
34
38
 
35
39
  /** */
36
40
  getBuyQuote(amount: bigint, asset: PurchaseAsset): Promise<PurchaseQuote>;
@@ -51,7 +55,7 @@ export interface Portfolio {
51
55
  /** Extra PDAs and account addresses */
52
56
  extras?: Record<string, any>;
53
57
  /** Chain ID of the network for which this portfolio is from */
54
- chainID: ChainID;
58
+ chainID: ChainID;
55
59
  }
56
60
 
57
61
  export type BalanceView = {
@@ -61,45 +65,79 @@ export type BalanceView = {
61
65
  ata?: SolPubKey; // associated token account address
62
66
  };
63
67
 
68
+ export interface TrancheLadderItem {
69
+ /** On-chain tranche id, 0-based (0,1,2,...) */
70
+ id: number;
71
+ /** Total capacity for this tranche (pretokens, 1e8 scale) */
72
+ capacity: bigint;
73
+ /** Sold amount in this tranche (1e8 scale) */
74
+ sold: bigint;
75
+ /** Remaining = capacity - sold (1e8 scale) */
76
+ remaining: bigint;
77
+ /** Price for this tranche in USD (1e8 scale) */
78
+ priceUsd: bigint;
79
+ }
80
+
64
81
  /**
65
- * Program-global prelaunch WIRE / tranche state for a single chain.
66
- *
67
- * All integer values are raw on-chain integers:
68
- * - `currentTranchePriceUsd`: 1e8 USD
69
- * - supplies / warrants / shares: 1e8 WIRE units (per liqsol_core/ETH analog)
70
- * - index: same scale the program uses (INDEX_SCALE = 1e12 on Solana today)
82
+ * Unified pretoken/tranche snapshot for any chain.
83
+ * ETH / other chains just fill the same shape from their own contracts.
71
84
  */
72
85
  export interface TrancheSnapshot {
73
86
  chainID: ChainID;
74
87
 
75
- // From GlobalState
76
- totalShares: bigint; // globalState.totalShares
77
- currentIndex: bigint; // globalState.currentIndex
88
+ /** Global share index (1e12 on Sol today; other chains can use their own scale) */
89
+ currentIndex: bigint;
90
+ /** Total accounting shares (wire pretoken “shares”) */
91
+ totalShares: bigint;
92
+
93
+ /** Current tranche id as stored on chain (0-based) */
94
+ currentTranche: number;
95
+
96
+ /** Current tranche price in USD (1e8 scale) */
97
+ currentPriceUsd: bigint;
98
+ /** Optional min/max bounds for price validation (1e8 scale) */
99
+ minPriceUsd?: bigint;
100
+ maxPriceUsd?: bigint;
101
+
102
+ /** Tranche curve config (per-chain) */
103
+ supplyGrowthBps: number; // e.g. 100 = +1% per tranche
104
+ priceGrowthBps: number; // e.g. 200 = +2% per tranche
78
105
 
79
- // From TrancheState
80
- currentTrancheNumber: bigint; // trancheState.currentTrancheNumber
81
- currentTrancheSupply: bigint; // trancheState.currentTrancheSupply
82
- totalWarrantsSold: bigint; // trancheState.totalWarrantsSold
83
- currentTranchePriceUsd: bigint; // trancheState.currentTranchePriceUsd (1e8)
106
+ /** Current tranche supply state (1e8 scale) */
107
+ currentTrancheSupply: bigint; // remaining in current tranche
108
+ initialTrancheSupply: bigint; // capacity for current tranche
109
+ totalWarrantsSold: bigint; // global cumulative sold (all tranches), 1e8
110
+
111
+ /** Native token → USD price if available (SOL/USD, ETH/USD, etc, 1e8 scale) */
112
+ nativePriceUsd?: bigint;
113
+ /** Optional timestamp (sec) for the last recorded native price */
114
+ nativePriceTimestamp?: number;
115
+
116
+ /**
117
+ * Local window of tranche “rows” centered around current.
118
+ * Used directly by the frontend for ladder graphs.
119
+ */
120
+ ladder: TrancheLadderItem[];
84
121
  }
85
122
 
86
- // Enum describing which asset is being used to buy pretoken
123
+ /** Purchase asset selection used by staking client(s) */
87
124
  export enum PurchaseAsset {
88
- SOL = "SOL",
89
- LIQSOL = "LIQSOL",
90
- ETH = "ETH",
91
- LIQETH = "LIQETH",
92
- YIELD = "YIELD",
125
+ SOL = 'SOL',
126
+ LIQSOL = 'LIQSOL',
127
+ ETH = 'ETH',
128
+ LIQETH = 'LIQETH',
129
+ YIELD = 'YIELD',
93
130
  }
94
131
 
95
132
  export interface PurchaseQuote {
96
133
  purchaseAsset: PurchaseAsset;
97
- amountIn: bigint; // smallest unit (lamports / wei / token units)
98
- // share info (prelaunch WIRE)
99
- wireShares: bigint; // 1e8 precision
100
- wireDecimals: number; // always 8
101
- // pricing info
102
- wirePriceUsd: bigint; // 1e8 precision
103
- notionalUsd: bigint; // 1e8 precision
104
- }
134
+ amountIn: bigint; // lamports / wei / token units
135
+
136
+ /** Expected pretoken “shares” (pretokens) and decimals */
137
+ wireShares: bigint; // 1e8 scale
138
+ wireDecimals: number; // always 8 for now
105
139
 
140
+ /** Current price + notional in USD (1e8 scale) */
141
+ wirePriceUsd: bigint;
142
+ notionalUsd: bigint;
143
+ }