@wireio/stake 0.0.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/LICENSE.md +114 -0
- package/README.md +28 -0
- package/lib/stake.browser.js +6875 -0
- package/lib/stake.browser.js.map +1 -0
- package/lib/stake.d.ts +6759 -0
- package/lib/stake.js +7178 -0
- package/lib/stake.js.map +1 -0
- package/lib/stake.m.js +6875 -0
- package/lib/stake.m.js.map +1 -0
- package/package.json +102 -0
- package/src/assets/ethereum/ABI/token/ERC1155Token.sol/ERC1155Token.dbg.json +4 -0
- package/src/assets/ethereum/ABI/token/ERC1155Token.sol/ERC1155Token.json +472 -0
- package/src/assets/ethereum/ABI/token/ERC20Token.sol/ERC20Token.dbg.json +4 -0
- package/src/assets/ethereum/ABI/token/ERC20Token.sol/ERC20Token.json +330 -0
- package/src/assets/ethereum/ABI/token/ERC721Token.sol/ERC721Token.dbg.json +4 -0
- package/src/assets/ethereum/ABI/token/ERC721Token.sol/ERC721Token.json +449 -0
- package/src/assets/solana/idl/deposit.json +260 -0
- package/src/assets/solana/idl/distribution.json +736 -0
- package/src/assets/solana/idl/liq_sol_token.json +275 -0
- package/src/assets/solana/idl/stake_controller.json +1788 -0
- package/src/assets/solana/idl/stake_registry.json +435 -0
- package/src/assets/solana/idl/treasury.json +336 -0
- package/src/assets/solana/idl/validator_leaderboard.json +528 -0
- package/src/assets/solana/idl/validator_registry.json +418 -0
- package/src/assets/solana/idl/yield_oracle.json +32 -0
- package/src/assets/solana/types/deposit.ts +266 -0
- package/src/assets/solana/types/distribution.ts +742 -0
- package/src/assets/solana/types/liq_sol_token.ts +281 -0
- package/src/assets/solana/types/stake_controller.ts +1794 -0
- package/src/assets/solana/types/stake_registry.ts +441 -0
- package/src/assets/solana/types/treasury.ts +342 -0
- package/src/assets/solana/types/validator_leaderboard.ts +534 -0
- package/src/assets/solana/types/validator_registry.ts +424 -0
- package/src/assets/solana/types/yield_oracle.ts +38 -0
- package/src/index.ts +21 -0
- package/src/networks/ethereum/contract.ts +167 -0
- package/src/networks/ethereum/ethereum.ts +64 -0
- package/src/networks/ethereum/types.ts +6 -0
- package/src/networks/solana/clients/deposit.client.ts +178 -0
- package/src/networks/solana/clients/distribution.client.ts +230 -0
- package/src/networks/solana/clients/leaderboard.client.ts +179 -0
- package/src/networks/solana/constants.ts +73 -0
- package/src/networks/solana/program.ts +113 -0
- package/src/networks/solana/solana.ts +84 -0
- package/src/networks/solana/utils.ts +122 -0
- package/src/staker/staker.ts +38 -0
- package/src/staker/types.ts +26 -0
- package/src/utils.ts +9 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { AnchorProvider } from '@coral-xyz/anchor';
|
|
2
|
+
import { BN } from '@coral-xyz/anchor';
|
|
3
|
+
import {
|
|
4
|
+
PublicKey,
|
|
5
|
+
Transaction,
|
|
6
|
+
SystemProgram,
|
|
7
|
+
SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
8
|
+
SYSVAR_CLOCK_PUBKEY,
|
|
9
|
+
SYSVAR_RENT_PUBKEY,
|
|
10
|
+
SYSVAR_STAKE_HISTORY_PUBKEY,
|
|
11
|
+
StakeProgram,
|
|
12
|
+
ComputeBudgetProgram,
|
|
13
|
+
VersionedTransaction,
|
|
14
|
+
Signer,
|
|
15
|
+
TransactionSignature,
|
|
16
|
+
} from '@solana/web3.js';
|
|
17
|
+
import {
|
|
18
|
+
TOKEN_2022_PROGRAM_ID,
|
|
19
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
20
|
+
} from '@solana/spl-token';
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
deriveDepositAuthorityPDA,
|
|
24
|
+
deriveLiqsolMintAuthorityPDA,
|
|
25
|
+
deriveStakeControllerVaultPDA,
|
|
26
|
+
deriveStakeControllerReservePoolPDA,
|
|
27
|
+
deriveStakeControllerStatePDA,
|
|
28
|
+
deriveDistributionStatePDA,
|
|
29
|
+
deriveUserRecordPDA,
|
|
30
|
+
getUserLiqsolATA,
|
|
31
|
+
} from '../utils';
|
|
32
|
+
import {
|
|
33
|
+
LIQSOL_MINT_ADDRESS,
|
|
34
|
+
TREASURY_WALLET_PDA,
|
|
35
|
+
STAKE_CONTROLLER_PROGRAM_ID,
|
|
36
|
+
LIQSOL_TOKEN_PROGRAM_ID,
|
|
37
|
+
YIELD_ORACLE_PROGRAM_ID,
|
|
38
|
+
DISTRIBUTION_PROGRAM_ID,
|
|
39
|
+
MIN_SOL_TO_PARTICIPATE,
|
|
40
|
+
} from '../constants';
|
|
41
|
+
import { SolanaProgramService } from '../program';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* DepositClient provides methods for building, simulating, and sending deposit transactions
|
|
45
|
+
* to the Solana blockchain using the Anchor framework. It handles the creation of ephemeral
|
|
46
|
+
* stake accounts, derives necessary PDAs and ATAs, and manages transaction construction
|
|
47
|
+
* with compute budget adjustments. This client is designed to facilitate deposits into
|
|
48
|
+
* the Liqsol staking protocol, ensuring all required accounts and instructions are included.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* - Uses AnchorProvider for network interactions.
|
|
52
|
+
* - Enforces minimum deposit requirements.
|
|
53
|
+
* - Supports dry-run simulation for transaction debugging.
|
|
54
|
+
* - Provides high-level helper for building and sending deposit transactions.
|
|
55
|
+
*/
|
|
56
|
+
export class DepositClient {
|
|
57
|
+
private program = new SolanaProgramService(this.provider);
|
|
58
|
+
|
|
59
|
+
constructor(private provider: AnchorProvider) { }
|
|
60
|
+
|
|
61
|
+
/** Minimum lamports (1 SOL) */
|
|
62
|
+
static readonly MIN_DEPOSIT = MIN_SOL_TO_PARTICIPATE;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Build the deposit transaction (compute-budget bump + deposit Ix).
|
|
66
|
+
* Returns both the Transaction and the ephemeral stake account Pubkey.
|
|
67
|
+
*/
|
|
68
|
+
async buildDepositTx(
|
|
69
|
+
user: PublicKey,
|
|
70
|
+
amount: number
|
|
71
|
+
): Promise<{ transaction: Transaction; ephemeralStakePubkey: PublicKey }> {
|
|
72
|
+
if (amount < MIN_SOL_TO_PARTICIPATE) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Minimum deposit is ${MIN_SOL_TO_PARTICIPATE / 1e9} SOL`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 1) grab your typed Anchor program
|
|
79
|
+
const program = this.program.getProgram('deposit');
|
|
80
|
+
|
|
81
|
+
// 2) PDAs & ATAs
|
|
82
|
+
const userAta = getUserLiqsolATA(user);
|
|
83
|
+
const [depositAuthPDA] = deriveDepositAuthorityPDA();
|
|
84
|
+
const [mintAuthPDA] = deriveLiqsolMintAuthorityPDA();
|
|
85
|
+
const [vaultPDA] = deriveStakeControllerVaultPDA();
|
|
86
|
+
const [reservePoolPDA] = deriveStakeControllerReservePoolPDA();
|
|
87
|
+
const [controllerStatePDA] = deriveStakeControllerStatePDA();
|
|
88
|
+
const [distStatePDA] = deriveDistributionStatePDA();
|
|
89
|
+
const [userRecordPDA] = deriveUserRecordPDA(user);
|
|
90
|
+
|
|
91
|
+
// 3) ephemeral stake account
|
|
92
|
+
const seed = Math.floor(Math.random() * 0xffffffff);
|
|
93
|
+
const ephemeralSeed = `ephemeral_${seed}`;
|
|
94
|
+
const ephemeralStakePubkey = await PublicKey.createWithSeed(
|
|
95
|
+
user,
|
|
96
|
+
ephemeralSeed,
|
|
97
|
+
StakeProgram.programId
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
// 4) build deposit instruction
|
|
101
|
+
const depositIx = await program.methods
|
|
102
|
+
.deposit(new BN(amount), seed)
|
|
103
|
+
.accounts({
|
|
104
|
+
user,
|
|
105
|
+
programAuthority: depositAuthPDA,
|
|
106
|
+
treasuryWallet: TREASURY_WALLET_PDA,
|
|
107
|
+
systemProgram: SystemProgram.programId,
|
|
108
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
109
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
110
|
+
stakeControllerProgram: STAKE_CONTROLLER_PROGRAM_ID,
|
|
111
|
+
liqsolProgram: LIQSOL_TOKEN_PROGRAM_ID,
|
|
112
|
+
yieldOracleProgram: YIELD_ORACLE_PROGRAM_ID,
|
|
113
|
+
stakeProgram: StakeProgram.programId,
|
|
114
|
+
distributionProgram: DISTRIBUTION_PROGRAM_ID,
|
|
115
|
+
liqsolMint: LIQSOL_MINT_ADDRESS,
|
|
116
|
+
userAta,
|
|
117
|
+
liqsolMintAuthority: mintAuthPDA,
|
|
118
|
+
reservePool: reservePoolPDA,
|
|
119
|
+
vault: vaultPDA,
|
|
120
|
+
ephemeralStake: ephemeralStakePubkey,
|
|
121
|
+
controllerState: controllerStatePDA,
|
|
122
|
+
userRecord: userRecordPDA,
|
|
123
|
+
distributionState: distStatePDA,
|
|
124
|
+
instructionsSysvar: SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
125
|
+
clock: SYSVAR_CLOCK_PUBKEY,
|
|
126
|
+
stakeHistory: SYSVAR_STAKE_HISTORY_PUBKEY,
|
|
127
|
+
rent: SYSVAR_RENT_PUBKEY,
|
|
128
|
+
} as any)
|
|
129
|
+
.instruction();
|
|
130
|
+
|
|
131
|
+
// 5) prepend compute-budget bump
|
|
132
|
+
const computeIx = ComputeBudgetProgram.setComputeUnitLimit({
|
|
133
|
+
units: 400_000,
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const tx = new Transaction().add(computeIx, depositIx);
|
|
137
|
+
return { transaction: tx, ephemeralStakePubkey };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Simulate (dry-run) a built transaction.
|
|
142
|
+
* Returns any error plus the total compute-units consumed.
|
|
143
|
+
*/
|
|
144
|
+
async simulate(
|
|
145
|
+
tx: Transaction
|
|
146
|
+
): Promise<{ err: any; unitsConsumed: number }> {
|
|
147
|
+
tx.feePayer = this.provider.wallet.publicKey;
|
|
148
|
+
const { blockhash } = await this.provider.connection.getLatestBlockhash();
|
|
149
|
+
tx.recentBlockhash = blockhash;
|
|
150
|
+
|
|
151
|
+
const versioned = new VersionedTransaction(tx.compileMessage());
|
|
152
|
+
const sim = await this.provider.connection.simulateTransaction(
|
|
153
|
+
versioned,
|
|
154
|
+
{ sigVerify: false }
|
|
155
|
+
);
|
|
156
|
+
return { err: sim.value.err, unitsConsumed: sim.value.unitsConsumed! };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* High-level “build & send” helper.
|
|
161
|
+
* Returns the confirmed signature.
|
|
162
|
+
*/
|
|
163
|
+
async deposit(
|
|
164
|
+
user: PublicKey,
|
|
165
|
+
amount: number,
|
|
166
|
+
signers: Signer[] = []
|
|
167
|
+
): Promise<TransactionSignature> {
|
|
168
|
+
const { transaction } = await this.buildDepositTx(user, amount);
|
|
169
|
+
|
|
170
|
+
// set feePayer & recent blockhash
|
|
171
|
+
transaction.feePayer = this.provider.wallet.publicKey;
|
|
172
|
+
const { blockhash } = await this.provider.connection.getLatestBlockhash();
|
|
173
|
+
transaction.recentBlockhash = blockhash;
|
|
174
|
+
|
|
175
|
+
// send + confirm
|
|
176
|
+
return this.provider.sendAndConfirm(transaction, signers);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
// src/solana/clients/DistributionClient.ts
|
|
2
|
+
import { AnchorProvider, Program, BN, web3 } from '@coral-xyz/anchor';
|
|
3
|
+
import {
|
|
4
|
+
PublicKey,
|
|
5
|
+
Transaction,
|
|
6
|
+
TransactionInstruction,
|
|
7
|
+
SystemProgram,
|
|
8
|
+
VersionedTransaction,
|
|
9
|
+
} from '@solana/web3.js';
|
|
10
|
+
import {
|
|
11
|
+
TOKEN_2022_PROGRAM_ID,
|
|
12
|
+
ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
13
|
+
getAssociatedTokenAddressSync,
|
|
14
|
+
} from '@solana/spl-token';
|
|
15
|
+
import {
|
|
16
|
+
DISTRIBUTION_PROGRAM_ID,
|
|
17
|
+
STAKE_CONTROLLER_PROGRAM_ID,
|
|
18
|
+
DistributionIDL,
|
|
19
|
+
Distribution,
|
|
20
|
+
YIELD_ORACLE_PROGRAM_ID,
|
|
21
|
+
LIQSOL_TOKEN_PROGRAM_ID,
|
|
22
|
+
} from '../constants';
|
|
23
|
+
import {
|
|
24
|
+
deriveDistributionStatePDA,
|
|
25
|
+
deriveUserRecordPDA,
|
|
26
|
+
deriveLiqsolMintAuthorityPDA,
|
|
27
|
+
} from '../utils';
|
|
28
|
+
|
|
29
|
+
export class DistributionClient {
|
|
30
|
+
constructor(private provider: AnchorProvider) { }
|
|
31
|
+
|
|
32
|
+
/** Wrapped Anchor Program for Distribution */
|
|
33
|
+
private get program(): Program<Distribution> {
|
|
34
|
+
const idlWithAddress = {
|
|
35
|
+
...JSON.parse(JSON.stringify(DistributionIDL)),
|
|
36
|
+
address: DISTRIBUTION_PROGRAM_ID.toString(),
|
|
37
|
+
};
|
|
38
|
+
return new Program(idlWithAddress as any, this.provider) as Program<Distribution>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Derive the PDA for global distribution state */
|
|
42
|
+
deriveStatePDA(): PublicKey {
|
|
43
|
+
return deriveDistributionStatePDA()[0];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Derive the PDA for a user’s record */
|
|
47
|
+
deriveUserRecordPDA(user: PublicKey): PublicKey {
|
|
48
|
+
return deriveUserRecordPDA(user)[0];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Fetch on-chain distribution state */
|
|
52
|
+
async getState(): Promise<any> {
|
|
53
|
+
return this.program.account.distributionState.fetch(this.deriveStatePDA());
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Fetch a user’s record or return null if it doesn’t exist */
|
|
57
|
+
async getUserRecord(user: PublicKey): Promise<any | null> {
|
|
58
|
+
try {
|
|
59
|
+
return await this.program.account.userRecord.fetch(
|
|
60
|
+
this.deriveUserRecordPDA(user)
|
|
61
|
+
);
|
|
62
|
+
} catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Build an `initialize` transaction for the distribution program */
|
|
68
|
+
async buildInitializeTransaction(user: PublicKey): Promise<Transaction> {
|
|
69
|
+
const [statePda] = deriveDistributionStatePDA();
|
|
70
|
+
const ix = await this.program.methods
|
|
71
|
+
.initialize()
|
|
72
|
+
.accounts({
|
|
73
|
+
authority: user,
|
|
74
|
+
distributionState: statePda,
|
|
75
|
+
systemProgram: SystemProgram.programId,
|
|
76
|
+
rent: web3.SYSVAR_RENT_PUBKEY,
|
|
77
|
+
} as any)
|
|
78
|
+
.instruction();
|
|
79
|
+
|
|
80
|
+
return new Transaction().add(ix);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Build an `updateUser` transaction (register or refresh a user record) */
|
|
84
|
+
async buildUpdateUserTransaction(user: PublicKey): Promise<Transaction> {
|
|
85
|
+
const statePda = this.deriveStatePDA();
|
|
86
|
+
const userPda = this.deriveUserRecordPDA(user);
|
|
87
|
+
|
|
88
|
+
// Fetch state to get the liqSOL mint address
|
|
89
|
+
const state: any = await this.getState();
|
|
90
|
+
const liqsolMint: PublicKey = state.liqsolMint;
|
|
91
|
+
const userAta = getAssociatedTokenAddressSync(
|
|
92
|
+
liqsolMint,
|
|
93
|
+
user,
|
|
94
|
+
false,
|
|
95
|
+
TOKEN_2022_PROGRAM_ID,
|
|
96
|
+
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
const [stakeControllerStatePda] = PublicKey.findProgramAddressSync(
|
|
100
|
+
[Buffer.from('stake_controller')],
|
|
101
|
+
STAKE_CONTROLLER_PROGRAM_ID
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const ix = await this.program.methods
|
|
105
|
+
.updateUser()
|
|
106
|
+
.accounts({
|
|
107
|
+
user,
|
|
108
|
+
userAta,
|
|
109
|
+
userRecord: userPda,
|
|
110
|
+
authority: user,
|
|
111
|
+
payer: user,
|
|
112
|
+
distributionState: statePda,
|
|
113
|
+
stakeControllerState: stakeControllerStatePda,
|
|
114
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
115
|
+
yieldOracleProgram: YIELD_ORACLE_PROGRAM_ID,
|
|
116
|
+
systemProgram: SystemProgram.programId,
|
|
117
|
+
} as any)
|
|
118
|
+
.instruction();
|
|
119
|
+
|
|
120
|
+
return new Transaction().add(ix);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Build a `withdraw` transaction */
|
|
124
|
+
async buildWithdrawTransaction(user: PublicKey, amount: number): Promise<Transaction> {
|
|
125
|
+
const statePda = this.deriveStatePDA();
|
|
126
|
+
const userPda = this.deriveUserRecordPDA(user);
|
|
127
|
+
|
|
128
|
+
// Fetch state for the mint
|
|
129
|
+
const state: any = await this.getState();
|
|
130
|
+
const liqsolMint: PublicKey = state.liqsolMint;
|
|
131
|
+
const userAta = getAssociatedTokenAddressSync(
|
|
132
|
+
liqsolMint,
|
|
133
|
+
user,
|
|
134
|
+
false,
|
|
135
|
+
TOKEN_2022_PROGRAM_ID,
|
|
136
|
+
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
const [stakeControllerStatePda] = PublicKey.findProgramAddressSync(
|
|
140
|
+
[Buffer.from('stake_controller')],
|
|
141
|
+
STAKE_CONTROLLER_PROGRAM_ID
|
|
142
|
+
);
|
|
143
|
+
const [controllerAuthPda] = PublicKey.findProgramAddressSync(
|
|
144
|
+
[Buffer.from('stake_authority')],
|
|
145
|
+
STAKE_CONTROLLER_PROGRAM_ID
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
const ix = await this.program.methods
|
|
149
|
+
.withdraw(new BN(amount))
|
|
150
|
+
.accounts({
|
|
151
|
+
user,
|
|
152
|
+
userAta,
|
|
153
|
+
userRecord: userPda,
|
|
154
|
+
distributionState: statePda,
|
|
155
|
+
liqsolMint,
|
|
156
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
157
|
+
stakeControllerProgram: STAKE_CONTROLLER_PROGRAM_ID,
|
|
158
|
+
stakeControllerState: stakeControllerStatePda,
|
|
159
|
+
controllerAuthority: controllerAuthPda,
|
|
160
|
+
yieldOracleProgram: YIELD_ORACLE_PROGRAM_ID,
|
|
161
|
+
systemProgram: SystemProgram.programId,
|
|
162
|
+
} as any)
|
|
163
|
+
.instruction();
|
|
164
|
+
|
|
165
|
+
return new Transaction().add(ix);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Build a `claimRewards` transaction */
|
|
169
|
+
async buildClaimRewardsTransaction(user: PublicKey): Promise<Transaction> {
|
|
170
|
+
const statePda = this.deriveStatePDA();
|
|
171
|
+
const userPda = this.deriveUserRecordPDA(user);
|
|
172
|
+
|
|
173
|
+
// Fetch state for the mint
|
|
174
|
+
const state: any = await this.getState();
|
|
175
|
+
const liqsolMint: PublicKey = state.liqsolMint;
|
|
176
|
+
const userAta = getAssociatedTokenAddressSync(
|
|
177
|
+
liqsolMint,
|
|
178
|
+
user,
|
|
179
|
+
false,
|
|
180
|
+
TOKEN_2022_PROGRAM_ID,
|
|
181
|
+
ASSOCIATED_TOKEN_PROGRAM_ID
|
|
182
|
+
);
|
|
183
|
+
const [mintAuthPda] = deriveLiqsolMintAuthorityPDA();
|
|
184
|
+
|
|
185
|
+
const ix = await this.program.methods
|
|
186
|
+
.claimRewards()
|
|
187
|
+
.accounts({
|
|
188
|
+
user,
|
|
189
|
+
userAta,
|
|
190
|
+
userRecord: userPda,
|
|
191
|
+
distributionState: statePda,
|
|
192
|
+
liqsolMint,
|
|
193
|
+
liqsolProgram: LIQSOL_TOKEN_PROGRAM_ID,
|
|
194
|
+
liqsolMintAuthority: mintAuthPda,
|
|
195
|
+
instructionsSysvar: web3.SYSVAR_INSTRUCTIONS_PUBKEY,
|
|
196
|
+
yieldOracleProgram: YIELD_ORACLE_PROGRAM_ID,
|
|
197
|
+
tokenProgram: TOKEN_2022_PROGRAM_ID,
|
|
198
|
+
associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID,
|
|
199
|
+
systemProgram: SystemProgram.programId,
|
|
200
|
+
} as any)
|
|
201
|
+
.instruction();
|
|
202
|
+
|
|
203
|
+
return new Transaction().add(ix);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Send & confirm a single-IX transaction; returns the tx signature */
|
|
207
|
+
async sendTransaction(
|
|
208
|
+
tx: Transaction,
|
|
209
|
+
signers: import('@solana/web3.js').Signer[] = []
|
|
210
|
+
): Promise<string> {
|
|
211
|
+
tx.feePayer = this.provider.wallet.publicKey;
|
|
212
|
+
const { blockhash } = await this.provider.connection.getLatestBlockhash();
|
|
213
|
+
tx.recentBlockhash = blockhash;
|
|
214
|
+
return this.provider.sendAndConfirm(tx, signers);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Simulate a single-IX transaction; returns error (if any) + compute units used */
|
|
218
|
+
async simulateTransaction(
|
|
219
|
+
tx: Transaction
|
|
220
|
+
): Promise<{ err: any; unitsConsumed: number }> {
|
|
221
|
+
tx.feePayer = this.provider.wallet.publicKey;
|
|
222
|
+
const { blockhash } = await this.provider.connection.getLatestBlockhash();
|
|
223
|
+
tx.recentBlockhash = blockhash;
|
|
224
|
+
const versioned = new VersionedTransaction(tx.compileMessage());
|
|
225
|
+
const sim = await this.provider.connection.simulateTransaction(versioned, {
|
|
226
|
+
sigVerify: false,
|
|
227
|
+
});
|
|
228
|
+
return { err: sim.value.err, unitsConsumed: sim.value.unitsConsumed! };
|
|
229
|
+
}
|
|
230
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// src/solana/clients/ValidatorLeaderboardClient.ts
|
|
2
|
+
import { AnchorProvider, Program, BN } from '@coral-xyz/anchor';
|
|
3
|
+
import { PublicKey, Transaction, TransactionInstruction, SystemProgram, VersionedTransaction } from '@solana/web3.js';
|
|
4
|
+
import {
|
|
5
|
+
deriveLeaderboardHeadPDA,
|
|
6
|
+
deriveValidatorRecordPDA,
|
|
7
|
+
deriveTop10CachePDA,
|
|
8
|
+
} from '../utils';
|
|
9
|
+
import { VALIDATOR_LEADERBOARD_PROGRAM_ID, ValidatorLeaderboard, ValidatorLeaderboardIDL } from '../constants';
|
|
10
|
+
|
|
11
|
+
export class ValidatorLeaderboardClient {
|
|
12
|
+
constructor(private provider: AnchorProvider) { }
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Wrapped Anchor Program for ValidatorLeaderboard
|
|
16
|
+
*/
|
|
17
|
+
private get program(): Program<ValidatorLeaderboard> {
|
|
18
|
+
const idlWithAddress = {
|
|
19
|
+
...JSON.parse(JSON.stringify(ValidatorLeaderboardIDL)),
|
|
20
|
+
address: VALIDATOR_LEADERBOARD_PROGRAM_ID.toString(),
|
|
21
|
+
};
|
|
22
|
+
return new Program(idlWithAddress as any, this.provider) as Program<ValidatorLeaderboard>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Derive the PDA for the leaderboard head */
|
|
26
|
+
deriveHeadPDA(): PublicKey {
|
|
27
|
+
return deriveLeaderboardHeadPDA()[0];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Derive the PDA for a given validator record */
|
|
31
|
+
deriveRecordPDA(voteAccount: PublicKey): PublicKey {
|
|
32
|
+
return deriveValidatorRecordPDA(voteAccount)[0];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Build the initialize instruction as a Transaction
|
|
37
|
+
*/
|
|
38
|
+
async buildInitializeTransaction(authority: PublicKey): Promise<Transaction> {
|
|
39
|
+
const [headPda] = deriveLeaderboardHeadPDA();
|
|
40
|
+
const ix = await this.program.methods
|
|
41
|
+
.initialize()
|
|
42
|
+
.accounts({
|
|
43
|
+
authority,
|
|
44
|
+
leaderboardHead: headPda,
|
|
45
|
+
systemProgram: SystemProgram.programId,
|
|
46
|
+
} as any)
|
|
47
|
+
.instruction();
|
|
48
|
+
|
|
49
|
+
return new Transaction().add(ix);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Build an updateValidator transaction
|
|
54
|
+
*/
|
|
55
|
+
async buildUpdateValidatorTransaction(params: {
|
|
56
|
+
authority: PublicKey;
|
|
57
|
+
registrant: PublicKey;
|
|
58
|
+
voteAccount: PublicKey;
|
|
59
|
+
vpp: number;
|
|
60
|
+
insertAfter?: PublicKey;
|
|
61
|
+
insertBefore?: PublicKey;
|
|
62
|
+
currentPrev?: PublicKey;
|
|
63
|
+
currentNext?: PublicKey;
|
|
64
|
+
}): Promise<Transaction> {
|
|
65
|
+
const { authority, registrant, voteAccount, vpp, insertAfter, insertBefore, currentPrev, currentNext } = params;
|
|
66
|
+
const [headPda] = deriveLeaderboardHeadPDA();
|
|
67
|
+
const [recPda] = deriveValidatorRecordPDA(voteAccount);
|
|
68
|
+
|
|
69
|
+
const accounts: any = {
|
|
70
|
+
registrant,
|
|
71
|
+
voteAccount,
|
|
72
|
+
validatorRecord: recPda,
|
|
73
|
+
leaderboardHead: headPda,
|
|
74
|
+
systemProgram: SystemProgram.programId,
|
|
75
|
+
...(insertAfter && { insertAfter }),
|
|
76
|
+
...(insertBefore && { insertBefore }),
|
|
77
|
+
...(currentPrev && { currentPrev }),
|
|
78
|
+
...(currentNext && { currentNext }),
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const ix = await this.program.methods
|
|
82
|
+
.updateValidator(new BN(vpp))
|
|
83
|
+
.accounts(accounts)
|
|
84
|
+
.instruction();
|
|
85
|
+
|
|
86
|
+
return new Transaction().add(ix);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Build an updateTop10Cache transaction
|
|
91
|
+
*/
|
|
92
|
+
async buildUpdateTop10CacheTransaction(params: {
|
|
93
|
+
authority: PublicKey;
|
|
94
|
+
top10Validators: PublicKey[];
|
|
95
|
+
}): Promise<Transaction> {
|
|
96
|
+
const { authority, top10Validators } = params;
|
|
97
|
+
if (top10Validators.length !== 10) {
|
|
98
|
+
throw new Error('Must supply exactly 10 validators');
|
|
99
|
+
}
|
|
100
|
+
const [cachePda] = deriveTop10CachePDA();
|
|
101
|
+
const ix = await this.program.methods
|
|
102
|
+
.updateTop10Cache(top10Validators)
|
|
103
|
+
.accounts({ authority, cache: cachePda, systemProgram: SystemProgram.programId } as any)
|
|
104
|
+
.instruction();
|
|
105
|
+
|
|
106
|
+
return new Transaction().add(ix);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Fetch the cached top 10 validators via a view call
|
|
111
|
+
*/
|
|
112
|
+
async getCachedTop10(): Promise<PublicKey[]> {
|
|
113
|
+
const [cachePda] = deriveTop10CachePDA();
|
|
114
|
+
return this.program.methods
|
|
115
|
+
.getCachedTop10()
|
|
116
|
+
.accounts({ cache: cachePda } as any)
|
|
117
|
+
.view();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Traverse the on-chain linked list and return the sequence of PublicKeys
|
|
122
|
+
*/
|
|
123
|
+
async getLeaderboard(): Promise<PublicKey[]> {
|
|
124
|
+
const [headPda] = deriveLeaderboardHeadPDA();
|
|
125
|
+
let headAccount;
|
|
126
|
+
try {
|
|
127
|
+
headAccount = await this.program.account.leaderboardHead.fetch(headPda);
|
|
128
|
+
} catch {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
const result: PublicKey[] = [];
|
|
132
|
+
let cursor: PublicKey = headAccount.nextValidator;
|
|
133
|
+
while (cursor && !cursor.equals(PublicKey.default)) {
|
|
134
|
+
result.push(cursor);
|
|
135
|
+
const rec = await this.program.account.validatorRecord.fetch(cursor);
|
|
136
|
+
cursor = rec.nextValidator;
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Fetch a single validator record or null
|
|
143
|
+
*/
|
|
144
|
+
async getRecord(voteAccount: PublicKey): Promise<any | null> {
|
|
145
|
+
const [recPda] = deriveValidatorRecordPDA(voteAccount);
|
|
146
|
+
try {
|
|
147
|
+
return await this.program.account.validatorRecord.fetch(recPda);
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Send & confirm a single-IX Transaction; returns the signature
|
|
155
|
+
*/
|
|
156
|
+
async sendTransaction(
|
|
157
|
+
tx: Transaction,
|
|
158
|
+
signers: import('@solana/web3.js').Signer[] = []
|
|
159
|
+
): Promise<string> {
|
|
160
|
+
tx.feePayer = this.provider.wallet.publicKey;
|
|
161
|
+
const { blockhash } = await this.provider.connection.getLatestBlockhash();
|
|
162
|
+
tx.recentBlockhash = blockhash;
|
|
163
|
+
return this.provider.sendAndConfirm(tx, signers);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Simulate a single-IX Transaction; returns any err + compute units consumed
|
|
168
|
+
*/
|
|
169
|
+
async simulateTransaction(
|
|
170
|
+
tx: Transaction
|
|
171
|
+
): Promise<{ err: any; unitsConsumed: number }> {
|
|
172
|
+
tx.feePayer = this.provider.wallet.publicKey;
|
|
173
|
+
const { blockhash } = await this.provider.connection.getLatestBlockhash();
|
|
174
|
+
tx.recentBlockhash = blockhash;
|
|
175
|
+
const versioned = new VersionedTransaction(tx.compileMessage());
|
|
176
|
+
const sim = await this.provider.connection.simulateTransaction(versioned, { sigVerify: false });
|
|
177
|
+
return { err: sim.value.err, unitsConsumed: sim.value.unitsConsumed! };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/networks/solana/constants.ts
|
|
2
|
+
|
|
3
|
+
import { PublicKey } from '@solana/web3.js';
|
|
4
|
+
import { TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID } from '@solana/spl-token';
|
|
5
|
+
|
|
6
|
+
// — IDLs — (must match your filesystem: src/assets/solana/idl/*.json)
|
|
7
|
+
import DepositIDL from '../../assets/solana/idl/deposit.json';
|
|
8
|
+
import DistributionIDL from '../../assets/solana/idl/distribution.json';
|
|
9
|
+
import LiqSolTokenIDL from '../../assets/solana/idl/liq_sol_token.json';
|
|
10
|
+
import StakeControllerIDL from '../../assets/solana/idl/stake_controller.json';
|
|
11
|
+
import StakeRegistryIDL from '../../assets/solana/idl/stake_registry.json';
|
|
12
|
+
import TreasuryIDL from '../../assets/solana/idl/treasury.json';
|
|
13
|
+
import ValidatorLeaderboardIDL from '../../assets/solana/idl/validator_leaderboard.json';
|
|
14
|
+
import ValidatorRegistryIDL from '../../assets/solana/idl/validator_registry.json';
|
|
15
|
+
import YieldOracleIDL from '../../assets/solana/idl/yield_oracle.json';
|
|
16
|
+
|
|
17
|
+
// — Types — for your clients
|
|
18
|
+
import type { Deposit } from '../../assets/solana/types/deposit';
|
|
19
|
+
import type { Distribution } from '../../assets/solana/types/distribution';
|
|
20
|
+
import type { ValidatorLeaderboard } from '../../assets/solana/types/validator_leaderboard';
|
|
21
|
+
import type { LiqSolToken } from '../../assets/solana/types/liq_sol_token';
|
|
22
|
+
import type { StakeController } from '../../assets/solana/types/stake_controller';
|
|
23
|
+
import type { StakeRegistry } from '../../assets/solana/types/stake_registry';
|
|
24
|
+
import type { Treasury } from '../../assets/solana/types/treasury';
|
|
25
|
+
import type { ValidatorRegistry } from '../../assets/solana/types/validator_registry';
|
|
26
|
+
import type { YieldOracle } from '../../assets/solana/types/yield_oracle';
|
|
27
|
+
|
|
28
|
+
// Re-export for easy import elsewhere:
|
|
29
|
+
export {
|
|
30
|
+
Deposit,
|
|
31
|
+
DepositIDL,
|
|
32
|
+
Distribution,
|
|
33
|
+
DistributionIDL,
|
|
34
|
+
LiqSolToken,
|
|
35
|
+
LiqSolTokenIDL,
|
|
36
|
+
StakeController,
|
|
37
|
+
StakeControllerIDL,
|
|
38
|
+
StakeRegistry,
|
|
39
|
+
StakeRegistryIDL,
|
|
40
|
+
Treasury,
|
|
41
|
+
TreasuryIDL,
|
|
42
|
+
ValidatorLeaderboard,
|
|
43
|
+
ValidatorLeaderboardIDL,
|
|
44
|
+
ValidatorRegistry,
|
|
45
|
+
ValidatorRegistryIDL,
|
|
46
|
+
YieldOracle,
|
|
47
|
+
YieldOracleIDL,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// — Program IDs — anchor “address” fields inside each IDL must match these:
|
|
51
|
+
export const DEPOSIT_PROGRAM_ID = new PublicKey(DepositIDL.address);
|
|
52
|
+
export const DISTRIBUTION_PROGRAM_ID = new PublicKey(DistributionIDL.address);
|
|
53
|
+
export const LIQSOL_TOKEN_PROGRAM_ID = new PublicKey(LiqSolTokenIDL.address);
|
|
54
|
+
export const STAKE_CONTROLLER_PROGRAM_ID = new PublicKey(StakeControllerIDL.address);
|
|
55
|
+
export const STAKE_REGISTRY_PROGRAM_ID = new PublicKey(StakeRegistryIDL.address);
|
|
56
|
+
export const TREASURY_PROGRAM_ID = new PublicKey(TreasuryIDL.address);
|
|
57
|
+
export const VALIDATOR_LEADERBOARD_PROGRAM_ID = new PublicKey(ValidatorLeaderboardIDL.address);
|
|
58
|
+
export const VALIDATOR_REGISTRY_PROGRAM_ID = new PublicKey(ValidatorRegistryIDL.address);
|
|
59
|
+
export const YIELD_ORACLE_PROGRAM_ID = new PublicKey(YieldOracleIDL.address);
|
|
60
|
+
|
|
61
|
+
// — Protocol constants — tweak as needed:
|
|
62
|
+
export const MIN_SOL_TO_PARTICIPATE = 1_000_000_000; // 1 SOL in lamports
|
|
63
|
+
export const MIN_FUNDS_THRESHOLD = 10_000_000_000; // 10 SOL in lamports
|
|
64
|
+
export const RESERVE_FOR_FEES = 1_000_000_000; // 1 SOL
|
|
65
|
+
|
|
66
|
+
// — Pre-derived PDA seeds for your convenience —
|
|
67
|
+
// (you can still use your utils.ts helpers instead)
|
|
68
|
+
export const TREASURY_WALLET_PDA = new PublicKey('9gLj1MRrm66GNYqnMLPGUzyYqYJpVLQgFSbtCQV9Ta4G');
|
|
69
|
+
export const LIQSOL_MINT_ADDRESS = new PublicKey('B2XtJABkc6eUoYUNgSfZVb3kYckmbh2zxp4rp2hX2Xwa');
|
|
70
|
+
export const STAKE_VAULT_PDA = new PublicKey('AY83EfU5LXJGDhbLeP45H3tLKisGZgSe2G4mGYfPimqN');
|
|
71
|
+
|
|
72
|
+
// re-export SPL token constants:
|
|
73
|
+
export { TOKEN_2022_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID };
|