Skip to content

Commit 61a14a3

Browse files
committed
withdrawv2 tested
1 parent 6e06b8c commit 61a14a3

File tree

21 files changed

+4075
-359
lines changed

21 files changed

+4075
-359
lines changed

Cargo.lock

Lines changed: 3625 additions & 341 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,21 @@ edition = "2021"
1313
# and enable individual features in indiv crates
1414
borsh = { version = "^1", default-features = false }
1515
const-crypto = { version = "^0.3", default-features = false }
16+
generic-array-struct = { version = "^0.2", default-features = false }
1617

1718
# dev deps
1819
bs58 = { version = "^0.5", default-features = false }
1920
serde = { version = "^1", default-features = false }
2021
serde_json = { version = "^1", default-features = false }
2122

22-
# solana deps
23+
# solana dev deps
2324
# group them together to ensure same version throughout
2425
# and to deal with dependency hell more easily
26+
mollusk-svm-programs-token = { version = "^0.1", default-features = false }
27+
mollusk-svm = { version = "^0.1", default-features = false }
28+
solana-account = { version = "^2", default-features = false }
2529
solana-account-decoder-client-types = { version = "^2", default-features = false }
30+
solana-instruction = { version = "^2", default-features = false }
2631
solana-pubkey = { version = "^2", default-features = false }
2732

2833
# workspace members

core/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,15 @@ edition.workspace = true
66
[dependencies]
77
borsh = { workspace = true, features = ["derive"] }
88
const-crypto = { workspace = true }
9+
generic-array-struct = { workspace = true }
910

1011
[dev-dependencies]
1112
bs58 = { workspace = true }
13+
mollusk-svm = { workspace = true, features = ["all-builtins"] } # needed for stake program
14+
mollusk-svm-programs-token = { workspace = true, features = ["token"] }
1215
serde = { workspace = true, features = ["derive"] }
1316
serde_json = { workspace = true }
17+
solana-account = { workspace = true }
1418
solana-account-decoder-client-types = { workspace = true }
19+
solana-instruction = { workspace = true, features = ["std"] }
1520
solana-pubkey = { workspace = true, features = ["curve25519"] }

