Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split accounts #246

Merged
merged 25 commits into from
Oct 17, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions staking/app/StakeConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -810,6 +810,42 @@ export class StakeConnection {
})
.rpc();
}

public async requestSplit(
stakeAccount: StakeAccount,
amount: PythBalance,
recipient: PublicKey
) {
await this.program.methods
.requestSplit(amount.toBN(), recipient)
.accounts({
stakeAccountPositions: stakeAccount.address,
})
.rpc();
}

public async acceptSplit(stakeAccount: StakeAccount) {
const newStakeAccountKeypair = new Keypair();

const instructions = [];
instructions.push(
await this.program.account.positionData.createInstruction(
newStakeAccountKeypair,
wasm.Constants.POSITIONS_ACCOUNT_SIZE()
)
);

await this.program.methods
.acceptSplit()
.accounts({
currentStakeAccountPositions: stakeAccount.address,
newStakeAccountPositions: newStakeAccountKeypair.publicKey,
mint: this.config.pythTokenMint,
})
.signers([newStakeAccountKeypair])
.preInstructions(instructions)
.rpc();
}
}
export interface BalanceSummary {
withdrawable: PythBalance;
Expand Down
92 changes: 92 additions & 0 deletions staking/programs/staking/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub const TARGET_SEED: &str = "target";
pub const MAX_VOTER_RECORD_SEED: &str = "max_voter";
pub const VOTING_TARGET_SEED: &str = "voting";
pub const DATA_TARGET_SEED: &str = "staking";
pub const SPLIT_REQUEST: &str = "split_request";

impl positions::Target {
pub fn get_seed(&self) -> Vec<u8> {
Expand Down Expand Up @@ -278,6 +279,97 @@ pub struct CreateTarget<'info> {
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
#[instruction(amount : u64, recipient : Pubkey)]
pub struct RequestSplit<'info> {
// Native payer:
#[account(mut, address = stake_account_metadata.owner)]
pub payer: Signer<'info>,
// Stake program accounts:
pub stake_account_positions: AccountLoader<'info, positions::PositionData>,
#[account(seeds = [STAKE_ACCOUNT_METADATA_SEED.as_bytes(), stake_account_positions.key().as_ref()], bump = stake_account_metadata.metadata_bump)]
pub stake_account_metadata: Account<'info, stake_account::StakeAccountMetadataV2>,
#[account(init_if_needed, payer = payer, space=split_request::SplitRequest::LEN , seeds = [SPLIT_REQUEST.as_bytes(), stake_account_positions.key().as_ref()], bump)]
pub stake_account_split_request: Account<'info, split_request::SplitRequest>,
#[account(seeds = [CONFIG_SEED.as_bytes()], bump = config.bump)]
pub config: Account<'info, global_config::GlobalConfig>,
// Primitive accounts :
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct AcceptSplit<'info> {
// Native payer:
#[account(mut, address = config.pda_authority)]
pub payer: Signer<'info>,
// Current stake accounts:
#[account(mut)]
pub current_stake_account_positions: AccountLoader<'info, positions::PositionData>,
#[account(mut, seeds = [STAKE_ACCOUNT_METADATA_SEED.as_bytes(), current_stake_account_positions.key().as_ref()], bump = current_stake_account_metadata.metadata_bump)]
pub current_stake_account_metadata: Box<Account<'info, stake_account::StakeAccountMetadataV2>>,
#[account(seeds = [SPLIT_REQUEST.as_bytes(), current_stake_account_positions.key().as_ref()], bump)]
pub current_stake_account_split_request: Box<Account<'info, split_request::SplitRequest>>,
#[account(
mut,
seeds = [CUSTODY_SEED.as_bytes(), current_stake_account_positions.key().as_ref()],
bump = current_stake_account_metadata.custody_bump,
)]
pub current_stake_account_custody: Account<'info, TokenAccount>,
/// CHECK : This AccountInfo is safe because it's a checked PDA
#[account(seeds = [AUTHORITY_SEED.as_bytes(), current_stake_account_positions.key().as_ref()], bump = current_stake_account_metadata.authority_bump)]
pub current_custody_authority: AccountInfo<'info>,

// New stake accounts :
guibescos marked this conversation as resolved.
Show resolved Hide resolved
#[account(zero)]
pub new_stake_account_positions: AccountLoader<'info, positions::PositionData>,
#[account(init, payer = payer, space = stake_account::StakeAccountMetadataV2::LEN, seeds = [STAKE_ACCOUNT_METADATA_SEED.as_bytes(), new_stake_account_positions.key().as_ref()], bump)]
pub new_stake_account_metadata: Box<Account<'info, stake_account::StakeAccountMetadataV2>>,
#[account(
init,
seeds = [CUSTODY_SEED.as_bytes(), new_stake_account_positions.key().as_ref()],
bump,
payer = payer,
token::mint = mint,
token::authority = new_custody_authority,
)]
pub new_stake_account_custody: Account<'info, TokenAccount>,
/// CHECK : This AccountInfo is safe because it's a checked PDA
#[account(seeds = [AUTHORITY_SEED.as_bytes(), new_stake_account_positions.key().as_ref()], bump)]
pub new_custody_authority: AccountInfo<'info>,
#[account(
init,
payer = payer,
space = voter_weight_record::VoterWeightRecord::LEN,
seeds = [VOTER_RECORD_SEED.as_bytes(), new_stake_account_positions.key().as_ref()],
bump)]
pub new_voter_record: Box<Account<'info, voter_weight_record::VoterWeightRecord>>,

