Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
94 changes: 81 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions programs/launchpad/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub mod fund;
pub mod initialize_launch;
pub mod refund;
pub mod start_launch;
pub mod recover_nested_tokens;

pub use claim::*;
pub use close_launch::*;
Expand All @@ -13,3 +14,4 @@ pub use fund::*;
pub use initialize_launch::*;
pub use refund::*;
pub use start_launch::*;
pub use recover_nested_tokens::*;
64 changes: 64 additions & 0 deletions programs/launchpad/src/instructions/recover_nested_tokens.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use anchor_lang::prelude::*;
use anchor_spl::{associated_token::AssociatedToken, token::{Mint, Token, TokenAccount}};

use crate::{state::{Launch, associated_token_program::{RecoverNested, recover_nested}}};

#[event_cpi]
#[derive(Accounts)]
pub struct RecoverNestedTokens<'info> {
#[account(
mut,
has_one = launch_quote_vault,
has_one = launch_signer,
)]
pub launch: Account<'info, Launch>,

#[account(mut)]
pub launch_quote_vault: Account<'info, TokenAccount>,
#[account(
mut,
associated_token::authority = launch_quote_vault,
associated_token::mint = launch_quote_vault.mint
)]
pub nested_launch_quote_vault: Account<'info, TokenAccount>,
#[account(
address = launch_quote_vault.mint
)]
pub mint: Account<'info, Mint>,

/// CHECK: just a signer
pub launch_signer: UncheckedAccount<'info>,

pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
}

impl RecoverNestedTokens<'_> {
pub fn handle(ctx: Context<Self>) -> Result<()> {
let seeds = &[
b"launch_signer",
ctx.accounts.launch.to_account_info().key.as_ref(),
&[ctx.accounts.launch.launch_signer_pda_bump],
];
let signer = &[&seeds[..]];

let accounts = RecoverNested {
nested_associated_account_address: ctx.accounts.nested_launch_quote_vault.to_account_info(),
nested_associated_mint_address: ctx.accounts.mint.to_account_info(),
destination_associated_account_address: ctx.accounts.launch_quote_vault.to_account_info(),
owner_associated_account_address: ctx.accounts.launch_quote_vault.to_account_info(),
owner_token_mint_address: ctx.accounts.mint.to_account_info(),
wallet_address: ctx.accounts.launch_signer.to_account_info(),
token_program: ctx.accounts.token_program.to_account_info(),
associated_token_program: ctx.accounts.associated_token_program.to_account_info(),
};

recover_nested(
CpiContext::new_with_signer(
ctx.accounts.associated_token_program.to_account_info(),
accounts,
signer
)
)
}
}
4 changes: 4 additions & 0 deletions programs/launchpad/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,8 @@ pub mod launchpad {
pub fn close_launch(ctx: Context<CloseLaunch>) -> Result<()> {
CloseLaunch::handle(ctx)
}

pub fn recover_nested_tokens(ctx: Context<RecoverNestedTokens>) -> Result<()> {
RecoverNestedTokens::handle(ctx)
}
}
56 changes: 56 additions & 0 deletions programs/launchpad/src/state/associated_token_program.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
use anchor_lang::prelude::*;

use anchor_spl::associated_token::AssociatedToken;
use solana_program::instruction::Instruction;

use super::*;

pub fn recover_nested<'info>(
ctx: CpiContext<'_, '_, '_, 'info, RecoverNested<'info>>,
) -> Result<()> {

// RecoverNested instruction
let instruction_data = vec![2];

let ix = Instruction {
program_id: ctx.accounts.associated_token_program.key(),
accounts: vec![
AccountMeta::new(ctx.accounts.nested_associated_account_address.key(), false),
AccountMeta::new_readonly(ctx.accounts.nested_associated_mint_address.key(), false),
AccountMeta::new(ctx.accounts.destination_associated_account_address.key(), false),
AccountMeta::new_readonly(ctx.accounts.owner_associated_account_address.key(), false),
AccountMeta::new_readonly(ctx.accounts.owner_token_mint_address.key(), false),
AccountMeta::new(ctx.accounts.wallet_address.key(), true),
AccountMeta::new_readonly(ctx.accounts.token_program.key(), false),
],
data: instruction_data.try_to_vec().unwrap()
};

solana_program::program::invoke_signed(
&ix,
&[
ctx.accounts.nested_associated_account_address,
ctx.accounts.nested_associated_mint_address,
ctx.accounts.destination_associated_account_address,
ctx.accounts.owner_associated_account_address,
ctx.accounts.owner_token_mint_address,
ctx.accounts.wallet_address,
ctx.accounts.token_program
],
ctx.signer_seeds,
)
.map_err(Into::into)
}

#[derive(Accounts)]
pub struct RecoverNested<'info> {
pub nested_associated_account_address: AccountInfo<'info>,
pub nested_associated_mint_address: AccountInfo<'info>,
pub destination_associated_account_address: AccountInfo<'info>,
pub owner_associated_account_address: AccountInfo<'info>,
pub owner_token_mint_address: AccountInfo<'info>,
pub wallet_address: AccountInfo<'info>,
pub token_program: AccountInfo<'info>,
#[account(address = AssociatedToken::id())]
pub associated_token_program: AccountInfo<'info>,
}
2 changes: 2 additions & 0 deletions programs/launchpad/src/state/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
pub mod associated_token_program;
pub mod funding_record;
pub mod launch;

pub use associated_token_program::*;
pub use funding_record::*;
pub use launch::*;