Skip to content

Commit

Permalink
solana[WIP]: Refactor to reduce code duplication
Browse files Browse the repository at this point in the history
  • Loading branch information
nvsriram committed Dec 7, 2024
1 parent efe3daa commit 4e93386
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,7 @@ pub struct Initialize<'info> {
)]
pub config: Box<Account<'info, crate::config::Config>>,

#[account(
constraint =
args.mode == Mode::Locking
|| mint.mint_authority.unwrap() == token_authority.key()
@ NTTError::InvalidMintAuthority,
)]
#[account()]
pub mint: Box<InterfaceAccount<'info, token_interface::Mint>>,

#[account(
Expand Down Expand Up @@ -95,25 +90,79 @@ pub struct InitializeArgs {
pub mode: ntt_messages::mode::Mode,
}

pub fn initialize(ctx: Context<Initialize>, args: InitializeArgs) -> Result<()> {
ctx.accounts.config.set_inner(crate::config::Config {
bump: ctx.bumps.config,
mint: ctx.accounts.mint.key(),
token_program: ctx.accounts.token_program.key(),
mode: args.mode,
chain_id: ChainId { id: args.chain_id },
owner: ctx.accounts.deployer.key(),
#[derive(Accounts)]
#[instruction(args: InitializeArgs)]
pub struct InitializeDefault<'info> {
#[account(
constraint =
args.mode == Mode::Locking
|| common.mint.mint_authority.unwrap() == common.token_authority.key()
@ NTTError::InvalidMintAuthority,
)]
pub common: Initialize<'info>,
}

pub fn initialize(ctx: Context<InitializeDefault>, args: InitializeArgs) -> Result<()> {
initialize_config_and_rate_limit(
&mut ctx.accounts.common,
ctx.bumps.common.config,
args.chain_id,
args.limit,
args.mode,
)
}

#[derive(Accounts)]
#[instruction(args: InitializeArgs)]
pub struct InitializeMultisig<'info> {
#[account(
constraint =
args.mode == Mode::Locking
|| common.mint.mint_authority.unwrap() == multisig.key()
@ NTTError::InvalidMintAuthority,
)]
pub common: Initialize<'info>,

#[account()]
/// CHECK: multisig is mint authority
pub multisig: UncheckedAccount<'info>,
}

pub fn initialize_multisig(ctx: Context<InitializeMultisig>, args: InitializeArgs) -> Result<()> {
initialize_config_and_rate_limit(
&mut ctx.accounts.common,
ctx.bumps.common.config,
args.chain_id,
args.limit,
args.mode,
)
}

fn initialize_config_and_rate_limit(
common: &mut Initialize<'_>,
config_bump: u8,
chain_id: u16,
limit: u64,
mode: ntt_messages::mode::Mode,
) -> Result<()> {
common.config.set_inner(crate::config::Config {
bump: config_bump,
mint: common.mint.key(),
token_program: common.token_program.key(),
mode,
chain_id: ChainId { id: chain_id },
owner: common.deployer.key(),
pending_owner: None,
paused: false,
next_transceiver_id: 0,
// NOTE: can't be changed for now
threshold: 1,
enabled_transceivers: Bitmap::new(),
custody: ctx.accounts.custody.key(),
custody: common.custody.key(),
});

ctx.accounts.rate_limit.set_inner(OutboxRateLimit {
rate_limit: RateLimitState::new(args.limit),
common.rate_limit.set_inner(OutboxRateLimit {
rate_limit: RateLimitState::new(limit),
});

Ok(())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
pub mod admin;
pub mod initialize;
pub mod initialize_multisig;
pub mod luts;
pub mod mark_outbox_item_as_released;
pub mod redeem;
pub mod release_inbound;
pub mod release_inbound_multisig;
pub mod transfer;

pub use admin::*;
pub use initialize::*;
pub use initialize_multisig::*;
pub use luts::*;
pub use mark_outbox_item_as_released::*;
pub use redeem::*;
pub use release_inbound::*;
pub use release_inbound_multisig::*;
pub use transfer::*;
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::ops::{Deref, DerefMut};

use anchor_lang::prelude::*;
use anchor_spl::token_interface;
use ntt_messages::mode::Mode;
Expand Down Expand Up @@ -66,6 +68,34 @@ pub struct ReleaseInboundMint<'info> {
common: ReleaseInbound<'info>,
}

impl<'info> Deref for ReleaseInboundMint<'info> {
type Target = ReleaseInbound<'info>;

fn deref(&self) -> &Self::Target {
&self.common
}
}

impl Deref for ReleaseInboundMintBumps {
type Target = ReleaseInboundBumps;

fn deref(&self) -> &Self::Target {
&self.common
}
}

impl<'info> DerefMut for ReleaseInboundMint<'info> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.common
}
}

