diff --git a/solana/programs/example-native-token-transfers/src/error.rs b/solana/programs/example-native-token-transfers/src/error.rs index 0002a12c1..5b3efe3cf 100644 --- a/solana/programs/example-native-token-transfers/src/error.rs +++ b/solana/programs/example-native-token-transfers/src/error.rs @@ -53,6 +53,12 @@ pub enum NTTError { BitmapIndexOutOfBounds, #[msg("NoRegisteredTransceivers")] NoRegisteredTransceivers, + #[msg("NotPaused")] + NotPaused, + #[msg("InvalidPendingTokenAuthority")] + InvalidPendingTokenAuthority, + #[msg("IncorrectRentPayer")] + IncorrectRentPayer, } impl From for NTTError { diff --git a/solana/programs/example-native-token-transfers/src/instructions/admin.rs b/solana/programs/example-native-token-transfers/src/instructions/admin.rs index 3486e21a4..e5c87d7f5 100644 --- a/solana/programs/example-native-token-transfers/src/instructions/admin.rs +++ b/solana/programs/example-native-token-transfers/src/instructions/admin.rs @@ -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}; @@ -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, }; @@ -154,6 +156,210 @@ pub fn claim_ownership(ctx: Context) -> 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) -> 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) -> 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, +) -> 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 ClaimTokenAuthorityBase<'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>, +} + +#[derive(Accounts)] +pub struct RevertTokenAuthority<'info> { + pub common: ClaimTokenAuthorityBase<'info>, + + #[account( + address = common.config.owner + )] + pub owner: Signer<'info>, +} + +pub fn revert_token_authority(_ctx: Context) -> Result<()> { + Ok(()) +} + +#[derive(Accounts)] +pub struct ClaimTokenAuthority<'info> { + pub common: ClaimTokenAuthorityBase<'info>, + + #[account( + address = common.pending_token_authority.pending_authority @ NTTError::InvalidPendingTokenAuthority + )] + pub new_authority: Signer<'info>, +} + +pub fn claim_token_authority(ctx: Context) -> 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)] @@ -267,7 +473,7 @@ pub fn register_transceiver(ctx: Context) -> Result<()> { #[derive(Accounts)] pub struct SetOutboundLimit<'info> { #[account( - constraint = config.owner == owner.key() + has_one = owner, )] pub config: Account<'info, Config>, @@ -294,7 +500,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>, diff --git a/solana/programs/example-native-token-transfers/src/lib.rs b/solana/programs/example-native-token-transfers/src/lib.rs index 1cf9cdf4b..5ad846e7a 100644 --- a/solana/programs/example-native-token-transfers/src/lib.rs +++ b/solana/programs/example-native-token-transfers/src/lib.rs @@ -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; @@ -126,6 +127,28 @@ pub mod example_native_token_transfers { instructions::claim_ownership(ctx) } + pub fn accept_token_authority(ctx: Context) -> Result<()> { + instructions::accept_token_authority(ctx) + } + + pub fn set_token_authority(ctx: Context) -> Result<()> { + instructions::set_token_authority(ctx) + } + + pub fn set_token_authority_one_step_unchecked( + ctx: Context, + ) -> Result<()> { + instructions::set_token_authority_one_step_unchecked(ctx) + } + + pub fn revert_token_authority(ctx: Context) -> Result<()> { + instructions::revert_token_authority(ctx) + } + + pub fn claim_token_authority(ctx: Context) -> Result<()> { + instructions::claim_token_authority(ctx) + } + pub fn set_paused(ctx: Context, pause: bool) -> Result<()> { instructions::set_paused(ctx, pause) } diff --git a/solana/programs/example-native-token-transfers/src/pending_token_authority.rs b/solana/programs/example-native-token-transfers/src/pending_token_authority.rs new file mode 100644 index 000000000..0f70beddd --- /dev/null +++ b/solana/programs/example-native-token-transfers/src/pending_token_authority.rs @@ -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"; +} diff --git a/solana/ts/idl/3_0_0/json/example_native_token_transfers.json b/solana/ts/idl/3_0_0/json/example_native_token_transfers.json index cdc046fa1..fd11bb705 100644 --- a/solana/ts/idl/3_0_0/json/example_native_token_transfers.json +++ b/solana/ts/idl/3_0_0/json/example_native_token_transfers.json @@ -719,6 +719,231 @@ ], "args": [] }, + { + "name": "acceptTokenAuthority", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "currentAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "setTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": true + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "setTokenAuthorityOneStepUnchecked", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "revertTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": false + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "claimTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": false + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, { "name": "setPaused", "accounts": [ @@ -1376,6 +1601,26 @@ ] } }, + { + "name": "PendingTokenAuthority", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "pendingAuthority", + "type": "publicKey" + }, + { + "name": "rentPayer", + "type": "publicKey" + } + ] + } + }, { "name": "InboxItem", "type": { @@ -2019,6 +2264,21 @@ "code": 6023, "name": "NoRegisteredTransceivers", "msg": "NoRegisteredTransceivers" + }, + { + "code": 6024, + "name": "NotPaused", + "msg": "NotPaused" + }, + { + "code": 6025, + "name": "InvalidPendingTokenAuthority", + "msg": "InvalidPendingTokenAuthority" + }, + { + "code": 6026, + "name": "IncorrectRentPayer", + "msg": "IncorrectRentPayer" } ] } diff --git a/solana/ts/idl/3_0_0/ts/example_native_token_transfers.ts b/solana/ts/idl/3_0_0/ts/example_native_token_transfers.ts index 6c8a7fce3..c098e6e5f 100644 --- a/solana/ts/idl/3_0_0/ts/example_native_token_transfers.ts +++ b/solana/ts/idl/3_0_0/ts/example_native_token_transfers.ts @@ -719,6 +719,231 @@ export type ExampleNativeTokenTransfers = { ], "args": [] }, + { + "name": "acceptTokenAuthority", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "currentAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "setTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": true + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "setTokenAuthorityOneStepUnchecked", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "revertTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": false + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "claimTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": false + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, { "name": "setPaused", "accounts": [ @@ -1376,6 +1601,26 @@ export type ExampleNativeTokenTransfers = { ] } }, + { + "name": "pendingTokenAuthority", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "pendingAuthority", + "type": "publicKey" + }, + { + "name": "rentPayer", + "type": "publicKey" + } + ] + } + }, { "name": "inboxItem", "type": { @@ -2019,6 +2264,21 @@ export type ExampleNativeTokenTransfers = { "code": 6023, "name": "NoRegisteredTransceivers", "msg": "NoRegisteredTransceivers" + }, + { + "code": 6024, + "name": "NotPaused", + "msg": "NotPaused" + }, + { + "code": 6025, + "name": "InvalidPendingTokenAuthority", + "msg": "InvalidPendingTokenAuthority" + }, + { + "code": 6026, + "name": "IncorrectRentPayer", + "msg": "IncorrectRentPayer" } ] } @@ -2743,6 +3003,231 @@ export const IDL: ExampleNativeTokenTransfers = { ], "args": [] }, + { + "name": "acceptTokenAuthority", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "currentAuthority", + "isMut": false, + "isSigner": true + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "setTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": true + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "setTokenAuthorityOneStepUnchecked", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + } + ], + "args": [] + }, + { + "name": "revertTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": false + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "owner", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, + { + "name": "claimTokenAuthority", + "accounts": [ + { + "name": "common", + "accounts": [ + { + "name": "config", + "isMut": false, + "isSigner": false + }, + { + "name": "mint", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenAuthority", + "isMut": false, + "isSigner": false + }, + { + "name": "rentPayer", + "isMut": true, + "isSigner": false + }, + { + "name": "pendingTokenAuthority", + "isMut": true, + "isSigner": false + }, + { + "name": "tokenProgram", + "isMut": false, + "isSigner": false + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false + } + ] + }, + { + "name": "newAuthority", + "isMut": false, + "isSigner": true + } + ], + "args": [] + }, { "name": "setPaused", "accounts": [ @@ -3400,6 +3885,26 @@ export const IDL: ExampleNativeTokenTransfers = { ] } }, + { + "name": "pendingTokenAuthority", + "type": { + "kind": "struct", + "fields": [ + { + "name": "bump", + "type": "u8" + }, + { + "name": "pendingAuthority", + "type": "publicKey" + }, + { + "name": "rentPayer", + "type": "publicKey" + } + ] + } + }, { "name": "inboxItem", "type": { @@ -4043,6 +4548,21 @@ export const IDL: ExampleNativeTokenTransfers = { "code": 6023, "name": "NoRegisteredTransceivers", "msg": "NoRegisteredTransceivers" + }, + { + "code": 6024, + "name": "NotPaused", + "msg": "NotPaused" + }, + { + "code": 6025, + "name": "InvalidPendingTokenAuthority", + "msg": "InvalidPendingTokenAuthority" + }, + { + "code": 6026, + "name": "IncorrectRentPayer", + "msg": "IncorrectRentPayer" } ] } diff --git a/solana/ts/lib/ntt.ts b/solana/ts/lib/ntt.ts index c3507c1c4..147b9a0d3 100644 --- a/solana/ts/lib/ntt.ts +++ b/solana/ts/lib/ntt.ts @@ -97,6 +97,8 @@ export namespace NTT { derivePda("outbox_rate_limit", programId); const tokenAuthority = (): PublicKey => derivePda("token_authority", programId); + const pendingTokenAuthority = (): PublicKey => + derivePda("pending_token_authority", programId); const peerAccount = (chain: Chain): PublicKey => derivePda(["peer", chainToBytes(chain)], programId); const registeredTransceiver = (transceiver: PublicKey): PublicKey => @@ -131,6 +133,7 @@ export namespace NTT { inboxItemAccount, sessionAuthority, tokenAuthority, + pendingTokenAuthority, peerAccount, registeredTransceiver, lutAccount, @@ -251,7 +254,7 @@ export namespace NTT { pdas = pdas ?? NTT.pdas(program.programId); const limit = new BN(args.outboundLimit.toString()); - return await program.methods + return program.methods .initialize({ chainId, limit: limit, mode }) .accountsStrict({ payer: args.payer, @@ -358,7 +361,7 @@ export namespace NTT { } } - return await program.methods + return program.methods .initializeLut(new BN(slot)) .accountsStrict({ payer: args.payer, @@ -701,7 +704,7 @@ export namespace NTT { pdas?: Pdas ) { pdas = pdas ?? NTT.pdas(program.programId); - return await program.methods + return program.methods .transferOwnership() .accounts({ config: pdas.configAccount(), @@ -710,6 +713,82 @@ export namespace NTT { .instruction(); } + export async function createAcceptTokenAuthorityInstruction( + program: Program>, + config: NttBindings.Config, + args: { + currentAuthority: PublicKey; + }, + pdas?: Pdas + ) { + pdas = pdas ?? NTT.pdas(program.programId); + return program.methods + .acceptTokenAuthority() + .accountsStrict({ + config: pdas.configAccount(), + mint: config.mint, + tokenProgram: config.tokenProgram, + tokenAuthority: pdas.tokenAuthority(), + currentAuthority: args.currentAuthority, + }) + .instruction(); + } + + export async function createSetTokenAuthorityInstruction( + program: Program>, + config: NttBindings.Config, + args: { + rentPayer: PublicKey; + owner: PublicKey; + newAuthority: PublicKey; + }, + pdas?: Pdas + ) { + pdas = pdas ?? NTT.pdas(program.programId); + return program.methods + .setTokenAuthority() + .accountsStrict({ + common: { + config: pdas.configAccount(), + tokenAuthority: pdas.tokenAuthority(), + mint: config.mint, + owner: args.owner, + newAuthority: args.newAuthority, + }, + rentPayer: args.rentPayer, + pendingTokenAuthority: pdas.pendingTokenAuthority(), + systemProgram: SystemProgram.programId, + }) + .instruction(); + } + + export async function createRevertTokenAuthorityInstruction( + program: Program>, + config: NttBindings.Config, + args: { + rentPayer: PublicKey; + owner: PublicKey; + }, + pdas?: Pdas + ) { + pdas = pdas ?? NTT.pdas(program.programId); + return program.methods + .revertTokenAuthority() + .accountsStrict({ + common: { + config: pdas.configAccount(), + mint: config.mint, + tokenAuthority: pdas.tokenAuthority(), + tokenProgram: config.tokenProgram, + systemProgram: SystemProgram.programId, + rentPayer: args.rentPayer, + pendingTokenAuthority: pdas.pendingTokenAuthority(), + }, + owner: args.owner, + }) + .instruction(); + } + export async function createSetPeerInstruction( program: Program>, args: { @@ -723,7 +802,7 @@ export namespace NTT { pdas?: Pdas ) { pdas = pdas ?? NTT.pdas(program.programId); - return await program.methods + return program.methods .setPeer({ chainId: { id: toChainId(args.chain) }, address: Array.from(args.address), @@ -750,7 +829,7 @@ export namespace NTT { pdas?: Pdas ) { pdas = pdas ?? NTT.pdas(program.programId); - return await program.methods + return program.methods .setPaused(args.paused) .accountsStrict({ owner: args.owner, @@ -768,7 +847,7 @@ export namespace NTT { pdas?: Pdas ) { pdas = pdas ?? NTT.pdas(program.programId); - return await program.methods + return program.methods .setOutboundLimit({ limit: args.limit, }) @@ -790,7 +869,7 @@ export namespace NTT { pdas?: Pdas ) { pdas = pdas ?? NTT.pdas(program.programId); - return await program.methods + return program.methods .setInboundLimit({ chainId: { id: toChainId(args.chain) }, limit: args.limit, @@ -855,7 +934,7 @@ export namespace NTT { program: Program>, pdas: Pdas ): Promise> { - return await program.account.config.fetch(pdas.configAccount()); + return program.account.config.fetch(pdas.configAccount()); } export async function getInboxItem( @@ -863,7 +942,7 @@ export namespace NTT { fromChain: Chain, nttMessage: Ntt.Message ): Promise> { - return await program.account.inboxItem.fetch( + return program.account.inboxItem.fetch( NTT.pdas(program.programId).inboxItemAccount(fromChain, nttMessage) ); }