core/src/instructions/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
mod withdraw_v2;
2+
3+
pub use withdraw_v2::*;
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
use generic_array_struct::generic_array_struct;
2+
3+
use crate::{
4+
internal_utils::const_assign_byte_arr, LIDO_STATE_ADDR, STAKE_AUTH_PDA, STAKE_PROGRAM,
5+
STSOL_MINT_ADDR, SYSTEM_PROGRAM, SYSVAR_CLOCK, TOKENKEG_PROGRAM, VALIDATOR_LIST_ADDR,
6+
};
7+
8+
pub const WITHDRAW_V2_IX_DISCM: u8 = 23;
9+
10+
#[generic_array_struct(pub)]
11+
#[repr(transparent)]
12+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
13+
pub struct WithdrawV2IxAccs<T> {
14+
pub lido_state: T,
15+
pub user: T,
16+
pub burn_stsol_from: T,
17+
pub stsol_mint: T,
18+
pub vote: T,
19+
20+
/// PDA obtained using
21+
/// [`crate::Validator::withdraw_stake_acc_seeds()`]
22+
/// of `Validator` entry on the `ValidatorList` account
23+
/// for `vote = self.vote()`
24+
pub withdraw_stake_from: T,
25+
26+
/// Must be a system account prefunded with rent-exempt lamports
27+
/// for a stake account. The program will call allocate() and assign()
28+
pub split_stake_to: T,
29+
30+
pub stake_auth: T,
31+
pub validator_list: T,
32+
pub token_prog: T,
33+
pub sysvar_clock: T,
34+
pub system_prog: T,
35+
pub stake_prog: T,
36+
}
37+
38+
pub type WithdrawV2IxKeysOwned = WithdrawV2IxAccs<[u8; 32]>;
39+
pub type WithdrawV2IxKeys<'a> = WithdrawV2IxAccs<&'a [u8; 32]>;
40+
pub type WithdrawV2IxAccsFlag = WithdrawV2IxAccs<bool>;
41+
42+
pub const WITHDRAW_V2_IX_IS_WRITER: WithdrawV2IxAccsFlag =
43+
WithdrawV2IxAccsFlag::new([false; WITHDRAW_V2_IX_ACCS_LEN])
44+
.const_with_lido_state(true)
45+
.const_with_burn_stsol_from(true)
46+
.const_with_stsol_mint(true)
47+
.const_with_withdraw_stake_from(true)
48+
.const_with_split_stake_to(true)
49+
.const_with_validator_list(true);
50+
51+
pub const WITHDRAW_V2_IX_IS_SIGNER: WithdrawV2IxAccsFlag =
52+
WithdrawV2IxAccsFlag::new([false; WITHDRAW_V2_IX_ACCS_LEN])
53+
.const_with_user(true)
54+
.const_with_split_stake_to(true);
55+
56+
impl<T> WithdrawV2IxAccs<T> {
57+
/// This seems redundant because `.0` is `pub`, but this is necessary for
58+
/// nice init syntax with type aliases.
59+
///
60+
/// With this, you can now do
61+
///
62+
/// ```
63+
/// use solido_legacy_core::WithdrawV2IxAccsFlag;
64+
/// let var: WithdrawV2IxAccsFlag = WithdrawV2IxAccsFlag::new(Default::default());
65+
/// ```
66+
///
67+
/// instead of
68+
///
69+
/// ```
70+
/// use solido_legacy_core::{WithdrawV2IxAccsFlag, WithdrawV2IxAccs};
71+
/// let var: WithdrawV2IxAccsFlag = WithdrawV2IxAccs(Default::default());
72+
/// ```
73+
#[inline]
74+
pub const fn new(arr: [T; WITHDRAW_V2_IX_ACCS_LEN]) -> Self {
75+
Self(arr)
76+
}
77+
}
78+
79+
impl WithdrawV2IxKeysOwned {
80+
#[inline]
81+
pub fn as_borrowed(&self) -> WithdrawV2IxKeys<'_> {
82+
WithdrawV2IxKeys::new(self.0.each_ref())
83+
}
84+
}
85+
86+
impl WithdrawV2IxKeysOwned {
87+
#[inline]
88+
pub fn with_lido_consts(self) -> Self {
89+
self.as_borrowed().with_lido_consts().into_owned()
90+
}
91+
92+
#[inline]
93+
pub fn with_sol_consts(self) -> Self {
94+
self.as_borrowed().with_sol_consts().into_owned()
95+
}
96+
}
97+
98+
impl WithdrawV2IxKeys<'_> {
99+
#[inline]
100+
pub fn into_owned(self) -> WithdrawV2IxKeysOwned {
101+
WithdrawV2IxKeysOwned::new(self.0.map(|pk| *pk))
102+
}
103+
}
104+
105+
impl WithdrawV2IxKeys<'_> {
106+
#[inline]
107+
pub const fn with_lido_consts(self) -> Self {
108+
self.const_with_lido_state(&LIDO_STATE_ADDR)
109+
.const_with_stsol_mint(&STSOL_MINT_ADDR)
110+
.const_with_stake_auth(&STAKE_AUTH_PDA)
111+
.const_with_validator_list(&VALIDATOR_LIST_ADDR)
112+
}
113+
114+
#[inline]
115+
pub const fn with_sol_consts(self) -> Self {
116+
self.const_with_system_prog(&SYSTEM_PROGRAM)
117+
.const_with_stake_prog(&STAKE_PROGRAM)
118+
.const_with_sysvar_clock(&SYSVAR_CLOCK)
119+
.const_with_token_prog(&TOKENKEG_PROGRAM)
120+
}
121+
}
122+
123+
pub const WITHDRAW_V2_IX_DATA_LEN: usize = 13;
124+
125+
#[repr(transparent)]
126+
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
127+
pub struct WithdrawV2IxData([u8; WITHDRAW_V2_IX_DATA_LEN]);
128+
129+
impl WithdrawV2IxData {
130+
#[inline]
131+
pub const fn new(amount: u64, validator_index: u32) -> Self {
132+
let mut res = [0u8; WITHDRAW_V2_IX_DATA_LEN];
133+
const_assign_byte_arr::<WITHDRAW_V2_IX_DATA_LEN, 0, 1>(&mut res, [WITHDRAW_V2_IX_DISCM]);
134+
const_assign_byte_arr::<WITHDRAW_V2_IX_DATA_LEN, 1, 8>(&mut res, amount.to_le_bytes());
135+
const_assign_byte_arr::<WITHDRAW_V2_IX_DATA_LEN, 9, 4>(
136+
&mut res,
137+
validator_index.to_le_bytes(),
138+
);
139+
Self(res)
140+
}
141+
142+
#[inline]
143+
pub const fn to_buf(self) -> [u8; WITHDRAW_V2_IX_DATA_LEN] {
144+
self.0
145+
}
146+
}

core/src/internal_utils.rs

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ macro_rules! impl_get_le {
4545
}
4646

