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

solana: Allow transferring mint authority #570

Merged
merged 15 commits into from
Jan 17, 2025
Merged
Show file tree
Hide file tree
Changes from 12 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
6 changes: 6 additions & 0 deletions solana/programs/example-native-token-transfers/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ pub enum NTTError {
BitmapIndexOutOfBounds,
#[msg("NoRegisteredTransceivers")]
NoRegisteredTransceivers,
#[msg("NotPaused")]
NotPaused,
#[msg("InvalidPendingTokenAuthority")]
InvalidPendingTokenAuthority,
#[msg("IncorrectRentPayer")]
IncorrectRentPayer,
}

impl From<ScalingError> for NTTError {
Expand Down
nvsriram marked this conversation as resolved.
Show resolved Hide resolved
nvsriram marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anchor_lang::prelude::*;
use anchor_spl::{token_2022::spl_token_2022::instruction::AuthorityType, token_interface};
use ntt_messages::chain_id::ChainId;
use wormhole_solana_utils::cpi::bpf_loader_upgradeable::{self, BpfLoaderUpgradeable};

Expand All @@ -9,6 +10,7 @@ use crate::{
config::Config,
error::NTTError,
peer::NttManagerPeer,
pending_token_authority::PendingTokenAuthority,
queue::{inbox::InboxRateLimit, outbox::OutboxRateLimit, rate_limit::RateLimitState},
registered_transceiver::RegisteredTransceiver,
};
Expand Down Expand Up @@ -154,6 +156,200 @@ pub fn claim_ownership(ctx: Context<ClaimOwnership>) -> Result<()> {
)
}

// * Set token authority

#[derive(Accounts)]
pub struct AcceptTokenAuthority<'info> {
#[account(
has_one = mint,
constraint = config.paused @ NTTError::NotPaused,
)]
pub config: Account<'info, Config>,

#[account(mut)]
pub mint: InterfaceAccount<'info, token_interface::Mint>,

#[account(
seeds = [crate::TOKEN_AUTHORITY_SEED],
bump,
)]
/// CHECK: The constraints enforce this is valid mint authority
pub token_authority: UncheckedAccount<'info>,

pub current_authority: Signer<'info>,

pub token_program: Interface<'info, token_interface::TokenInterface>,
}

pub fn accept_token_authority(ctx: Context<AcceptTokenAuthority>) -> Result<()> {
token_interface::set_authority(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
token_interface::SetAuthority {
account_or_mint: ctx.accounts.mint.to_account_info(),
current_authority: ctx.accounts.current_authority.to_account_info(),
},
),
AuthorityType::MintTokens,
Some(ctx.accounts.token_authority.key()),
)
}

#[derive(Accounts)]
pub struct SetTokenAuthority<'info> {
#[account(
has_one = owner,
has_one = mint,
constraint = config.paused @ NTTError::NotPaused,
)]
pub config: Account<'info, Config>,

pub owner: Signer<'info>,

#[account(mut)]
pub mint: InterfaceAccount<'info, token_interface::Mint>,

#[account(
seeds = [crate::TOKEN_AUTHORITY_SEED],
bump,
)]
/// CHECK: The constraints enforce this is valid mint authority
pub token_authority: UncheckedAccount<'info>,

/// CHECK: This account will be the signer in the [claim_token_authority] instruction.
pub new_authority: UncheckedAccount<'info>,
}

#[derive(Accounts)]
pub struct SetTokenAuthorityChecked<'info> {
#[account(
constraint = common.token_authority.key() == common.mint.mint_authority.unwrap() @ NTTError::InvalidMintAuthority
)]
pub common: SetTokenAuthority<'info>,

#[account(mut)]
pub rent_payer: Signer<'info>,

#[account(
init_if_needed,
space = 8 + PendingTokenAuthority::INIT_SPACE,
payer = rent_payer,
seeds = [PendingTokenAuthority::SEED_PREFIX],
bump
)]
pub pending_token_authority: Account<'info, PendingTokenAuthority>,

pub system_program: Program<'info, System>,
}

pub fn set_token_authority(ctx: Context<SetTokenAuthorityChecked>) -> Result<()> {
ctx.accounts
.pending_token_authority
.set_inner(PendingTokenAuthority {
bump: ctx.bumps.pending_token_authority,
pending_authority: ctx.accounts.common.new_authority.key(),
rent_payer: ctx.accounts.rent_payer.key(),
});
Ok(())
}

#[derive(Accounts)]
pub struct SetTokenAuthorityUnchecked<'info> {
pub common: SetTokenAuthority<'info>,

pub token_program: Interface<'info, token_interface::TokenInterface>,
}

pub fn set_token_authority_one_step_unchecked(
ctx: Context<SetTokenAuthorityUnchecked>,
) -> Result<()> {
token_interface::set_authority(
CpiContext::new_with_signer(
ctx.accounts.token_program.to_account_info(),
token_interface::SetAuthority {
account_or_mint: ctx.accounts.common.mint.to_account_info(),
current_authority: ctx.accounts.common.token_authority.to_account_info(),
},
&[&[
crate::TOKEN_AUTHORITY_SEED,
&[ctx.bumps.common.token_authority],
]],
),
AuthorityType::MintTokens,
Some(ctx.accounts.common.new_authority.key()),
)
}

