@wireio/stake 0.2.2 → 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,6 +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';
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
+
55
260
 
56
261
  // -----------------------------------------------------------------------------
57
262
  // Read-only liqsol_core Program helper
@@ -68,7 +273,7 @@ export function getLiqsolCoreProgram(
68
273
 
69
274
  // Dummy wallet – we're only doing read-only account fetches here.
70
275
  const tmpKeypair = Keypair.generate();
71
- const wallet : any = { publicKey: tmpKeypair.publicKey, signAllTransactions: async () => [], signTransaction: async () => tmpKeypair };
276
+ const wallet: any = { publicKey: tmpKeypair.publicKey, signAllTransactions: async () => [], signTransaction: async () => tmpKeypair };
72
277
 
73
278
  const provider = new AnchorProvider(connection, wallet, {
74
279
  commitment: 'confirmed',
@@ -364,8 +569,21 @@ export async function buildOutpostAccounts(
364
569
  );
365
570
 
366
571
  // Chainlink program feeds
367
- const chainLinkFeed = CHAINLINK_FEED;
368
- const chainLinkProgram = CHAINLINK_PROGRAM
572
+ let chainLinkFeed = CHAINLINK_FEED;
573
+ let chainLinkProgram = CHAINLINK_PROGRAM
574
+
575
+ try {
576
+ const program = getLiqsolCoreProgram(connection);
577
+ const ts: TrancheState = await program.account.trancheState.fetch(trancheState);
578
+ if (ts.chainlinkFeed && ts.chainlinkProgram) {
579
+ chainLinkFeed = ts.chainlinkFeed as PublicKey;
580
+ chainLinkProgram = ts.chainlinkProgram as PublicKey;
581
+ }
582
+ } catch {
583
+ // If trancheState isn't initialized yet, we fall back to the constants.
584
+ // In that case, pretoken instructions will still fail, which is the correct
585
+ // behavior until admin initializes TrancheState on-chain.
586
+ }
369
587
 
370
588
  void connection; // reserved for future extra validation
371
589
 
@@ -2,8 +2,8 @@
2
2
 
3
3
  import { ChainID, EvmChainID, SolChainID } from '@wireio/core';
4
4
  import { IStakingClient, StakerConfig } from './types';
5
- import { SolanaStakingClient } from '../networks/solana/solana';
6
- import { EthereumStakingClient } from '../networks/ethereum/ethereum';
5
+ import { SolanaStakingClient } from './networks/solana/solana';
6
+ import { EthereumStakingClient } from './networks/ethereum/ethereum';
7
7
 
8
8
  export class Staker {
9
9
  public selectedChainID?: ChainID;
package/src/types.ts ADDED
@@ -0,0 +1,143 @@
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 type StakerConfig = {
7
+ network: ExternalNetwork;
8
+ provider: BaseSignerWalletAdapter | ethers.providers.Web3Provider;
9
+ pubKey: PublicKey;
10
+ }
11
+
12
+ export interface IStakingClient {
13
+ pubKey: PublicKey;
14
+ network: ExternalNetwork;
15
+
16
+ /** Amount is in the chain's smallest unit (lamports/wei, etc.) */
17
+ deposit(amount: bigint): Promise<string>;
18
+ withdraw(amount: bigint): Promise<string>;
19
+ stake(amount: bigint): Promise<string>;
20
+ unstake(amount: bigint): Promise<string>;
21
+ buy(amount: bigint, asset: PurchaseAsset): Promise<string>;
22
+
23
+ /** Fetch the complete user portfolio */
24
+ getPortfolio(): Promise<Portfolio>;
25
+
26
+ /**
27
+ * Program-level prelaunch WIRE/tranche snapshot for this chain.
28
+ *
29
+ * Returns:
30
+ * - `TrancheSnapshot` when the chain supports pretoken/tranches
31
+ * - `null` if this chain has no WIRE/pretoken integration
32
+ */
33
+ getTrancheSnapshot(options?: {
34
+ chainID?: ChainID;
35
+ windowBefore?: number;
36
+ windowAfter?: number;
37
+ }): Promise<TrancheSnapshot | null>;
38
+
39
+ /** */
40
+ getBuyQuote(amount: bigint, asset: PurchaseAsset): Promise<PurchaseQuote>;
41
+ }
42
+
43
+ export interface Portfolio {
44
+ /** Native balance on chain: ETH, SOL */
45
+ native: BalanceView;
46
+ /** Actual Liquid balance of LiqETH, LiqSOL*/
47
+ liq: BalanceView;
48
+ /** Outpost Staked balance */
49
+ staked: BalanceView
50
+ /** Prelaunch WIRE “shares” (warrants/pretokens) */
51
+ wire: BalanceView;
52
+ /** SOL ONLY!
53
+ * Tracked liqSOL balance from distribution program */
54
+ tracked?: BalanceView;
55
+ /** Extra PDAs and account addresses */
56
+ extras?: Record<string, any>;
57
+ /** Chain ID of the network for which this portfolio is from */
58
+ chainID: ChainID;
59
+ }
60
+
61
+ export type BalanceView = {
62
+ amount: bigint; // raw on-chain integer value
63
+ decimals: number; // number of decimal places
64
+ symbol: string; // optional token symbol identifier
65
+ ata?: SolPubKey; // associated token account address
66
+ };
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
+
81
+ /**
82
+ * Unified pretoken/tranche snapshot for any chain.
83
+ * ETH / other chains just fill the same shape from their own contracts.
84
+ */
85
+ export interface TrancheSnapshot {
86
+ chainID: ChainID;
87
+
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
105
+
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[];
121
+ }
122
+
123
+ /** Purchase asset selection used by staking client(s) */
124
+ export enum PurchaseAsset {
125
+ SOL = 'SOL',
126
+ LIQSOL = 'LIQSOL',
127
+ ETH = 'ETH',
128
+ LIQETH = 'LIQETH',
129
+ YIELD = 'YIELD',
130
+ }
131
+
132
+ export interface PurchaseQuote {
133
+ purchaseAsset: PurchaseAsset;
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
139
+
140
+ /** Current price + notional in USD (1e8 scale) */
141
+ wirePriceUsd: bigint;
142
+ notionalUsd: bigint;
143
+ }
@@ -1,51 +0,0 @@
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
- // REMOVED from shared client, SOLANA ONLY
17
- /** Register any untracked LIQ staked tokens */
18
- // register(): Promise<string>;
19
-
20
- /** Fetch the portfolio for the LIQ stake user */
21
- getPortfolio(): Promise<Portfolio>;
22
- }
23
-
24
- export type StakerConfig = {
25
- network: ExternalNetwork;
26
- provider: BaseSignerWalletAdapter | ethers.providers.Web3Provider;
27
- pubKey: PublicKey;
28
- }
29
-
30
- export interface Portfolio {
31
- /** Native balance on chain: ETH, SOL */
32
- native: BalanceView;
33
- /** Actual Liquid balance of LiqETH, LiqSOL*/
34
- liq: BalanceView;
35
- /** Outpost Staked balance */
36
- staked: BalanceView
37
- /** SOL ONLY!
38
- * Tracked liqSOL balance from distribution program */
39
- tracked?: BalanceView;
40
- /** Extra PDAs and account addresses */
41
- extras?: Record<string, any>;
42
- /** Chain ID of the network for which this portfolio is from */
43
- chainID: ChainID;
44
- }
45
-
46
- export type BalanceView = {
47
- amount: bigint; // raw on-chain integer value
48
- decimals: number; // number of decimal places
49
- symbol?: string; // optional token symbol identifier
50
- ata?: SolPubKey; // associated token account address
51
- };