@wireio/stake 0.9.0 → 0.9.2
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/lib/stake.browser.js +341 -32
- package/lib/stake.browser.js.map +1 -1
- package/lib/stake.d.ts +93 -10
- package/lib/stake.js +386 -30
- package/lib/stake.js.map +1 -1
- package/lib/stake.m.js +341 -32
- package/lib/stake.m.js.map +1 -1
- package/package.json +4 -3
- package/src/networks/ethereum/clients/convert.client.ts +29 -1
- package/src/networks/ethereum/clients/receipt.client.ts +81 -3
- package/src/networks/ethereum/clients/stake.client.ts +1 -0
- package/src/networks/ethereum/ethereum.ts +33 -11
- package/src/networks/ethereum/types.ts +15 -0
- package/src/networks/solana/clients/deposit.client.ts +260 -9
- package/src/networks/solana/clients/distribution.client.ts +1 -1
- package/src/networks/solana/clients/outpost.client.ts +1 -1
- package/src/networks/solana/solana.ts +184 -26
- package/src/networks/solana/types.ts +4 -4
- package/src/staker.ts +2 -2
- package/src/types.ts +8 -0
|
@@ -7,17 +7,23 @@ import {
|
|
|
7
7
|
PublicKey as SolPubKey,
|
|
8
8
|
SystemProgram,
|
|
9
9
|
Transaction,
|
|
10
|
+
TransactionInstruction,
|
|
11
|
+
TransactionMessage,
|
|
10
12
|
TransactionSignature,
|
|
11
13
|
} from '@solana/web3.js';
|
|
12
14
|
import { AnchorProvider, BN } from '@coral-xyz/anchor';
|
|
13
15
|
import { BaseSignerWalletAdapter } from '@solana/wallet-adapter-base';
|
|
14
16
|
import {
|
|
15
17
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
18
|
+
createAssociatedTokenAccountInstruction,
|
|
16
19
|
getAssociatedTokenAddressSync,
|
|
17
20
|
TOKEN_2022_PROGRAM_ID,
|
|
18
21
|
} from '@solana/spl-token';
|
|
19
22
|
|
|
23
|
+
import * as multisig from "@sqds/multisig";
|
|
24
|
+
|
|
20
25
|
import {
|
|
26
|
+
Base58,
|
|
21
27
|
ChainID,
|
|
22
28
|
ExternalNetwork,
|
|
23
29
|
KeyType,
|
|
@@ -28,6 +34,7 @@ import {
|
|
|
28
34
|
import {
|
|
29
35
|
IStakingClient,
|
|
30
36
|
Portfolio,
|
|
37
|
+
SquadsXConfig,
|
|
31
38
|
StakerConfig,
|
|
32
39
|
TrancheSnapshot,
|
|
33
40
|
} from '../../types';
|
|
@@ -50,6 +57,7 @@ import {
|
|
|
50
57
|
import { buildSolanaTrancheSnapshot, ceilDiv } from './utils';
|
|
51
58
|
import { GlobalConfig, PayRateEntry, SolanaTransaction } from './types';
|
|
52
59
|
import { SolanaProgramService } from './program';
|
|
60
|
+
import bs58 from 'bs58';
|
|
53
61
|
|
|
54
62
|
const commitment: Commitment = 'confirmed';
|
|
55
63
|
|
|
@@ -78,6 +86,9 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
78
86
|
public tokenClient: TokenClient;
|
|
79
87
|
public program: SolanaProgramService
|
|
80
88
|
|
|
89
|
+
private smartAccount?: SolPubKey; // PDA (off-curve)
|
|
90
|
+
private signer?: SolPubKey; // on-curve signer
|
|
91
|
+
|
|
81
92
|
get solPubKey(): SolPubKey {
|
|
82
93
|
if (!this.pubKey) throw new Error('pubKey is undefined');
|
|
83
94
|
return new SolPubKey(this.pubKey.data.array);
|
|
@@ -87,6 +98,18 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
87
98
|
return this.config.network;
|
|
88
99
|
}
|
|
89
100
|
|
|
101
|
+
get feePayer(): SolPubKey {
|
|
102
|
+
if (this.signer) return this.signer;
|
|
103
|
+
// fallback for normal wallets
|
|
104
|
+
if (this.anchor.wallet.publicKey) return this.anchor.wallet.publicKey;
|
|
105
|
+
throw new Error('No signing authority available');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
get squadsX(): SquadsXConfig | null {
|
|
109
|
+
const config = this.config.extras?.squadsX;
|
|
110
|
+
return config ?? null;
|
|
111
|
+
}
|
|
112
|
+
|
|
90
113
|
constructor(private config: StakerConfig) {
|
|
91
114
|
const adapter = config.provider as BaseSignerWalletAdapter | undefined;
|
|
92
115
|
|
|
@@ -197,6 +220,83 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
197
220
|
// IStakingClient core methods
|
|
198
221
|
// ---------------------------------------------------------------------
|
|
199
222
|
|
|
223
|
+
|
|
224
|
+
async createVaultLiqsolAtaOneShot(params: {
|
|
225
|
+
connection: Connection;
|
|
226
|
+
payer: SolPubKey; // user's wallet pubkey (signer)
|
|
227
|
+
vaultPda: SolPubKey; // squads vault PDA (off-curve owner)
|
|
228
|
+
}): Promise<{ tx: Transaction; vaultAta: SolPubKey } | null> {
|
|
229
|
+
const { connection, payer, vaultPda } = params;
|
|
230
|
+
|
|
231
|
+
const liqsolMint = deriveLiqsolMintPda();
|
|
232
|
+
|
|
233
|
+
const vaultAta = getAssociatedTokenAddressSync(
|
|
234
|
+
liqsolMint,
|
|
235
|
+
vaultPda,
|
|
236
|
+
true, // allowOwnerOffCurve
|
|
237
|
+
TOKEN_2022_PROGRAM_ID,
|
|
238
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
239
|
+
);
|
|
240
|
+
|
|
241
|
+
// If it already exists, just no-op
|
|
242
|
+
const info = await connection.getAccountInfo(vaultAta, "confirmed");
|
|
243
|
+
console.log('info?', info);
|
|
244
|
+
|
|
245
|
+
if (info) return null;
|
|
246
|
+
|
|
247
|
+
const ix = createAssociatedTokenAccountInstruction(
|
|
248
|
+
payer, // payer = user
|
|
249
|
+
vaultAta, // ata address
|
|
250
|
+
vaultPda, // owner = vault
|
|
251
|
+
liqsolMint,
|
|
252
|
+
TOKEN_2022_PROGRAM_ID,
|
|
253
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
const tx = new Transaction().add(ix);
|
|
257
|
+
return { tx, vaultAta };
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
private async prepSquadsIxs(ix: TransactionInstruction): Promise<TransactionInstruction[]> {
|
|
261
|
+
if (!this.squadsX) throw new Error('Attempting to wrap Squads instruction without SquadsX config');
|
|
262
|
+
|
|
263
|
+
const multisigPda = this.squadsMultisigPDA!;
|
|
264
|
+
const vaultPda = this.squadsVaultPDA!;
|
|
265
|
+
const vaultIndex = this.squadsX?.vaultIndex ?? 0;
|
|
266
|
+
const creator = this.solPubKey;
|
|
267
|
+
|
|
268
|
+
// compute next transactionIndex
|
|
269
|
+
const ms = await multisig.accounts.Multisig.fromAccountAddress(this.connection, multisigPda);
|
|
270
|
+
const current = BigInt(ms.transactionIndex?.toString() ?? 0);
|
|
271
|
+
const transactionIndex = current + BigInt(1);
|
|
272
|
+
|
|
273
|
+
// inner message uses vault as payer
|
|
274
|
+
// const cuIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 });
|
|
275
|
+
const { blockhash } = await this.connection.getLatestBlockhash("confirmed");
|
|
276
|
+
const transactionMessage = new TransactionMessage({
|
|
277
|
+
payerKey: vaultPda,
|
|
278
|
+
recentBlockhash: blockhash,
|
|
279
|
+
instructions: [ix],
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
const createVaultTxIx = await multisig.instructions.vaultTransactionCreate({
|
|
283
|
+
multisigPda,
|
|
284
|
+
transactionIndex,
|
|
285
|
+
creator,
|
|
286
|
+
vaultIndex,
|
|
287
|
+
transactionMessage,
|
|
288
|
+
ephemeralSigners: 0,
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const createProposalIx = await multisig.instructions.proposalCreate({
|
|
292
|
+
multisigPda,
|
|
293
|
+
transactionIndex,
|
|
294
|
+
creator,
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
return [createVaultTxIx, createProposalIx];
|
|
298
|
+
}
|
|
299
|
+
|
|
200
300
|
/**
|
|
201
301
|
* Deposit native SOL into liqSOL (liqsol_core::deposit).
|
|
202
302
|
* Handles tx build, sign, send, and confirmation.
|
|
@@ -208,18 +308,51 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
208
308
|
}
|
|
209
309
|
|
|
210
310
|
try {
|
|
211
|
-
// Build compute budget increase instruction
|
|
212
311
|
const cuIx = ComputeBudgetProgram.setComputeUnitLimit({ units: 400_000 });
|
|
312
|
+
// console.log('amountLamports', amountLamports);
|
|
313
|
+
|
|
314
|
+
if (!!this.squadsX) {
|
|
315
|
+
|
|
316
|
+
const createVaultTx = await this.createVaultLiqsolAtaOneShot({
|
|
317
|
+
connection: this.connection,
|
|
318
|
+
payer: this.solPubKey,
|
|
319
|
+
vaultPda: this.squadsVaultPDA!,
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
if (createVaultTx !== null) {
|
|
323
|
+
console.log('need to create vault ata first...');
|
|
324
|
+
const tx0 = new Transaction().add(createVaultTx.tx);
|
|
325
|
+
const prepared0 = await this.prepareTx(tx0);
|
|
326
|
+
const signed0 = await this.signTransaction(prepared0.tx);
|
|
327
|
+
const sent0 = await this.sendAndConfirmHttp(signed0, prepared0);
|
|
328
|
+
console.log('create Vault ATA', sent0);
|
|
329
|
+
}
|
|
213
330
|
|
|
214
|
-
|
|
215
|
-
|
|
331
|
+
const ix = await this.depositClient.buildDepositTx(amountLamports, this.squadsVaultPDA!)
|
|
332
|
+
const squadIxs = await this.prepSquadsIxs(ix)
|
|
216
333
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
334
|
+
const tx1 = new Transaction().add(cuIx, squadIxs[0]);
|
|
335
|
+
const prepared1 = await this.prepareTx(tx1);
|
|
336
|
+
const signed1 = await this.signTransaction(prepared1.tx);
|
|
337
|
+
const sent1 = await this.sendAndConfirmHttp(signed1, prepared1);
|
|
338
|
+
console.log('SENT 1', sent1);
|
|
221
339
|
|
|
222
|
-
|
|
340
|
+
const tx2 = new Transaction().add(cuIx, squadIxs[1]);
|
|
341
|
+
const prepared2 = await this.prepareTx(tx2);
|
|
342
|
+
const signed2 = await this.signTransaction(prepared2.tx);
|
|
343
|
+
const sent2 = await this.sendAndConfirmHttp(signed2, prepared2);
|
|
344
|
+
console.log('SENT 2', sent2);
|
|
345
|
+
|
|
346
|
+
return sent2;
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
const ix = await this.depositClient.buildDepositTx(amountLamports)
|
|
350
|
+
const tx = new Transaction().add(ix);
|
|
351
|
+
const prepared = await this.prepareTx(tx);
|
|
352
|
+
const signed = await this.signTransaction(prepared.tx);
|
|
353
|
+
|
|
354
|
+
return this.sendAndConfirmHttp(signed, prepared);
|
|
355
|
+
}
|
|
223
356
|
} catch (err) {
|
|
224
357
|
throw new Error(`Failed to deposit Solana: ${err}`);
|
|
225
358
|
}
|
|
@@ -361,7 +494,9 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
361
494
|
if (!this.pubKey) throw new Error('User pubKey is undefined');
|
|
362
495
|
|
|
363
496
|
try {
|
|
364
|
-
const user = this.solPubKey;
|
|
497
|
+
const user = !!this.squadsX ? this.squadsVaultPDA! : this.solPubKey;
|
|
498
|
+
|
|
499
|
+
console.log('get portfolio for user', user.toBase58());
|
|
365
500
|
|
|
366
501
|
const reservePoolPDA = deriveReservePoolPda();
|
|
367
502
|
const vaultPDA = deriveVaultPda();
|
|
@@ -370,7 +505,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
370
505
|
const userLiqsolAta = getAssociatedTokenAddressSync(
|
|
371
506
|
liqsolMint,
|
|
372
507
|
user,
|
|
373
|
-
|
|
508
|
+
true, //set to true to allow off curve (e.g. PDA for squadsx wallet)
|
|
374
509
|
TOKEN_2022_PROGRAM_ID,
|
|
375
510
|
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
376
511
|
);
|
|
@@ -510,6 +645,21 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
510
645
|
return this.distributionClient.getUserRecord(this.solPubKey);
|
|
511
646
|
}
|
|
512
647
|
|
|
648
|
+
// ---------------------------------------------------------------------
|
|
649
|
+
// SquadsX Helpers
|
|
650
|
+
// ---------------------------------------------------------------------
|
|
651
|
+
|
|
652
|
+
get squadsMultisigPDA(): SolPubKey | null {
|
|
653
|
+
if (!this.squadsX) return null;
|
|
654
|
+
return new SolPubKey(this.squadsX.multisigPDA);
|
|
655
|
+
}
|
|
656
|
+
get squadsVaultPDA(): SolPubKey | null {
|
|
657
|
+
if (!this.squadsX || !this.squadsMultisigPDA) return null;
|
|
658
|
+
const multisigPda = this.squadsMultisigPDA;
|
|
659
|
+
const index = this.squadsX.vaultIndex ?? 0;
|
|
660
|
+
const pda = multisig.getVaultPda({ multisigPda, index });
|
|
661
|
+
return pda[0];
|
|
662
|
+
}
|
|
513
663
|
|
|
514
664
|
// ---------------------------------------------------------------------
|
|
515
665
|
// READ-ONLY Public Methods
|
|
@@ -690,7 +840,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
690
840
|
return BigInt(0);
|
|
691
841
|
}
|
|
692
842
|
|
|
693
|
-
const [avgPayRate, globalConfig]: [BN, GlobalConfig] = await Promise.all([
|
|
843
|
+
const [avgPayRate, globalConfig]: [BN, GlobalConfig | null] = await Promise.all([
|
|
694
844
|
this.distributionClient.getAverageScaledPayRate(windowSize),
|
|
695
845
|
this.distributionClient.getGlobalConfig(),
|
|
696
846
|
]);
|
|
@@ -741,14 +891,12 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
741
891
|
}): Promise<bigint> {
|
|
742
892
|
this.ensureUser();
|
|
743
893
|
|
|
744
|
-
const payer = this.solPubKey;
|
|
745
|
-
|
|
746
894
|
// -------------------------------------------------------------
|
|
747
895
|
// 1) Current wallet balance (prefer caller override)
|
|
748
896
|
// -------------------------------------------------------------
|
|
749
897
|
const balanceLamports: bigint =
|
|
750
898
|
options?.balanceOverrideLamports ??
|
|
751
|
-
BigInt(await this.connection.getBalance(
|
|
899
|
+
BigInt(await this.connection.getBalance(this.feePayer, commitment));
|
|
752
900
|
|
|
753
901
|
if (balanceLamports <= BigInt(0)) {
|
|
754
902
|
return BigInt(0);
|
|
@@ -859,7 +1007,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
859
1007
|
return this.cachedTxFee.value;
|
|
860
1008
|
}
|
|
861
1009
|
|
|
862
|
-
const payer = this.
|
|
1010
|
+
const payer = this.feePayer;
|
|
863
1011
|
|
|
864
1012
|
const dummyIx = SystemProgram.transfer({
|
|
865
1013
|
fromPubkey: payer,
|
|
@@ -961,7 +1109,7 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
961
1109
|
const { blockhash, lastValidBlockHeight } =
|
|
962
1110
|
await this.connection.getLatestBlockhash('confirmed');
|
|
963
1111
|
tx.recentBlockhash = blockhash;
|
|
964
|
-
tx.feePayer = this.
|
|
1112
|
+
tx.feePayer = this.feePayer;
|
|
965
1113
|
return { tx, blockhash, lastValidBlockHeight };
|
|
966
1114
|
}
|
|
967
1115
|
|
|
@@ -970,17 +1118,27 @@ export class SolanaStakingClient implements IStakingClient {
|
|
|
970
1118
|
* Ensures we have a Wire pubKey and an Anchor wallet pubKey, and that they match.
|
|
971
1119
|
*/
|
|
972
1120
|
ensureUser() {
|
|
973
|
-
if (!this.pubKey
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
) {
|
|
980
|
-
throw new Error(
|
|
981
|
-
'Write access requires connected wallet to match pubKey',
|
|
982
|
-
);
|
|
1121
|
+
if (!this.pubKey) throw new Error('User pubKey is undefined');
|
|
1122
|
+
|
|
1123
|
+
const wallet = this.anchor?.wallet as any;
|
|
1124
|
+
const pk = wallet?.publicKey as SolPubKey | undefined;
|
|
1125
|
+
|
|
1126
|
+
if (!pk) throw new Error('Wallet not connected');
|
|
1127
|
+
if (typeof wallet.signTransaction !== 'function') {
|
|
1128
|
+
throw new Error('Wallet does not support signTransaction');
|
|
983
1129
|
}
|
|
1130
|
+
|
|
1131
|
+
// if (!this.pubKey || !this.anchor.wallet.publicKey) {
|
|
1132
|
+
// throw new Error('User Authorization required: pubKey is undefined');
|
|
1133
|
+
// }
|
|
1134
|
+
// if (
|
|
1135
|
+
// this.solPubKey.toBase58() !==
|
|
1136
|
+
// this.anchor.wallet.publicKey.toBase58()
|
|
1137
|
+
// ) {
|
|
1138
|
+
// throw new Error(
|
|
1139
|
+
// 'Write access requires connected wallet to match pubKey',
|
|
1140
|
+
// );
|
|
1141
|
+
// }
|
|
984
1142
|
}
|
|
985
1143
|
|
|
986
1144
|
}
|
|
@@ -262,7 +262,7 @@ export type GlobalState = {
|
|
|
262
262
|
roleWarmupDuration: BN;
|
|
263
263
|
|
|
264
264
|
/** Wire lifecycle state (preLaunch / postLaunch / refund) */
|
|
265
|
-
wireState: WireState;
|
|
265
|
+
wireState: WireState | any;
|
|
266
266
|
|
|
267
267
|
/** PDA bump */
|
|
268
268
|
bump: number;
|
|
@@ -615,7 +615,7 @@ export type LeaderboardState = {
|
|
|
615
615
|
* Per-validator scores (i64 on-chain).
|
|
616
616
|
* Index corresponds to `registryIndex` in ValidatorRecord.
|
|
617
617
|
*/
|
|
618
|
-
scores:
|
|
618
|
+
scores: number[];
|
|
619
619
|
|
|
620
620
|
/**
|
|
621
621
|
* Sorted indices into `scores[]` / `voteAccounts[]`,
|
|
@@ -628,7 +628,7 @@ export type LeaderboardState = {
|
|
|
628
628
|
* `voteAccounts[i]` is the vote account for validator with
|
|
629
629
|
* `registryIndex == i`.
|
|
630
630
|
*/
|
|
631
|
-
voteAccounts:
|
|
631
|
+
voteAccounts: { bytes: number[]; }[];
|
|
632
632
|
|
|
633
633
|
/**
|
|
634
634
|
* Number of active validators currently tracked in the leaderboard.
|
|
@@ -643,7 +643,7 @@ export type LeaderboardState = {
|
|
|
643
643
|
* Reserved padding / future-proofing on-chain (u64[8]).
|
|
644
644
|
* Not used by client logic, but surfaced for completeness.
|
|
645
645
|
*/
|
|
646
|
-
padding:
|
|
646
|
+
padding: number[];
|
|
647
647
|
};
|
|
648
648
|
|
|
649
649
|
/**
|
package/src/staker.ts
CHANGED
|
@@ -36,12 +36,12 @@ export class Staker {
|
|
|
36
36
|
|
|
37
37
|
config.forEach((cfg) => {
|
|
38
38
|
switch (cfg.network.chainId) {
|
|
39
|
-
case SolChainID.Mainnet:
|
|
39
|
+
// case SolChainID.Mainnet:
|
|
40
40
|
case SolChainID.Devnet:
|
|
41
41
|
this.clients.set(cfg.network.chainId, new SolanaStakingClient(cfg));
|
|
42
42
|
break;
|
|
43
43
|
|
|
44
|
-
case EvmChainID.Ethereum:
|
|
44
|
+
// case EvmChainID.Ethereum:
|
|
45
45
|
case EvmChainID.Hoodi:
|
|
46
46
|
this.clients.set(cfg.network.chainId, new EthereumStakingClient(cfg));
|
|
47
47
|
break;
|
package/src/types.ts
CHANGED
|
@@ -7,8 +7,16 @@ export type StakerConfig = {
|
|
|
7
7
|
network: ExternalNetwork;
|
|
8
8
|
provider?: BaseSignerWalletAdapter | ethers.providers.Web3Provider;
|
|
9
9
|
pubKey?: PublicKey;
|
|
10
|
+
extras?: {
|
|
11
|
+
squadsX?: SquadsXConfig;
|
|
12
|
+
}
|
|
10
13
|
}
|
|
11
14
|
|
|
15
|
+
export type SquadsXConfig = {
|
|
16
|
+
multisigPDA: string; // REQUIRED, base58 multisig address string
|
|
17
|
+
vaultIndex?: number; // default 0
|
|
18
|
+
};
|
|
19
|
+
|
|
12
20
|
export interface IStakingClient {
|
|
13
21
|
pubKey?: PublicKey;
|
|
14
22
|
network: ExternalNetwork;
|