// * Claim token authority

#[derive(Accounts)]
pub struct RevertTokenAuthority<'info> {
#[account(
has_one = mint,
constraint = config.paused @ NTTError::NotPaused,
)]
pub config: Account<'info, Config>,

#[account(mut)]
pub mint: InterfaceAccount<'info, token_interface::Mint>,

#[account(
seeds = [crate::TOKEN_AUTHORITY_SEED],
bump,
)]
/// CHECK: The seeds constraint enforces that this is the correct address
pub token_authority: UncheckedAccount<'info>,

#[account(mut)]
/// CHECK: the `pending_token_authority` constraint enforces that this is the correct address
pub rent_payer: UncheckedAccount<'info>,

#[account(
mut,
seeds = [PendingTokenAuthority::SEED_PREFIX],
bump = pending_token_authority.bump,
has_one = rent_payer @ NTTError::IncorrectRentPayer,
close = rent_payer
)]
pub pending_token_authority: Account<'info, PendingTokenAuthority>,

pub token_program: Interface<'info, token_interface::TokenInterface>,

pub system_program: Program<'info, System>,
}
nvsriram marked this conversation as resolved.
Show resolved Hide resolved

pub fn revert_token_authority(_ctx: Context<RevertTokenAuthority>) -> Result<()> {
Ok(())
}

#[derive(Accounts)]
pub struct ClaimTokenAuthority<'info> {
pub common: RevertTokenAuthority<'info>,

#[account(
address = common.pending_token_authority.pending_authority @ NTTError::InvalidPendingTokenAuthority
)]
pub new_authority: Signer<'info>,
}

pub fn claim_token_authority(ctx: Context<ClaimTokenAuthority>) -> Result<()> {
token_interface::set_authority(
CpiContext::new_with_signer(
ctx.accounts.common.token_program.to_account_info(),
token_interface::SetAuthority {
account_or_mint: ctx.accounts.common.mint.to_account_info(),
current_authority: ctx.accounts.common.token_authority.to_account_info(),
},
&[&[
crate::TOKEN_AUTHORITY_SEED,
&[ctx.bumps.common.token_authority],
]],
),
AuthorityType::MintTokens,
Some(ctx.accounts.new_authority.key()),
)
}

// * Set peers

#[derive(Accounts)]
Expand Down Expand Up @@ -267,7 +463,7 @@ pub fn register_transceiver(ctx: Context<RegisterTransceiver>) -> Result<()> {
#[derive(Accounts)]
pub struct SetOutboundLimit<'info> {
#[account(
constraint = config.owner == owner.key()
has_one = owner,
)]
pub config: Account<'info, Config>,

Expand All @@ -294,7 +490,7 @@ pub fn set_outbound_limit(
#[instruction(args: SetInboundLimitArgs)]
pub struct SetInboundLimit<'info> {
#[account(
constraint = config.owner == owner.key()
has_one = owner,
)]
pub config: Account<'info, Config>,

Expand Down
23 changes: 23 additions & 0 deletions solana/programs/example-native-token-transfers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub mod error;
pub mod instructions;
pub mod messages;
pub mod peer;
pub mod pending_token_authority;
pub mod queue;
pub mod registered_transceiver;
pub mod transceivers;
Expand Down Expand Up @@ -126,6 +127,28 @@ pub mod example_native_token_transfers {
instructions::claim_ownership(ctx)
}

pub fn accept_token_authority(ctx: Context<AcceptTokenAuthority>) -> Result<()> {
instructions::accept_token_authority(ctx)
}

pub fn set_token_authority(ctx: Context<SetTokenAuthorityChecked>) -> Result<()> {
instructions::set_token_authority(ctx)
}

pub fn set_token_authority_one_step_unchecked(
ctx: Context<SetTokenAuthorityUnchecked>,
) -> Result<()> {
instructions::set_token_authority_one_step_unchecked(ctx)
}

pub fn revert_token_authority(ctx: Context<RevertTokenAuthority>) -> Result<()> {
instructions::revert_token_authority(ctx)
}

pub fn claim_token_authority(ctx: Context<ClaimTokenAuthority>) -> Result<()> {
instructions::claim_token_authority(ctx)
}

pub fn set_paused(ctx: Context<SetPaused>, pause: bool) -> Result<()> {
instructions::set_paused(ctx, pause)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
use anchor_lang::prelude::*;

#[account]
#[derive(InitSpace)]
pub struct PendingTokenAuthority {
pub bump: u8,
pub pending_authority: Pubkey,
pub rent_payer: Pubkey,
}

impl PendingTokenAuthority {
pub const SEED_PREFIX: &'static [u8] = b"pending_token_authority";
}
Loading
Loading