@wireio/stake 0.2.3 → 0.2.5

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;
@@ -0,0 +1,62 @@
1
+ import { BaseSignerWalletAdapter } from '@solana/wallet-adapter-base';
2
+ import { PublicKey as SolPubKey } from '@solana/web3.js';
3
+ import { ChainID, ExternalNetwork, PublicKey } from '@wireio/core';
4
+ import { ethers } from 'ethers';
5
+
6
+ export interface IStakingClient {
7
+ pubKey: PublicKey;
8
+ network: ExternalNetwork;
9
+
10
+ /** Amount is in the chain's smallest unit (lamports/wei, etc.) */
11
+ deposit(amount: bigint): Promise<string>;
12
+ withdraw(amount: bigint): Promise<string>;
13
+ stake(amount: bigint): Promise<string>;
14
+ unstake(amount: bigint): Promise<string>;
15
+
16
+ buy?(amount: bigint, purchaseAsset: PurchaseAsset): Promise<string>;
17
+
18
+ // REMOVED from shared client, SOLANA ONLY
19
+ /** Register any untracked LIQ staked tokens */
20
+ // register(): Promise<string>;
21
+
22
+ /** Fetch the portfolio for the LIQ stake user */
23
+ getPortfolio(): Promise<Portfolio>;
24
+ }
25
+
26
+ // Enum describing which asset is being used to buy pretoken
27
+ export enum PurchaseAsset {
28
+ SOL = "SOL",
29
+ LIQSOL = "LIQSOL",
30
+ ETH = "ETH",
31
+ LIQETH = "LIQETH",
32
+ YIELD = "YIELD",
33
+ }
34
+
35
+ export type StakerConfig = {
36
+ network: ExternalNetwork;
37
+ provider: BaseSignerWalletAdapter | ethers.providers.Web3Provider;
38
+ pubKey: PublicKey;
39
+ }
40
+
41
+ export interface Portfolio {
42
+ /** Native balance on chain: ETH, SOL */
43
+ native: BalanceView;
44
+ /** Actual Liquid balance of LiqETH, LiqSOL*/
45
+ liq: BalanceView;
46
+ /** Outpost Staked balance */
47
+ staked: BalanceView
48
+ /** SOL ONLY!
49
+ * Tracked liqSOL balance from distribution program */
50
+ tracked?: BalanceView;
51
+ /** Extra PDAs and account addresses */
52
+ extras?: Record<string, any>;
53
+ /** Chain ID of the network for which this portfolio is from */
54
+ chainID: ChainID;
55
+ }
56
+
57
+ export type BalanceView = {
58
+ amount: bigint; // raw on-chain integer value
59
+ decimals: number; // number of decimal places
60
+ symbol?: string; // optional token symbol identifier
61
+ ata?: SolPubKey; // associated token account address
62
+ };
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 = {
@@ -60,46 +64,93 @@ export type BalanceView = {
60
64
  symbol: string; // optional token symbol identifier
61
65
  ata?: SolPubKey; // associated token account address
62
66
  };
67
+ export interface TrancheLadderItem {
68
+ /** On-chain tranche id, 0-based (0,1,2,...) */
69
+ id: number;
70
+ /** Total capacity for this tranche (pretokens, 1e8 scale) */
71
+ capacity: bigint;
72
+ /** Sold amount in this tranche (1e8 scale) */
73
+ sold: bigint;
74
+ /** Remaining = capacity - sold (1e8 scale) */
75
+ remaining: bigint;
76
+ /** Price for this tranche in USD (1e8 scale) */
77
+ priceUsd: bigint;
78
+ }
79
+
80
+ export interface TrancheLadderItem {
81
+ /** On-chain tranche id, 0-based (0,1,2,...) */
82
+ id: number;
83
+ /** Total capacity for this tranche (pretokens, 1e8 scale) */
84
+ capacity: bigint;
85
+ /** Sold amount in this tranche (1e8 scale) */
86
+ sold: bigint;
87
+ /** Remaining = capacity - sold (1e8 scale) */
88
+ remaining: bigint;
89
+ /** Price for this tranche in USD (1e8 scale) */
90
+ priceUsd: bigint;
91
+ }
63
92
 
64
93
  /**
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)
94
+ * Unified pretoken/tranche snapshot for any chain.
95
+ * ETH / other chains just fill the same shape from their own contracts.
71
96
  */
72
97
  export interface TrancheSnapshot {
73
98
  chainID: ChainID;
74
99
 
75
- // From GlobalState
76
- totalShares: bigint; // globalState.totalShares
77
- currentIndex: bigint; // globalState.currentIndex
100
+ /** Global share index (1e12 on Sol today; other chains can use their own scale) */
101
+ currentIndex: bigint;
102
+ /** Total accounting shares (wire pretoken “shares”) */
103
+ totalShares: bigint;
104
+
105
+ /** Current tranche id as stored on chain (0-based) */
106
+ currentTranche: number;
107
+
108
+ /** Current tranche price in USD (1e8 scale) */
109
+ currentPriceUsd: bigint;
110
+ /** Optional min/max bounds for price validation (1e8 scale) */
111
+ minPriceUsd?: bigint;
112
+ maxPriceUsd?: bigint;
113
+
114
+ /** Tranche curve config (per-chain) */
115
+ supplyGrowthBps: number; // e.g. 100 = +1% per tranche
116
+ priceGrowthBps: number; // e.g. 200 = +2% per tranche
78
117
 
79
- // From TrancheState
80
- currentTrancheNumber: bigint; // trancheState.currentTrancheNumber
81
- currentTrancheSupply: bigint; // trancheState.currentTrancheSupply
82
- totalWarrantsSold: bigint; // trancheState.totalWarrantsSold
83
- currentTranchePriceUsd: bigint; // trancheState.currentTranchePriceUsd (1e8)
118
+ /** Current tranche supply state (1e8 scale) */
119
+ currentTrancheSupply: bigint; // remaining in current tranche
120
+ initialTrancheSupply: bigint; // capacity for current tranche
121
+ totalWarrantsSold: bigint; // global cumulative sold (all tranches), 1e8
122
+
123
+ /** Native token → USD price if available (SOL/USD, ETH/USD, etc, 1e8 scale) */
124
+ nativePriceUsd?: bigint;
125
+ /** Optional timestamp (sec) for the last recorded native price */
126
+ nativePriceTimestamp?: number;
127
+
128
+ /**
129
+ * Local window of tranche “rows” centered around current.
130
+ * Used directly by the frontend for ladder graphs.
131
+ */
132
+ ladder: TrancheLadderItem[];
84
133
  }
85
134
 
135
+
86
136
  // Enum describing which asset is being used to buy pretoken
87
137
  export enum PurchaseAsset {
88
- SOL = "SOL",
89
- LIQSOL = "LIQSOL",
90
- ETH = "ETH",
91
- LIQETH = "LIQETH",
92
- YIELD = "YIELD",
138
+ SOL = 'SOL',
139
+ LIQSOL = 'LIQSOL',
140
+ ETH = 'ETH',
141
+ LIQETH = 'LIQETH',
142
+ YIELD = 'YIELD',
93
143
  }
94
144
 
95
145
  export interface PurchaseQuote {
96
146
  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
- }
147
+ amountIn: bigint; // lamports / wei / token units
148
+
149
+ /** Expected pretoken “shares” (pretokens) and decimals */
150
+ wireShares: bigint; // 1e8 scale
151
+ wireDecimals: number; // always 8 for now
105
152
 
153
+ /** Current price + notional in USD (1e8 scale) */
154
+ wirePriceUsd: bigint;
155
+ notionalUsd: bigint;
156
+ }