4747
macro_rules! impl_set_le {
48-
($setter:ident, $field:ident, $t:ty) => {
48+
($set:ident, $field:ident, $t:ty) => {
4949
#[inline]
50-
pub const fn $setter(&mut self, val: $t) {
50+
pub const fn $set(&mut self, val: $t) {
5151
self.$field = val.to_le_bytes();
5252
}
5353
};
@@ -64,8 +64,8 @@ macro_rules! impl_with_le {
6464
}
6565

6666
macro_rules! impl_set_with_get_le {
67-
($setter:ident, $with:ident, $field:ident, $t:ty) => {
68-
impl_set_le!($setter, $field, $t);
67+
($set:ident, $with:ident, $field:ident, $t:ty) => {
68+
impl_set_le!($set, $field, $t);
6969
impl_with_le!($with, $field, $t);
7070
impl_get_le!($field, $t);
7171
};
@@ -90,9 +90,9 @@ macro_rules! impl_get {
9090
}
9191

9292
macro_rules! impl_set {
93-
($setter:ident, $field:ident, $t:ty) => {
93+
($set:ident, $field:ident, $t:ty) => {
9494
#[inline]
95-
pub const fn $setter(&mut self, val: $t) {
95+
pub const fn $set(&mut self, val: $t) {
9696
self.$field = val;
9797
}
9898
};
@@ -109,17 +109,31 @@ macro_rules! impl_with {
109109
}
110110

111111
macro_rules! impl_set_with_get_ref {
112-
($setter:ident, $with:ident, $field:ident, $t:ty) => {
113-
impl_set!($setter, $field, $t);
112+
($set:ident, $with:ident, $field:ident, $t:ty) => {
113+
impl_set!($set, $field, $t);
114114
impl_with!($with, $field, $t);
115115
impl_get_ref!($field, $t);
116116
};
117117
}
118118

119119
macro_rules! impl_set_with_get {
120-
($setter:ident, $with:ident, $field:ident, $t:ty) => {
121-
impl_set!($setter, $field, $t);
120+
($set:ident, $with:ident, $field:ident, $t:ty) => {
121+
impl_set!($set, $field, $t);
122122
impl_with!($with, $field, $t);
123123
impl_get!($field, $t);
124124
};
125125
}
126+
127+
pub const fn const_assign_byte_arr<const A: usize, const START: usize, const LEN: usize>(
128+
arr: &mut [u8; A],
129+
val: [u8; LEN],
130+
) {
131+
const {
132+
assert!(START + LEN <= A);
133+
}
134+
// safety: bounds checked at comptime above
135+
unsafe {
136+
// guarantee nonoverlapping due to `&mut`
137+
core::ptr::copy_nonoverlapping(val.as_ptr(), arr.as_ptr().add(START).cast_mut(), LEN);
138+
}
139+
}

core/src/keys.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,22 @@ pub const STSOL_MINT_ADDR_STR: &str = "7dHbWXmci3dT8UFYWYZweBLXgycu7Y3iL6trKn1Y7
1313
pub const STSOL_MINT_ADDR: [u8; 32] = bs58::decode_pubkey(STSOL_MINT_ADDR_STR);
1414

1515
const STAKE_AUTH_PDA_TUP: ([u8; 32], u8) = derive_program_address(
16-
&[b"stake_authority", LIDO_STATE_ADDR.as_slice()],
16+
&[LIDO_STATE_ADDR.as_slice(), b"stake_authority"],
1717
&PROGRAM_ID,
1818
);
1919
pub const STAKE_AUTH_PDA: [u8; 32] = STAKE_AUTH_PDA_TUP.0;
2020
pub const STAKE_AUTH_PDA_BUMP: u8 = STAKE_AUTH_PDA_TUP.1;
2121
pub const STAKE_AUTH_PDA_STR: &str = bs58::encode_pubkey(&STAKE_AUTH_PDA).str();
22+
23+
// solana system-wide consts
24+
25+
pub const SYSVAR_CLOCK: [u8; 32] =
26+
bs58::decode_pubkey("SysvarC1ock11111111111111111111111111111111");
27+
28+
pub const SYSTEM_PROGRAM: [u8; 32] = bs58::decode_pubkey("11111111111111111111111111111111");
29+
30+
pub const STAKE_PROGRAM: [u8; 32] =
31+
bs58::decode_pubkey("Stake11111111111111111111111111111111111111");
32+
33+
pub const TOKENKEG_PROGRAM: [u8; 32] =
34+
bs58::decode_pubkey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");

core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#![cfg_attr(not(test), no_std)]
22

3+
mod instructions;
34
mod internal_utils;
45
mod keys;
56
mod pda;
67
mod state;
78
mod typedefs;
89

10+
pub use instructions::*;
911
pub use keys::*;
1012
pub use pda::*;
1113
pub use state::*;

core/src/state/account_list.rs renamed to core/src/state/account_list/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ use core::num::TryFromIntError;
22

33
use borsh::{BorshDeserialize, BorshSerialize};
44

5-
use crate::{ListHeader, Validator};
5+
use crate::ListHeader;
66

7-
pub type ValidatorList<'a> = AccountList<'a, Validator>;
7+
mod validator_list;
8+
9+
pub use validator_list::*;
810

911
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)]
1012
pub struct AccountList<'a, T> {
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use crate::Validator;
2+
3+
use super::AccountList;
4+
5+
pub type ValidatorList<'a> = AccountList<'a, Validator>;
6+
7+
impl ValidatorList<'_> {
8+
/// Program only allows stake to be withdrawn from the largest
9+
/// validator on the list
10+
pub fn withdrawing_validator(&self) -> Option<(usize, &Validator)> {
11+
self.entries
12+
.iter()
13+
.enumerate()
14+
.max_by_key(|(_i, v)| v.effective_stake_balance())
15+
}
16+
}

0 commit comments

Comments
 (0)