#[derive(Accounts)]
pub struct ReleaseInboundMintDefault<'info> {
#[account(constraint = common.mint.mint_authority.unwrap() == common.token_authority.key())]
common: ReleaseInboundMint<'info>,
}

/// Release an inbound transfer and mint the tokens to the recipient.
/// When `revert_on_error` is true, the transaction will revert if the
/// release timestamp has not been reached. When `revert_on_error` is false, the
Expand All @@ -74,23 +104,9 @@ pub struct ReleaseInboundMint<'info> {
/// together with [`crate::instructions::redeem`] in a transaction, so that the minting
/// is attempted optimistically.
pub fn release_inbound_mint<'info>(
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMint<'info>>,
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMintDefault<'info>>,
args: ReleaseInboundArgs,
) -> Result<()> {
let inbox_item = &mut ctx.accounts.common.inbox_item;

let released = inbox_item.try_release()?;

if !released {
if args.revert_on_delay {
return Err(NTTError::CantReleaseYet.into());
} else {
return Ok(());
}
}

assert!(inbox_item.release_status == ReleaseStatus::Released);

// NOTE: minting tokens is a two-step process:
// 1. Mint tokens to the custody account
// 2. Transfer the tokens from the custody account to the recipient
Expand Down Expand Up @@ -120,7 +136,7 @@ pub fn release_inbound_mint<'info>(
&[ctx.bumps.common.token_authority],
]],
),
inbox_item.amount,
ctx.accounts.common.inbox_item.amount,
)?;

// Step 2: transfer the tokens from the custody account to the recipient
Expand All @@ -131,13 +147,100 @@ pub fn release_inbound_mint<'info>(
ctx.accounts.common.recipient.to_account_info(),
ctx.accounts.common.token_authority.to_account_info(),
ctx.remaining_accounts,
inbox_item.amount,
ctx.accounts.common.inbox_item.amount,
ctx.accounts.common.mint.decimals,
&[&[
crate::TOKEN_AUTHORITY_SEED,
&[ctx.bumps.common.token_authority],
]],
)?;

release_inbox_item(&mut ctx.accounts.common.inbox_item, args.revert_on_delay)
}

#[derive(Accounts)]
pub struct ReleaseInboundMintMultisig<'info> {
#[account(constraint = common.mint.mint_authority.unwrap() == multisig.key())]
common: ReleaseInboundMint<'info>,

/// CHECK: multisig account should be mint authority
pub multisig: UncheckedAccount<'info>,
}