#[account(seeds = [CONFIG_SEED.as_bytes()], bump = config.bump)]
pub config: Account<'info, global_config::GlobalConfig>,

// Pyth token mint:
#[account(address = config.pyth_token_mint)]
pub mint: Account<'info, Mint>,
// Primitive accounts :
pub rent: Sysvar<'info, Rent>,
pub token_program: Program<'info, Token>,
pub system_program: Program<'info, System>,
}

impl<'a, 'b, 'c, 'info> From<&AcceptSplit<'info>>
for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>
{
fn from(accounts: &AcceptSplit<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> {
let cpi_accounts = Transfer {
from: accounts.current_stake_account_custody.to_account_info(),
to: accounts.new_stake_account_custody.to_account_info(),
authority: accounts.current_custody_authority.to_account_info(),
};
let cpi_program = accounts.token_program.to_account_info();
CpiContext::new(cpi_program, cpi_accounts)
}
}

// Anchor's parser doesn't understand cfg(feature), so the IDL gets messed
// up if we try to use it here. We can just keep the definition the same.
#[derive(Accounts)]
Expand Down
34 changes: 34 additions & 0 deletions staking/programs/staking/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -534,4 +534,38 @@ pub mod staking {
Err(error!(ErrorCode::DebuggingOnly))
}
}

pub fn request_split(ctx: Context<RequestSplit>, amount: u64, recipient: Pubkey) -> Result<()> {
ctx.accounts.stake_account_split_request.amount = amount;
ctx.accounts.stake_account_split_request.recipient = recipient;
Ok(())
}


pub fn accept_split(ctx: Context<AcceptSplit>) -> Result<()> {
// TODO : Split vesting schedule between both accounts

// TODO : Transfer stake positions to the new account if need
guibescos marked this conversation as resolved.
Show resolved Hide resolved

// Transfer tokens
{
let split_request = &ctx.accounts.current_stake_account_split_request;
transfer(
CpiContext::from(&*ctx.accounts).with_signer(&[&[
AUTHORITY_SEED.as_bytes(),
ctx.accounts.current_stake_account_positions.key().as_ref(),
&[ctx.accounts.current_stake_account_metadata.authority_bump],
]]),
split_request.amount,
)?;
}

// Delete current request
{
ctx.accounts.current_stake_account_split_request.amount = 0;
}
Ok(())

// TODO Check both accounts are valid after the transfer
}
}
1 change: 1 addition & 0 deletions staking/programs/staking/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod global_config;
pub mod max_voter_weight_record;
pub mod positions;
pub mod split_request;
pub mod stake_account;
pub mod target;
pub mod vesting;
Expand Down
17 changes: 17 additions & 0 deletions staking/programs/staking/src/state/split_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use {
anchor_lang::prelude::*,
borsh::BorshSchema,
};

#[account]
#[derive(Default, BorshSchema)]
pub struct SplitRequest {
pub amount: u64,
pub recipient: Pubkey,
}

impl SplitRequest {
pub const LEN: usize = 8 // Discriminant
+ 8 // Amount
+ 32; // Recipient
}
Loading
Loading