@wireio/stake 0.1.0 → 0.1.1
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/README.md +57 -0
- package/lib/stake.browser.js +4623 -3451
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +372 -537
- package/lib/stake.js +4801 -3574
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +4623 -3451
- package/lib/stake.m.js.map +1 -1
- package/package.json +1 -1
- package/src/assets/solana/idl/liqsol_core.json +4239 -0
- package/src/assets/solana/idl/liqsol_token.json +183 -0
- package/src/assets/solana/idl/validator_leaderboard.json +270 -265
- package/src/assets/solana/types/liqsol_core.ts +4245 -0
- package/src/assets/solana/types/liqsol_token.ts +189 -0
- package/src/assets/solana/types/validator_leaderboard.ts +270 -265
- package/src/index.ts +1 -3
- package/src/networks/ethereum/contract.ts +138 -36
- package/src/networks/ethereum/ethereum.ts +167 -38
- package/src/networks/ethereum/types.ts +32 -1
- package/src/networks/solana/clients/deposit.client.ts +71 -109
- package/src/networks/solana/clients/distribution.client.ts +256 -383
- package/src/networks/solana/clients/leaderboard.client.ts +38 -133
- package/src/networks/solana/constants.ts +214 -130
- package/src/networks/solana/program.ts +25 -38
- package/src/networks/solana/solana.ts +100 -89
- package/src/networks/solana/types.ts +37 -47
- package/src/networks/solana/utils.ts +551 -0
- package/src/scripts/tsconfig.json +17 -0
- package/src/staker/staker.ts +5 -4
- package/src/staker/types.ts +2 -2
- package/src/assets/solana/idl/deposit.json +0 -296
- package/src/assets/solana/idl/distribution.json +0 -768
- package/src/assets/solana/idl/liq_sol_token.json +0 -298
- package/src/assets/solana/idl/mint_helper.json +0 -110
- package/src/assets/solana/idl/read_tracked_balance.json +0 -140
- package/src/assets/solana/idl/stake_controller.json +0 -2149
- package/src/assets/solana/idl/treasury.json +0 -110
- package/src/assets/solana/idl/validator_registry.json +0 -487
- package/src/assets/solana/idl/yield_oracle.json +0 -32
- package/src/assets/solana/types/deposit.ts +0 -302
- package/src/assets/solana/types/distribution.ts +0 -774
- package/src/assets/solana/types/liq_sol_token.ts +0 -304
- package/src/assets/solana/types/mint_helper.ts +0 -116
- package/src/assets/solana/types/read_tracked_balance.ts +0 -146
- package/src/assets/solana/types/stake_controller.ts +0 -2155
- package/src/assets/solana/types/stake_registry.ts +0 -441
- package/src/assets/solana/types/treasury.ts +0 -116
- package/src/assets/solana/types/validator_registry.ts +0 -493
- package/src/assets/solana/types/yield_oracle.ts +0 -38
- package/src/common/utils.ts +0 -9
|
@@ -1,20 +1,27 @@
|
|
|
1
1
|
import {
|
|
2
|
-
Connection,
|
|
3
|
-
PublicKey as SolPubKey,
|
|
4
|
-
TransactionSignature,
|
|
5
2
|
Commitment,
|
|
3
|
+
Connection,
|
|
6
4
|
ConnectionConfig,
|
|
5
|
+
PublicKey as SolPubKey,
|
|
7
6
|
Transaction,
|
|
7
|
+
TransactionSignature,
|
|
8
8
|
} from '@solana/web3.js';
|
|
9
9
|
import { AnchorProvider } from '@coral-xyz/anchor';
|
|
10
10
|
import { BaseSignerWalletAdapter } from '@solana/wallet-adapter-base';
|
|
11
|
-
import {
|
|
11
|
+
import { getAssociatedTokenAddressSync, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token';
|
|
12
|
+
|
|
12
13
|
import { ChainID, ExternalNetwork, KeyType, PublicKey } from '@wireio/core';
|
|
14
|
+
import { IStakingClient, Portfolio, StakerConfig } from '../../staker/types';
|
|
15
|
+
|
|
13
16
|
import { DepositClient } from './clients/deposit.client';
|
|
14
|
-
import { deriveStakeControllerReservePoolPDA, deriveStakeControllerVaultPDA, getUserLiqsolATA } from './constants';
|
|
15
17
|
import { DistributionClient } from './clients/distribution.client';
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
+
import {
|
|
19
|
+
deriveLiqsolMintPda,
|
|
20
|
+
deriveReservePoolPda,
|
|
21
|
+
deriveVaultPda,
|
|
22
|
+
} from './constants';
|
|
23
|
+
import { SolanaTransaction, UserRecord } from './types';
|
|
24
|
+
import { LeaderboardClient } from './clients/leaderboard.client';
|
|
18
25
|
|
|
19
26
|
const commitment: Commitment = 'confirmed';
|
|
20
27
|
|
|
@@ -25,29 +32,32 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
25
32
|
|
|
26
33
|
private depositClient: DepositClient;
|
|
27
34
|
private distributionClient: DistributionClient;
|
|
28
|
-
|
|
35
|
+
private leaderboardClient: LeaderboardClient;
|
|
29
36
|
|
|
30
37
|
get solPubKey(): SolPubKey {
|
|
31
38
|
return new SolPubKey(this.pubKey.data.array);
|
|
32
39
|
}
|
|
33
40
|
|
|
34
|
-
get network() {
|
|
41
|
+
get network() {
|
|
42
|
+
return this.config.network;
|
|
43
|
+
}
|
|
35
44
|
|
|
36
45
|
constructor(private config: StakerConfig) {
|
|
37
|
-
// 1) unwrap & validate wallet adapter
|
|
38
46
|
const adapter = config.provider as BaseSignerWalletAdapter;
|
|
39
|
-
if (!adapter.publicKey)
|
|
40
|
-
|
|
47
|
+
if (!adapter.publicKey) {
|
|
48
|
+
throw new Error('Solana wallet adapter not connected');
|
|
49
|
+
}
|
|
50
|
+
if (!config.network.rpcUrls.length) {
|
|
51
|
+
throw new Error('No RPC URLs provided');
|
|
52
|
+
}
|
|
41
53
|
|
|
42
|
-
// 2) sanity‐check wire ↔ solana pubkey
|
|
43
54
|
const publicKey = adapter.publicKey;
|
|
44
55
|
const wirePub = new PublicKey(KeyType.ED, publicKey.toBytes());
|
|
45
56
|
if (!wirePub.equals(config.pubKey)) {
|
|
46
|
-
throw new Error(
|
|
57
|
+
throw new Error("Passed-in pubKey doesn't match adapter.publicKey");
|
|
47
58
|
}
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
let opts: ConnectionConfig = { commitment }
|
|
60
|
+
const opts: ConnectionConfig = { commitment };
|
|
51
61
|
if (config.network.rpcUrls.length > 1 && config.network.rpcUrls[1].startsWith('ws')) {
|
|
52
62
|
opts.wsEndpoint = config.network.rpcUrls[1];
|
|
53
63
|
}
|
|
@@ -67,10 +77,9 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
67
77
|
|
|
68
78
|
this.anchor = new AnchorProvider(this.connection, anchorWallet, { commitment });
|
|
69
79
|
|
|
70
|
-
// 4) staking clients
|
|
71
80
|
this.depositClient = new DepositClient(this.anchor);
|
|
72
81
|
this.distributionClient = new DistributionClient(this.anchor);
|
|
73
|
-
|
|
82
|
+
this.leaderboardClient = new LeaderboardClient(this.anchor);
|
|
74
83
|
}
|
|
75
84
|
|
|
76
85
|
/**
|
|
@@ -80,67 +89,74 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
80
89
|
* tracked = liqSOL tracked balance (from Distribution.userRecord)
|
|
81
90
|
*/
|
|
82
91
|
async getPortfolio(): Promise<Portfolio> {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
92
|
+
try {
|
|
93
|
+
const user = this.solPubKey;
|
|
94
|
+
const reservePoolPDA = deriveReservePoolPda();
|
|
95
|
+
const vaultPDA = deriveVaultPda();
|
|
96
|
+
const liqsolMint = deriveLiqsolMintPda();
|
|
97
|
+
const userLiqsolAta = getAssociatedTokenAddressSync(
|
|
98
|
+
liqsolMint,
|
|
99
|
+
user,
|
|
100
|
+
false,
|
|
101
|
+
TOKEN_2022_PROGRAM_ID,
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const [nativeLamports, actualBalResp, userRecord] = await Promise.all([
|
|
105
|
+
this.connection.getBalance(user),
|
|
106
|
+
this.connection.getTokenAccountBalance(userLiqsolAta).catch(() => null),
|
|
107
|
+
this.distributionClient.getUserRecord(user).catch(() => null),
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
const actualAmountStr = actualBalResp?.value?.amount ?? '0';
|
|
111
|
+
const actualDecimals = actualBalResp?.value?.decimals ?? 9;
|
|
112
|
+
|
|
113
|
+
const trackedAmountStr = userRecord?.trackedBalance
|
|
114
|
+
? userRecord.trackedBalance.toString()
|
|
115
|
+
: '0';
|
|
116
|
+
const trackedDecimals = actualDecimals;
|
|
117
|
+
|
|
118
|
+
const portfolio: Portfolio = {
|
|
119
|
+
native: {
|
|
120
|
+
symbol: 'SOL',
|
|
121
|
+
decimals: 9,
|
|
122
|
+
amount: BigInt(nativeLamports),
|
|
123
|
+
},
|
|
124
|
+
actual: {
|
|
125
|
+
symbol: 'LiqSOL',
|
|
126
|
+
decimals: actualDecimals,
|
|
127
|
+
amount: BigInt(actualAmountStr),
|
|
128
|
+
},
|
|
129
|
+
tracked: {
|
|
130
|
+
symbol: 'LiqSOL',
|
|
131
|
+
decimals: trackedDecimals,
|
|
132
|
+
amount: BigInt(trackedAmountStr),
|
|
133
|
+
},
|
|
134
|
+
extras: {
|
|
135
|
+
userLiqsolAta: userLiqsolAta.toBase58(),
|
|
136
|
+
reservePoolPDA: reservePoolPDA.toBase58(),
|
|
137
|
+
vaultPDA: vaultPDA.toBase58(),
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return portfolio;
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.log('Error in getPortfolio:', error);
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
133
147
|
}
|
|
148
|
+
|
|
134
149
|
/**
|
|
135
150
|
* Optional: fetch your Distribution program user record
|
|
136
151
|
* (often contains per-user deposit/claim state).
|
|
152
|
+
* @returns UserRecord or null
|
|
137
153
|
*/
|
|
138
|
-
async getUserRecord()
|
|
154
|
+
async getUserRecord() {
|
|
139
155
|
return this.distributionClient.getUserRecord(this.solPubKey);
|
|
140
156
|
}
|
|
141
157
|
|
|
142
158
|
getProtocolFee() {
|
|
143
|
-
|
|
159
|
+
// TODO: wire to pay-rate math once we finalize protocol fee API
|
|
144
160
|
}
|
|
145
161
|
|
|
146
162
|
/**
|
|
@@ -153,27 +169,22 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
153
169
|
*/
|
|
154
170
|
async register(amount?: bigint): Promise<string> {
|
|
155
171
|
try {
|
|
156
|
-
console.log('Building CorrectAndRegister transaction with amount:', amount);
|
|
157
|
-
|
|
158
|
-
// Build the transaction using the Distribution client (self = this.solPubKey)
|
|
159
172
|
const build = await this.distributionClient.buildCorrectRegisterTx({ amount });
|
|
160
|
-
if (!build.canSucceed || !build.transaction)
|
|
173
|
+
if (!build.canSucceed || !build.transaction) {
|
|
161
174
|
throw new Error(build.reason ?? 'Unable to build Correct&Register transaction');
|
|
162
|
-
|
|
163
|
-
console.log('buildCorrectRegisterTx:', build);
|
|
175
|
+
}
|
|
164
176
|
|
|
165
177
|
const { tx, blockhash, lastValidBlockHeight } = await this.prepareTx(build.transaction);
|
|
166
178
|
const signed = await this.signTransaction(tx);
|
|
167
179
|
const result = await this.sendAndConfirmHttp(signed, { blockhash, lastValidBlockHeight });
|
|
168
180
|
|
|
169
|
-
// Optionally refresh portfolio after success (non-blocking)
|
|
170
181
|
console.log('Registered:', {
|
|
171
182
|
needToRegister: build.needToRegister,
|
|
172
183
|
freed: build.plan.willFree,
|
|
173
|
-
corrected: build.plan.selected.map(c => ({
|
|
174
|
-
owner: c.owner
|
|
175
|
-
delta: c.delta
|
|
176
|
-
}))
|
|
184
|
+
corrected: build.plan.selected.map((c) => ({
|
|
185
|
+
owner: c.owner?.toBase58(),
|
|
186
|
+
delta: c.delta?.toString(),
|
|
187
|
+
})),
|
|
177
188
|
});
|
|
178
189
|
|
|
179
190
|
return result.signature;
|
|
@@ -198,19 +209,17 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
198
209
|
|
|
199
210
|
private async sendAndConfirmHttp(
|
|
200
211
|
signed: SolanaTransaction,
|
|
201
|
-
ctx: { blockhash: string; lastValidBlockHeight: number }
|
|
212
|
+
ctx: { blockhash: string; lastValidBlockHeight: number },
|
|
202
213
|
): Promise<TxResult> {
|
|
203
|
-
// sendRawTransaction is HTTP
|
|
204
214
|
const signature = await this.connection.sendRawTransaction(signed.serialize(), {
|
|
205
215
|
skipPreflight: false,
|
|
206
216
|
preflightCommitment: commitment,
|
|
207
217
|
maxRetries: 3,
|
|
208
218
|
});
|
|
209
219
|
|
|
210
|
-
// Poll confirmation using blockhash/lastValidBlockHeight
|
|
211
220
|
const conf = await this.connection.confirmTransaction(
|
|
212
221
|
{ signature, blockhash: ctx.blockhash, lastValidBlockHeight: ctx.lastValidBlockHeight },
|
|
213
|
-
commitment
|
|
222
|
+
commitment,
|
|
214
223
|
);
|
|
215
224
|
|
|
216
225
|
const ok = !conf.value.err;
|
|
@@ -221,14 +230,16 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
221
230
|
}
|
|
222
231
|
|
|
223
232
|
async signTransaction(tx: SolanaTransaction): Promise<SolanaTransaction> {
|
|
224
|
-
return
|
|
233
|
+
return this.anchor.wallet.signTransaction(tx);
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
async sendTransaction(signed: SolanaTransaction): Promise<TransactionSignature> {
|
|
228
|
-
return
|
|
237
|
+
return this.anchor.sendAndConfirm(signed);
|
|
229
238
|
}
|
|
230
239
|
|
|
231
|
-
async prepareTx(
|
|
240
|
+
async prepareTx(
|
|
241
|
+
tx: Transaction,
|
|
242
|
+
): Promise<{ tx: Transaction; blockhash: string; lastValidBlockHeight: number }> {
|
|
232
243
|
const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash('confirmed');
|
|
233
244
|
tx.recentBlockhash = blockhash;
|
|
234
245
|
tx.feePayer = this.solPubKey;
|
|
@@ -241,4 +252,4 @@ export interface TxResult {
|
|
|
241
252
|
signature: string;
|
|
242
253
|
slot: number;
|
|
243
254
|
confirmed: boolean;
|
|
244
|
-
}
|
|
255
|
+
}
|
|
@@ -1,57 +1,47 @@
|
|
|
1
|
-
import { PublicKey, Transaction, VersionedTransaction } from "@solana/web3.js";
|
|
1
|
+
import { PublicKey, StakeActivationData, TokenAmount, Transaction, VersionedTransaction } from "@solana/web3.js";
|
|
2
2
|
|
|
3
3
|
export type SolanaTransaction = Transaction | VersionedTransaction
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
export type MismatchCandidate = {
|
|
7
|
-
/** Wallet that owns the ATA (decoded from token account) */
|
|
8
|
-
owner: PublicKey;
|
|
9
|
-
/** user_record PDA */
|
|
10
|
-
userRecordPda: PublicKey;
|
|
11
|
-
/** user’s ATA for liqSOL */
|
|
5
|
+
export type UserRecord = {
|
|
12
6
|
userAta: PublicKey;
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
trackedBalance: bigint; // What we think they have (for reward calculations)
|
|
8
|
+
claimBalance: bigint; // Accumulated unclaimed rewards
|
|
9
|
+
lastClaimTimestamp: bigint; // When they last claimed (unix timestamp)
|
|
10
|
+
bump: number;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type DistributionState = {
|
|
14
|
+
liqsolMint: PublicKey;
|
|
15
|
+
availableBalance: bigint;
|
|
16
|
+
totalTrackedBalance: bigint;
|
|
17
|
+
bump: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type ParsedAccountInfo = {
|
|
21
|
+
extensions: Array<{ extension: string }>;
|
|
22
|
+
isNative: boolean;
|
|
23
|
+
mint: string;
|
|
24
|
+
owner: string;
|
|
25
|
+
state: string;
|
|
26
|
+
tokenAmount: TokenAmount
|
|
19
27
|
};
|
|
28
|
+
export interface MismatchCandidate {
|
|
29
|
+
owner: PublicKey;
|
|
30
|
+
actual: bigint;
|
|
31
|
+
tracked: bigint;
|
|
32
|
+
delta: bigint; // tracked - actual
|
|
33
|
+
}
|
|
20
34
|
|
|
21
|
-
|
|
22
|
-
export type CorrectionPlan = {
|
|
23
|
-
/** selected candidates sorted by delta desc */
|
|
24
|
-
selected: MismatchCandidate[];
|
|
25
|
-
/** total delta we’ll free by correcting selected */
|
|
26
|
-
willFree: bigint;
|
|
27
|
-
/** how much still missing after selection (0 if we can meet the target) */
|
|
35
|
+
export interface CorrectRegisterPlan {
|
|
28
36
|
deficit: bigint;
|
|
29
|
-
|
|
37
|
+
willFree: bigint;
|
|
38
|
+
selected: MismatchCandidate[];
|
|
39
|
+
}
|
|
30
40
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
/** The ready-to-send transaction (if buildable) */
|
|
34
|
-
transaction?: Transaction;
|
|
35
|
-
/** True if the tx can succeed with current state */
|
|
41
|
+
export interface CorrectRegisterBuildResult {
|
|
42
|
+
needToRegister: boolean;
|
|
36
43
|
canSucceed: boolean;
|
|
37
|
-
/** Explanation if not buildable */
|
|
38
44
|
reason?: string;
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
/** Amount you need to register (actual - tracked if positive) */
|
|
43
|
-
needToRegister: bigint;
|
|
44
|
-
/** Distribution “availableBalance” before this action */
|
|
45
|
-
availableBefore: bigint;
|
|
46
|
-
|
|
47
|
-
/** Candidates we scanned (already sorted by delta desc) */
|
|
48
|
-
candidates: MismatchCandidate[];
|
|
49
|
-
/** Subset we’d correct (maybe empty) */
|
|
50
|
-
plan: CorrectionPlan;
|
|
51
|
-
|
|
52
|
-
/** Convenience for caller */
|
|
53
|
-
accounts: {
|
|
54
|
-
selfUserRecordPda: PublicKey;
|
|
55
|
-
selfUserAta: PublicKey;
|
|
56
|
-
};
|
|
57
|
-
};
|
|
45
|
+
transaction?: Transaction;
|
|
46
|
+
plan: CorrectRegisterPlan;
|
|
47
|
+
}
|