pub fn release_inbound_mint_multisig<'info>(
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMintMultisig<'info>>,
args: ReleaseInboundArgs,
) -> Result<()> {
// NOTE: minting tokens is a two-step process:
// 1. Mint tokens to the custody account
// 2. Transfer the tokens from the custody account to the recipient
//
// This is done to ensure that if the token has a transfer hook defined, it
// will be called after the tokens are minted.
// Unfortunately the Token2022 program doesn't trigger transfer hooks when
// minting tokens, so we have to do it "manually" via a transfer.
//
// If we didn't do this, transfer hooks could be bypassed by transferring
// the tokens out through NTT first, then back in to the intended recipient.
//
// The [`transfer_burn`] function operates in a similar way
// (transfer to custody from sender, *then* burn).

// Step 1: mint tokens to the custody account
let ix = spl_token_2022::instruction::mint_to(
&ctx.accounts.common.token_program.key(),
&ctx.accounts.common.mint.key(),
&ctx.accounts.common.custody.key(),
&ctx.accounts.multisig.key(),
&[&ctx.accounts.common.token_authority.key()],
ctx.accounts.common.inbox_item.amount,
)?;
solana_program::program::invoke_signed(
&ix,
&[
ctx.accounts.common.custody.to_account_info(),
ctx.accounts.common.mint.to_account_info(),
ctx.accounts.common.token_authority.to_account_info(),
ctx.accounts.multisig.to_account_info(),
],
&[&[
crate::TOKEN_AUTHORITY_SEED,
&[ctx.bumps.common.token_authority],
]],
)?;

// Step 2: transfer the tokens from the custody account to the recipient
onchain::invoke_transfer_checked(
&ctx.accounts.common.token_program.key(),
ctx.accounts.common.custody.to_account_info(),
ctx.accounts.common.mint.to_account_info(),
ctx.accounts.common.recipient.to_account_info(),
ctx.accounts.common.token_authority.to_account_info(),
ctx.remaining_accounts,
ctx.accounts.common.inbox_item.amount,
ctx.accounts.common.mint.decimals,
&[&[
crate::TOKEN_AUTHORITY_SEED,
&[ctx.bumps.common.token_authority],
]],
)?;

release_inbox_item(&mut ctx.accounts.common.inbox_item, args.revert_on_delay)
}

fn release_inbox_item(inbox_item: &mut InboxItem, revert_on_delay: bool) -> Result<()> {
let released = inbox_item.try_release()?;

if !released {
if revert_on_delay {
return Err(NTTError::CantReleaseYet.into());
} else {
return Ok(());
}
}

assert!(inbox_item.release_status == ReleaseStatus::Released);

Ok(())
}

Expand Down
20 changes: 10 additions & 10 deletions solana/programs/example-native-token-transfers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,13 @@ pub mod example_native_token_transfers {

use super::*;

pub fn initialize(ctx: Context<Initialize>, args: InitializeArgs) -> Result<()> {
pub fn initialize(ctx: Context<InitializeDefault>, args: InitializeArgs) -> Result<()> {
instructions::initialize(ctx, args)
}

pub fn initialize_multisig(
ctx: Context<InitializeMultisig>,
args: InitializeMultisigArgs,
args: InitializeArgs,
) -> Result<()> {
instructions::initialize_multisig(ctx, args)
}
Expand Down Expand Up @@ -108,24 +108,24 @@ pub mod example_native_token_transfers {
}

pub fn release_inbound_mint<'info>(
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMint<'info>>,
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMintDefault<'info>>,
args: ReleaseInboundArgs,
) -> Result<()> {
instructions::release_inbound_mint(ctx, args)
}

pub fn release_inbound_unlock<'info>(
ctx: Context<'_, '_, '_, 'info, ReleaseInboundUnlock<'info>>,
pub fn release_inbound_mint_multisig<'info>(
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMintMultisig<'info>>,
args: ReleaseInboundArgs,
) -> Result<()> {
instructions::release_inbound_unlock(ctx, args)
instructions::release_inbound_mint_multisig(ctx, args)
}

pub fn release_inbound_multisig_mint<'info>(
ctx: Context<'_, '_, '_, 'info, ReleaseInboundMultisigMint<'info>>,
args: ReleaseInboundMultisigArgs,
pub fn release_inbound_unlock<'info>(
ctx: Context<'_, '_, '_, 'info, ReleaseInboundUnlock<'info>>,
args: ReleaseInboundArgs,
) -> Result<()> {
instructions::release_inbound_multisig_mint(ctx, args)
instructions::release_inbound_unlock(ctx, args)
}

pub fn transfer_ownership(ctx: Context<TransferOwnership>) -> Result<()> {
Expand Down

0 comments on commit 4e93386

Please sign in to comment.