diff --git a/cross-chain/solana/programs/tbtc/src/lib.rs b/cross-chain/solana/programs/tbtc/src/lib.rs index 94a987451..5accd9bc8 100644 --- a/cross-chain/solana/programs/tbtc/src/lib.rs +++ b/cross-chain/solana/programs/tbtc/src/lib.rs @@ -29,16 +29,16 @@ pub mod tbtc { processor::add_minter(ctx) } - pub fn remove_minter(ctx: Context, minter: Pubkey) -> Result<()> { - processor::remove_minter(ctx, minter) + pub fn remove_minter(ctx: Context) -> Result<()> { + processor::remove_minter(ctx) } pub fn add_guardian(ctx: Context) -> Result<()> { processor::add_guardian(ctx) } - pub fn remove_guardian(ctx: Context, guardian: Pubkey) -> Result<()> { - processor::remove_guardian(ctx, guardian) + pub fn remove_guardian(ctx: Context) -> Result<()> { + processor::remove_guardian(ctx) } pub fn pause(ctx: Context) -> Result<()> { diff --git a/cross-chain/solana/programs/tbtc/src/processor/admin/add_guardian.rs b/cross-chain/solana/programs/tbtc/src/processor/admin/add_guardian.rs index cd79f2d5e..422bac905 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/admin/add_guardian.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/admin/add_guardian.rs @@ -1,6 +1,6 @@ use crate::{ error::TbtcError, - state::{GuardianInfo, Tbtc}, + state::{Config, GuardianInfo}, }; use anchor_lang::prelude::*; @@ -8,30 +8,36 @@ use anchor_lang::prelude::*; pub struct AddGuardian<'info> { #[account( mut, + seeds = [Config::SEED_PREFIX], + bump, has_one = authority @ TbtcError::IsNotAuthority )] - pub tbtc: Account<'info, Tbtc>, - pub authority: Signer<'info>, - /// CHECK: the guardian does not need to sign - pub guardian: UncheckedAccount<'info>, + config: Account<'info, Config>, + #[account(mut)] - pub payer: Signer<'info>, + authority: Signer<'info>, + #[account( init, - payer = payer, - space = GuardianInfo::MAXIMUM_SIZE, - seeds = [GuardianInfo::SEED_PREFIX, tbtc.key().as_ref(), guardian.key().as_ref()], bump + payer = authority, + space = 8 + GuardianInfo::INIT_SPACE, + seeds = [GuardianInfo::SEED_PREFIX, guardian.key().as_ref()], + bump )] - pub guardian_info: Account<'info, GuardianInfo>, - pub system_program: Program<'info, System>, + guardian_info: Account<'info, GuardianInfo>, + + /// CHECK: Required authority to pause contract. This pubkey lives in `GuardianInfo`. + guardian: AccountInfo<'info>, + + system_program: Program<'info, System>, } pub fn add_guardian(ctx: Context) -> Result<()> { ctx.accounts.guardian_info.set_inner(GuardianInfo { guardian: ctx.accounts.guardian.key(), - bump: *ctx.bumps.get("guardian_info").unwrap(), + bump: ctx.bumps["guardian_info"], }); - ctx.accounts.tbtc.guardians += 1; + ctx.accounts.config.num_guardians += 1; Ok(()) } diff --git a/cross-chain/solana/programs/tbtc/src/processor/admin/add_minter.rs b/cross-chain/solana/programs/tbtc/src/processor/admin/add_minter.rs index f263dafe6..ccb8620cb 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/admin/add_minter.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/admin/add_minter.rs @@ -1,6 +1,6 @@ use crate::{ error::TbtcError, - state::{MinterInfo, Tbtc}, + state::{Config, MinterInfo}, }; use anchor_lang::prelude::*; @@ -8,30 +8,36 @@ use anchor_lang::prelude::*; pub struct AddMinter<'info> { #[account( mut, + seeds = [Config::SEED_PREFIX], + bump, has_one = authority @ TbtcError::IsNotAuthority )] - pub tbtc: Account<'info, Tbtc>, - pub authority: Signer<'info>, - /// CHECK: the minter does not need to sign - pub minter: UncheckedAccount<'info>, + config: Account<'info, Config>, + #[account(mut)] - pub payer: Signer<'info>, + authority: Signer<'info>, + #[account( init, - payer = payer, - space = MinterInfo::MAXIMUM_SIZE, - seeds = [MinterInfo::SEED_PREFIX, tbtc.key().as_ref(), minter.key().as_ref()], bump + payer = authority, + space = 8 + MinterInfo::INIT_SPACE, + seeds = [MinterInfo::SEED_PREFIX, minter.key().as_ref()], + bump )] - pub minter_info: Account<'info, MinterInfo>, - pub system_program: Program<'info, System>, + minter_info: Account<'info, MinterInfo>, + + /// CHECK: Required authority to mint tokens. This pubkey lives in `MinterInfo`. + minter: AccountInfo<'info>, + + system_program: Program<'info, System>, } pub fn add_minter(ctx: Context) -> Result<()> { ctx.accounts.minter_info.set_inner(MinterInfo { minter: ctx.accounts.minter.key(), - bump: *ctx.bumps.get("minter_info").unwrap(), + bump: ctx.bumps["minter_info"], }); - ctx.accounts.tbtc.minters += 1; + ctx.accounts.config.num_minters += 1; Ok(()) } diff --git a/cross-chain/solana/programs/tbtc/src/processor/admin/change_authority.rs b/cross-chain/solana/programs/tbtc/src/processor/admin/change_authority.rs index 2526c88f7..d2d0d1669 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/admin/change_authority.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/admin/change_authority.rs @@ -1,20 +1,22 @@ -use crate::{error::TbtcError, state::Tbtc}; +use crate::{error::TbtcError, state::Config}; use anchor_lang::prelude::*; #[derive(Accounts)] pub struct ChangeAuthority<'info> { #[account( mut, + seeds = [Config::SEED_PREFIX], + bump, has_one = authority @ TbtcError::IsNotAuthority )] - pub tbtc: Account<'info, Tbtc>, - pub authority: Signer<'info>, - #[account(mut)] - pub payer: Signer<'info>, - pub new_authority: Signer<'info>, + config: Account<'info, Config>, + + authority: Signer<'info>, + + new_authority: Signer<'info>, } pub fn change_authority(ctx: Context) -> Result<()> { - ctx.accounts.tbtc.authority = ctx.accounts.new_authority.key(); + ctx.accounts.config.authority = ctx.accounts.new_authority.key(); Ok(()) } diff --git a/cross-chain/solana/programs/tbtc/src/processor/admin/initialize.rs b/cross-chain/solana/programs/tbtc/src/processor/admin/initialize.rs index d4feff73b..9b418ddfd 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/admin/initialize.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/admin/initialize.rs @@ -1,4 +1,4 @@ -use crate::{constants::SEED_PREFIX_TBTC_MINT, state::Tbtc}; +use crate::{constants::SEED_PREFIX_TBTC_MINT, state::Config}; use anchor_lang::prelude::*; use anchor_spl::token; @@ -8,31 +8,38 @@ pub struct Initialize<'info> { // so we can sign for it from the program #[account( init, - seeds = [SEED_PREFIX_TBTC_MINT, tbtc.key().as_ref()], + seeds = [SEED_PREFIX_TBTC_MINT], bump, payer = authority, mint::decimals = 9, - mint::authority = tbtc_mint, + mint::authority = config, )] - pub tbtc_mint: Account<'info, token::Mint>, + mint: Account<'info, token::Mint>, - #[account(init, payer = authority, space = Tbtc::MAXIMUM_SIZE)] - pub tbtc: Account<'info, Tbtc>, + #[account( + init, + payer = authority, + space = 8 + Config::INIT_SPACE, + seeds = [Config::SEED_PREFIX], + bump, + )] + config: Account<'info, Config>, #[account(mut)] - pub authority: Signer<'info>, + authority: Signer<'info>, - pub token_program: Program<'info, token::Token>, - pub system_program: Program<'info, System>, + token_program: Program<'info, token::Token>, + system_program: Program<'info, System>, } pub fn initialize(ctx: Context) -> Result<()> { - ctx.accounts.tbtc.set_inner(Tbtc { + ctx.accounts.config.set_inner(Config { + bump: ctx.bumps["config"], authority: ctx.accounts.authority.key(), - token_mint: ctx.accounts.tbtc_mint.key(), - token_bump: *ctx.bumps.get("tbtc_mint").unwrap(), - minters: 0, - guardians: 0, + mint: ctx.accounts.mint.key(), + mint_bump: ctx.bumps["mint"], + num_minters: 0, + num_guardians: 0, paused: false, }); Ok(()) diff --git a/cross-chain/solana/programs/tbtc/src/processor/admin/pause.rs b/cross-chain/solana/programs/tbtc/src/processor/admin/pause.rs index 798b2b79f..afa376f8a 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/admin/pause.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/admin/pause.rs @@ -1,6 +1,6 @@ use crate::{ error::TbtcError, - state::{GuardianInfo, Tbtc}, + state::{Config, GuardianInfo}, }; use anchor_lang::prelude::*; @@ -8,19 +8,23 @@ use anchor_lang::prelude::*; pub struct Pause<'info> { #[account( mut, - constraint = !tbtc.paused @ TbtcError::IsPaused + seeds = [Config::SEED_PREFIX], + bump, + constraint = !config.paused @ TbtcError::IsPaused )] - pub tbtc: Account<'info, Tbtc>, + config: Account<'info, Config>, + #[account( has_one = guardian, - seeds = [GuardianInfo::SEED_PREFIX, tbtc.key().as_ref(), guardian.key().as_ref()], + seeds = [GuardianInfo::SEED_PREFIX, guardian.key().as_ref()], bump = guardian_info.bump )] - pub guardian_info: Account<'info, GuardianInfo>, - pub guardian: Signer<'info>, + guardian_info: Account<'info, GuardianInfo>, + + guardian: Signer<'info>, } pub fn pause(ctx: Context) -> Result<()> { - ctx.accounts.tbtc.paused = true; + ctx.accounts.config.paused = true; Ok(()) } diff --git a/cross-chain/solana/programs/tbtc/src/processor/admin/remove_guardian.rs b/cross-chain/solana/programs/tbtc/src/processor/admin/remove_guardian.rs index 9fb2f1aea..777edf865 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/admin/remove_guardian.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/admin/remove_guardian.rs @@ -1,29 +1,33 @@ use crate::{ error::TbtcError, - state::{GuardianInfo, Tbtc}, + state::{Config, GuardianInfo}, }; use anchor_lang::prelude::*; #[derive(Accounts)] -#[instruction(guardian: Pubkey)] pub struct RemoveGuardian<'info> { #[account( mut, has_one = authority @ TbtcError::IsNotAuthority, )] - pub tbtc: Account<'info, Tbtc>, - pub authority: Signer<'info>, + config: Account<'info, Config>, + + authority: Signer<'info>, + #[account( mut, has_one = guardian, close = authority, - seeds = [GuardianInfo::SEED_PREFIX, tbtc.key().as_ref(), guardian.as_ref()], + seeds = [GuardianInfo::SEED_PREFIX, guardian.key().as_ref()], bump = guardian_info.bump, )] - pub guardian_info: Account<'info, GuardianInfo>, + guardian_info: Account<'info, GuardianInfo>, + + /// CHECK: Required authority to pause contract. This pubkey lives in `GuardianInfo`. + guardian: AccountInfo<'info>, } -pub fn remove_guardian(ctx: Context, _guardian: Pubkey) -> Result<()> { - ctx.accounts.tbtc.guardians -= 1; +pub fn remove_guardian(ctx: Context) -> Result<()> { + ctx.accounts.config.num_guardians -= 1; Ok(()) } diff --git a/cross-chain/solana/programs/tbtc/src/processor/admin/remove_minter.rs b/cross-chain/solana/programs/tbtc/src/processor/admin/remove_minter.rs index 9ed557727..83aa5a07d 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/admin/remove_minter.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/admin/remove_minter.rs @@ -1,29 +1,35 @@ use crate::{ error::TbtcError, - state::{MinterInfo, Tbtc}, + state::{Config, MinterInfo}, }; use anchor_lang::prelude::*; #[derive(Accounts)] -#[instruction(minter: Pubkey)] pub struct RemoveMinter<'info> { #[account( mut, + seeds = [Config::SEED_PREFIX], + bump, has_one = authority @ TbtcError::IsNotAuthority )] - pub tbtc: Account<'info, Tbtc>, - pub authority: Signer<'info>, + config: Account<'info, Config>, + + authority: Signer<'info>, + #[account( mut, - constraint = minter_info.minter == minter, + has_one = minter, close = authority, - seeds = [MinterInfo::SEED_PREFIX, tbtc.key().as_ref(), minter.as_ref()], + seeds = [MinterInfo::SEED_PREFIX, minter.key().as_ref()], bump = minter_info.bump, )] - pub minter_info: Account<'info, MinterInfo>, + minter_info: Account<'info, MinterInfo>, + + /// CHECK: Required authority to mint tokens. This pubkey lives in `MinterInfo`. + minter: AccountInfo<'info>, } -pub fn remove_minter(ctx: Context, _minter: Pubkey) -> Result<()> { - ctx.accounts.tbtc.minters -= 1; +pub fn remove_minter(ctx: Context) -> Result<()> { + ctx.accounts.config.num_minters -= 1; Ok(()) } diff --git a/cross-chain/solana/programs/tbtc/src/processor/admin/unpause.rs b/cross-chain/solana/programs/tbtc/src/processor/admin/unpause.rs index a8c46c759..024a20685 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/admin/unpause.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/admin/unpause.rs @@ -1,18 +1,21 @@ -use crate::{error::TbtcError, state::Tbtc}; +use crate::{error::TbtcError, state::Config}; use anchor_lang::prelude::*; #[derive(Accounts)] pub struct Unpause<'info> { #[account( mut, - constraint = tbtc.paused @ TbtcError::IsNotPaused, - has_one = authority @ TbtcError::IsNotAuthority + seeds = [Config::SEED_PREFIX], + bump, + has_one = authority @ TbtcError::IsNotAuthority, + constraint = config.paused @ TbtcError::IsNotPaused )] - pub tbtc: Account<'info, Tbtc>, - pub authority: Signer<'info>, + config: Account<'info, Config>, + + authority: Signer<'info>, } pub fn unpause(ctx: Context) -> Result<()> { - ctx.accounts.tbtc.paused = false; + ctx.accounts.config.paused = false; Ok(()) } diff --git a/cross-chain/solana/programs/tbtc/src/processor/mint/mint.rs b/cross-chain/solana/programs/tbtc/src/processor/mint/mint.rs index aaf3517a9..c32ab15e5 100644 --- a/cross-chain/solana/programs/tbtc/src/processor/mint/mint.rs +++ b/cross-chain/solana/programs/tbtc/src/processor/mint/mint.rs @@ -1,73 +1,59 @@ use crate::{ constants::SEED_PREFIX_TBTC_MINT, error::TbtcError, - state::{MinterInfo, Tbtc}, + state::{Config, MinterInfo}, }; use anchor_lang::prelude::*; -use anchor_spl::{associated_token::AssociatedToken, token}; +use anchor_spl::token; #[derive(Accounts)] pub struct Mint<'info> { // Use the correct token mint for the program. #[account( mut, - seeds = [SEED_PREFIX_TBTC_MINT, tbtc.key().as_ref()], - bump = tbtc.token_bump, - mint::decimals = 9, - mint::authority = tbtc_mint, + seeds = [SEED_PREFIX_TBTC_MINT], + bump = config.mint_bump, + mint::authority = config, )] - pub tbtc_mint: Account<'info, token::Mint>, + mint: Account<'info, token::Mint>, // Can not mint when paused. #[account( - constraint = !tbtc.paused @ TbtcError::IsPaused + constraint = !config.paused @ TbtcError::IsPaused )] - pub tbtc: Account<'info, Tbtc>, + config: Account<'info, Config>, // Require the signing minter to match a valid minter info. #[account( has_one = minter, - seeds = [MinterInfo::SEED_PREFIX, tbtc.key().as_ref(), minter.key().as_ref()], + seeds = [MinterInfo::SEED_PREFIX, minter.key().as_ref()], bump = minter_info.bump, )] - pub minter_info: Account<'info, MinterInfo>, - pub minter: Signer<'info>, + minter_info: Account<'info, MinterInfo>, + minter: Signer<'info>, // Use the associated token account for the recipient. #[account( - init_if_needed, - payer = payer, - associated_token::mint = tbtc_mint, - associated_token::authority = recipient, + mut, + token::mint = mint, )] - pub recipient_account: Account<'info, token::TokenAccount>, - /// CHECK: the recipient doesn't need to sign the mint, - /// and it doesn't conform to any specific rules. - /// Validating the recipient is the minter's responsibility. - pub recipient: UncheckedAccount<'info>, - - #[account(mut)] - pub payer: Signer<'info>, + recipient_token: Account<'info, token::TokenAccount>, - pub token_program: Program<'info, token::Token>, - pub associated_token_program: Program<'info, AssociatedToken>, - pub system_program: Program<'info, System>, + token_program: Program<'info, token::Token>, + system_program: Program<'info, System>, } pub fn mint(ctx: Context, amount: u64) -> Result<()> { - let seed_prefix = SEED_PREFIX_TBTC_MINT; - let key_seed = ctx.accounts.tbtc.key(); - let mint_bump = ctx.accounts.tbtc.token_bump; - let signer: &[&[&[u8]]] = &[&[seed_prefix, key_seed.as_ref(), &[mint_bump]]]; - - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.token_program.to_account_info(), - token::MintTo { - mint: ctx.accounts.tbtc_mint.to_account_info(), - to: ctx.accounts.recipient_account.to_account_info(), - authority: ctx.accounts.tbtc_mint.to_account_info(), - }, - signer, - ); - token::mint_to(cpi_ctx, amount) + token::mint_to( + CpiContext::new_with_signer( + ctx.accounts.token_program.to_account_info(), + token::MintTo { + mint: ctx.accounts.mint.to_account_info(), + to: ctx.accounts.recipient_token.to_account_info(), + authority: ctx.accounts.config.to_account_info(), + }, + &[&[Config::SEED_PREFIX, &[ctx.accounts.config.bump]]], + ), + amount, + ) } diff --git a/cross-chain/solana/programs/tbtc/src/state/config.rs b/cross-chain/solana/programs/tbtc/src/state/config.rs new file mode 100644 index 000000000..4dd81b69c --- /dev/null +++ b/cross-chain/solana/programs/tbtc/src/state/config.rs @@ -0,0 +1,23 @@ +use anchor_lang::prelude::*; + +#[account] +#[derive(Debug, InitSpace)] +pub struct Config { + pub bump: u8, + + /// The authority over this program. + pub authority: Pubkey, + + // Mint info. + pub mint: Pubkey, + pub mint_bump: u8, + + // Admin info. + pub num_minters: u8, + pub num_guardians: u8, + pub paused: bool, +} + +impl Config { + pub const SEED_PREFIX: &'static [u8] = b"config"; +} diff --git a/cross-chain/solana/programs/tbtc/src/state/guardian_info.rs b/cross-chain/solana/programs/tbtc/src/state/guardian_info.rs index 656b767bb..00364eac5 100644 --- a/cross-chain/solana/programs/tbtc/src/state/guardian_info.rs +++ b/cross-chain/solana/programs/tbtc/src/state/guardian_info.rs @@ -1,17 +1,12 @@ use anchor_lang::prelude::*; #[account] -#[derive(Default)] +#[derive(Debug, InitSpace)] pub struct GuardianInfo { pub guardian: Pubkey, pub bump: u8, } impl GuardianInfo { - pub fn is_guardian(&self, key: &Pubkey) -> bool { - self.guardian == *key - } - - pub const MAXIMUM_SIZE: usize = 8 + 32 + 1; // discriminator + pubkey + bump pub const SEED_PREFIX: &'static [u8; 13] = b"guardian-info"; } diff --git a/cross-chain/solana/programs/tbtc/src/state/minter_info.rs b/cross-chain/solana/programs/tbtc/src/state/minter_info.rs index 978f6ff9d..e8b6a6eeb 100644 --- a/cross-chain/solana/programs/tbtc/src/state/minter_info.rs +++ b/cross-chain/solana/programs/tbtc/src/state/minter_info.rs @@ -1,17 +1,12 @@ use anchor_lang::prelude::*; #[account] -#[derive(Default)] +#[derive(Debug, InitSpace)] pub struct MinterInfo { pub minter: Pubkey, pub bump: u8, } impl MinterInfo { - pub fn is_minter(&self, key: &Pubkey) -> bool { - self.minter == *key - } - - pub const MAXIMUM_SIZE: usize = 8 + 32 + 1; // discriminator + pubkey + bump - pub const SEED_PREFIX: &'static [u8; 11] = b"minter-info"; + pub const SEED_PREFIX: &'static [u8] = b"minter-info"; } diff --git a/cross-chain/solana/programs/tbtc/src/state/mod.rs b/cross-chain/solana/programs/tbtc/src/state/mod.rs index abfdadb81..dffd4099b 100644 --- a/cross-chain/solana/programs/tbtc/src/state/mod.rs +++ b/cross-chain/solana/programs/tbtc/src/state/mod.rs @@ -1,7 +1,8 @@ +mod config; +pub use config::*; + +mod guardian_info; pub use guardian_info::*; -pub use minter_info::*; -pub use tbtc::*; -pub mod guardian_info; -pub mod minter_info; -pub mod tbtc; +mod minter_info; +pub use minter_info::*; diff --git a/cross-chain/solana/programs/tbtc/src/state/tbtc.rs b/cross-chain/solana/programs/tbtc/src/state/tbtc.rs deleted file mode 100644 index c5a3027b7..000000000 --- a/cross-chain/solana/programs/tbtc/src/state/tbtc.rs +++ /dev/null @@ -1,23 +0,0 @@ -use anchor_lang::prelude::*; - -#[account] -#[derive(Default)] -pub struct Tbtc { - pub authority: Pubkey, - pub token_mint: Pubkey, - pub token_bump: u8, - pub minters: u8, - pub guardians: u8, - pub paused: bool, -} - -impl Tbtc { - // 8 discriminator - // 32 pubkey - // 32 pubkey - // 1 u8 - // 1 u8 - // 1 u8 - // 1 bool - pub const MAXIMUM_SIZE: usize = 8 + 32 + 32 + 1 + 1 + 1 + 1; -} diff --git a/cross-chain/solana/tests/01__tbtc.ts b/cross-chain/solana/tests/01__tbtc.ts index f5976b7e1..fad8af5da 100644 --- a/cross-chain/solana/tests/01__tbtc.ts +++ b/cross-chain/solana/tests/01__tbtc.ts @@ -4,6 +4,7 @@ import * as spl from "@solana/spl-token"; import * as web3 from '@solana/web3.js'; import { Tbtc } from "../target/types/tbtc"; import { expect } from 'chai'; +import { ASSOCIATED_PROGRAM_ID } from "@coral-xyz/anchor/dist/cjs/utils/token"; function maybeAuthorityAnd( signer, @@ -14,53 +15,52 @@ function maybeAuthorityAnd( async function setup( program: Program, - tbtc, authority ) { - const [tbtcMintPDA, _] = getTokenPDA(program, tbtc); - + const [config,] = getConfigPDA(program); + const [tbtcMintPDA, _] = getTokenPDA(program); + await program.methods .initialize() .accounts({ - tbtcMint: tbtcMintPDA, - tbtc: tbtc.publicKey, + mint: tbtcMintPDA, + config, authority: authority.publicKey }) - .signers([tbtc]) .rpc(); } async function checkState( program: Program, - tbtc, expectedAuthority, expectedMinters, expectedGuardians, expectedTokensSupply ) { - let tbtcState = await program.account.tbtc.fetch(tbtc.publicKey); + const [config,] = getConfigPDA(program); + let configState = await program.account.config.fetch(config); - expect(tbtcState.authority).to.eql(expectedAuthority.publicKey); - expect(tbtcState.minters).to.equal(expectedMinters); - expect(tbtcState.guardians).to.equal(expectedGuardians); + expect(configState.authority).to.eql(expectedAuthority.publicKey); + expect(configState.numMinters).to.equal(expectedMinters); + expect(configState.numGuardians).to.equal(expectedGuardians); - let tbtcMint = tbtcState.tokenMint; + let tbtcMint = configState.mint; let mintState = await spl.getMint(program.provider.connection, tbtcMint); - expect(mintState.supply == expectedTokensSupply).to.be.true; + expect(mintState.supply).to.equal(BigInt(expectedTokensSupply)); } async function changeAuthority( program: Program, - tbtc, authority, newAuthority, ) { + const [config,] = getConfigPDA(program); await program.methods .changeAuthority() .accounts({ - tbtc: tbtc.publicKey, + config, authority: authority.publicKey, newAuthority: newAuthority.publicKey, }) @@ -70,21 +70,31 @@ async function changeAuthority( async function checkPaused( program: Program, - tbtc, paused: boolean ) { - let tbtcState = await program.account.tbtc.fetch(tbtc.publicKey); - expect(tbtcState.paused).to.equal(paused); + const [config,] = getConfigPDA(program); + let configState = await program.account.config.fetch(config); + expect(configState.paused).to.equal(paused); +} + + +function getConfigPDA( + program: Program, +): [anchor.web3.PublicKey, number] { + return web3.PublicKey.findProgramAddressSync( + [ + Buffer.from('config'), + ], + program.programId + ); } function getTokenPDA( program: Program, - tbtc, ): [anchor.web3.PublicKey, number] { return web3.PublicKey.findProgramAddressSync( [ - anchor.utils.bytes.utf8.encode('tbtc-mint'), - tbtc.publicKey.toBuffer(), + Buffer.from('tbtc-mint'), ], program.programId ); @@ -92,13 +102,11 @@ function getTokenPDA( function getMinterPDA( program: Program, - tbtc, minter ): [anchor.web3.PublicKey, number] { return web3.PublicKey.findProgramAddressSync( [ - anchor.utils.bytes.utf8.encode('minter-info'), - tbtc.publicKey.toBuffer(), + Buffer.from('minter-info'), minter.publicKey.toBuffer(), ], program.programId @@ -107,19 +115,18 @@ function getMinterPDA( async function addMinter( program: Program, - tbtc, authority, minter, payer ): Promise { - const [minterInfoPDA, _] = getMinterPDA(program, tbtc, minter); + const [config,] = getConfigPDA(program); + const [minterInfoPDA, _] = getMinterPDA(program, minter); await program.methods .addMinter() .accounts({ - tbtc: tbtc.publicKey, + config, authority: authority.publicKey, minter: minter.publicKey, - payer: payer.publicKey, minterInfo: minterInfoPDA, }) .signers(maybeAuthorityAnd(authority, [])) @@ -129,10 +136,9 @@ async function addMinter( async function checkMinter( program: Program, - tbtc, minter ) { - const [minterInfoPDA, bump] = getMinterPDA(program, tbtc, minter); + const [minterInfoPDA, bump] = getMinterPDA(program, minter); let minterInfo = await program.account.minterInfo.fetch(minterInfoPDA); expect(minterInfo.minter).to.eql(minter.publicKey); @@ -141,17 +147,18 @@ async function checkMinter( async function removeMinter( program: Program, - tbtc, authority, minter, minterInfo ) { + const [config,] = getConfigPDA(program); await program.methods - .removeMinter(minter.publicKey) + .removeMinter() .accounts({ - tbtc: tbtc.publicKey, + config, authority: authority.publicKey, minterInfo: minterInfo, + minter: minter.publicKey }) .signers(maybeAuthorityAnd(authority, [])) .rpc(); @@ -159,13 +166,11 @@ async function removeMinter( function getGuardianPDA( program: Program, - tbtc, guardian ): [anchor.web3.PublicKey, number] { return web3.PublicKey.findProgramAddressSync( [ - anchor.utils.bytes.utf8.encode('guardian-info'), - tbtc.publicKey.toBuffer(), + Buffer.from('guardian-info'), guardian.publicKey.toBuffer(), ], program.programId @@ -174,20 +179,19 @@ function getGuardianPDA( async function addGuardian( program: Program, - tbtc, authority, guardian, payer ): Promise { - const [guardianInfoPDA, _] = getGuardianPDA(program, tbtc, guardian); + const [config,] = getConfigPDA(program); + const [guardianInfoPDA, _] = getGuardianPDA(program, guardian); await program.methods .addGuardian() .accounts({ - tbtc: tbtc.publicKey, + config, authority: authority.publicKey, - guardian: guardian.publicKey, - payer: payer.publicKey, guardianInfo: guardianInfoPDA, + guardian: guardian.publicKey, }) .signers(maybeAuthorityAnd(authority, [])) .rpc(); @@ -196,10 +200,9 @@ async function addGuardian( async function checkGuardian( program: Program, - tbtc, guardian ) { - const [guardianInfoPDA, bump] = getGuardianPDA(program, tbtc, guardian); + const [guardianInfoPDA, bump] = getGuardianPDA(program, guardian); let guardianInfo = await program.account.guardianInfo.fetch(guardianInfoPDA); expect(guardianInfo.guardian).to.eql(guardian.publicKey); @@ -208,17 +211,18 @@ async function checkGuardian( async function removeGuardian( program: Program, - tbtc, authority, guardian, guardianInfo ) { + const [config,] = getConfigPDA(program); await program.methods - .removeGuardian(guardian.publicKey) + .removeGuardian() .accounts({ - tbtc: tbtc.publicKey, + config, authority: authority.publicKey, guardianInfo: guardianInfo, + guardian: guardian.publicKey }) .signers(maybeAuthorityAnd(authority, [])) .rpc(); @@ -226,14 +230,14 @@ async function removeGuardian( async function pause( program: Program, - tbtc, guardian ) { - const [guardianInfoPDA, _] = getGuardianPDA(program, tbtc, guardian); + const [config,] = getConfigPDA(program); + const [guardianInfoPDA, _] = getGuardianPDA(program, guardian); await program.methods .pause() .accounts({ - tbtc: tbtc.publicKey, + config, guardianInfo: guardianInfoPDA, guardian: guardian.publicKey }) @@ -243,13 +247,13 @@ async function pause( async function unpause( program: Program, - tbtc, authority ) { + const [config,] = getConfigPDA(program); await program.methods .unpause() .accounts({ - tbtc: tbtc.publicKey, + config, authority: authority.publicKey }) .signers(maybeAuthorityAnd(authority, [])) @@ -258,29 +262,50 @@ async function unpause( async function mint( program: Program, - tbtc, minter, minterInfoPDA, recipient, amount, payer, ) { - const [tbtcMintPDA, _] = getTokenPDA(program, tbtc); - const associatedTokenAccount = spl.getAssociatedTokenAddressSync( - tbtcMintPDA, - recipient.publicKey, - ); + const connection = program.provider.connection; + + const [config,] = getConfigPDA(program); + const [tbtcMintPDA, _] = getTokenPDA(program); + const recipientToken = spl.getAssociatedTokenAddressSync(tbtcMintPDA, recipient.publicKey); + + const tokenData = await spl.getAccount(connection, recipientToken).catch((err) => { + if (err instanceof spl.TokenAccountNotFoundError) { + return null; + } else { + throw err; + }; + }); + + if (tokenData === null) { + const tx = await web3.sendAndConfirmTransaction( + connection, + new web3.Transaction().add( + spl.createAssociatedTokenAccountIdempotentInstruction( + payer.publicKey, + recipientToken, + recipient.publicKey, + tbtcMintPDA, + ) + ), + [payer.payer] + ); + } + await program.methods .mint(new anchor.BN(amount)) .accounts({ - tbtcMint: tbtcMintPDA, - tbtc: tbtc.publicKey, + mint: tbtcMintPDA, + config, minterInfo: minterInfoPDA, minter: minter.publicKey, - recipientAccount: associatedTokenAccount, - recipient: recipient.publicKey, - payer: payer.publicKey, + recipientToken, }) .signers(maybeAuthorityAnd(payer, [minter])) .rpc(); @@ -292,8 +317,7 @@ describe("tbtc", () => { const program = anchor.workspace.Tbtc as Program; - - const authority = (program.provider as anchor.AnchorProvider).wallet; + const authority = (program.provider as anchor.AnchorProvider).wallet as anchor.Wallet; const newAuthority = anchor.web3.Keypair.generate(); const minterKeys = anchor.web3.Keypair.generate(); const minter2Keys = anchor.web3.Keypair.generate(); @@ -304,29 +328,39 @@ describe("tbtc", () => { const recipientKeys = anchor.web3.Keypair.generate(); it('setup', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); + await setup(program, authority); + await checkState(program, authority, 0, 0, 0); }); it('change authority', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - await changeAuthority(program, tbtcKeys, authority, newAuthority); - await checkState(program, tbtcKeys, newAuthority, 0, 0, 0); + await checkState(program, authority, 0, 0, 0); + await changeAuthority(program, authority, newAuthority); + await checkState(program, newAuthority, 0, 0, 0); + await changeAuthority(program, newAuthority, authority.payer); + await checkState(program, authority, 0, 0, 0); }) it('add minter', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - await addMinter(program, tbtcKeys, authority, minterKeys, authority); - await checkMinter(program, tbtcKeys, minterKeys); - await checkState(program, tbtcKeys, authority, 1, 0, 0); + await checkState(program, authority, 0, 0, 0); + await addMinter(program, authority, minterKeys, authority); + await checkMinter(program, minterKeys); + await checkState(program, authority, 1, 0, 0); + + // Transfer lamports to imposter. + await web3.sendAndConfirmTransaction( + program.provider.connection, + new web3.Transaction().add( + web3.SystemProgram.transfer({ + fromPubkey: authority.publicKey, + toPubkey: impostorKeys.publicKey, + lamports: 1000000000, + }) + ), + [authority.payer] + ); try { - await addMinter(program, tbtcKeys, impostorKeys, minter2Keys, authority); + await addMinter(program, impostorKeys, minter2Keys, authority); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -337,31 +371,35 @@ describe("tbtc", () => { }); it('mint', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - const minterInfoPDA = await addMinter(program, tbtcKeys, authority, minterKeys, authority); - await checkMinter(program, tbtcKeys, minterKeys); - await checkState(program, tbtcKeys, authority, 1, 0, 0); + await checkState(program, authority, 1, 0, 0); + const [minterInfoPDA, _] = getMinterPDA(program, minterKeys); + await checkMinter(program, minterKeys); - // await setupMint(program, tbtcKeys, authority, recipientKeys); - await mint(program, tbtcKeys, minterKeys, minterInfoPDA, recipientKeys, 1000, authority); + // await setupMint(program, authority, recipientKeys); + await mint(program, minterKeys, minterInfoPDA, recipientKeys, 1000, authority); + + await checkState(program, authority, 1, 0, 1000); + + // // Burn for next test. + // const ix = spl.createBurnCheckedInstruction( + // account, // PublicKey of Owner's Associated Token Account + // new PublicKey(MINT_ADDRESS), // Public Key of the Token Mint Address + // WALLET.publicKey, // Public Key of Owner's Wallet + // BURN_QUANTITY * (10**MINT_DECIMALS), // Number of tokens to burn + // MINT_DECIMALS // Number of Decimals of the Token Mint + // ) - await checkState(program, tbtcKeys, authority, 1, 0, 1000); }); it('won\'t mint', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - const minterInfoPDA = await addMinter(program, tbtcKeys, authority, minterKeys, authority); - await checkMinter(program, tbtcKeys, minterKeys); - await checkState(program, tbtcKeys, authority, 1, 0, 0); + await checkState(program, authority, 1, 0, 1000); + const [minterInfoPDA, _] = getMinterPDA(program, minterKeys); + await checkMinter(program, minterKeys); - // await setupMint(program, tbtcKeys, authority, recipientKeys); + // await setupMint(program, authority, recipientKeys); try { - await mint(program, tbtcKeys, impostorKeys, minterInfoPDA, recipientKeys, 1000, authority); + await mint(program, impostorKeys, minterInfoPDA, recipientKeys, 1000, authority); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -372,19 +410,17 @@ describe("tbtc", () => { }); it('use two minters', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - const minterInfoPDA = await addMinter(program, tbtcKeys, authority, minterKeys, authority); - const minter2InfoPDA = await addMinter(program, tbtcKeys, authority, minter2Keys, authority); - await checkMinter(program, tbtcKeys, minterKeys); - await checkMinter(program, tbtcKeys, minter2Keys); - await checkState(program, tbtcKeys, authority, 2, 0, 0); - // await setupMint(program, tbtcKeys, authority, recipientKeys); + await checkState(program, authority, 1, 0, 1000); + const [minterInfoPDA, _] = getMinterPDA(program, minterKeys); + await checkMinter(program, minterKeys); + const minter2InfoPDA = await addMinter(program, authority, minter2Keys, authority); + await checkMinter(program, minter2Keys); + await checkState(program, authority, 2, 0, 1000); + // await setupMint(program, authority, recipientKeys); // cannot mint with wrong keys try { - await mint(program, tbtcKeys, minter2Keys, minterInfoPDA, recipientKeys, 1000, authority); + await mint(program, minter2Keys, minterInfoPDA, recipientKeys, 1000, authority); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -395,7 +431,7 @@ describe("tbtc", () => { // cannot remove minter with wrong keys try { - await removeMinter(program, tbtcKeys, authority, minter2Keys, minterInfoPDA); + await removeMinter(program, authority, minter2Keys, minterInfoPDA); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -404,31 +440,26 @@ describe("tbtc", () => { expect(err.program.equals(program.programId)).is.true; } - await mint(program, tbtcKeys, minterKeys, minterInfoPDA, recipientKeys, 500, authority); - await checkState(program, tbtcKeys, authority, 2, 0, 500); + await mint(program, minterKeys, minterInfoPDA, recipientKeys, 500, authority); + await checkState(program, authority, 2, 0, 1500); }); it('remove minter', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - const minterInfoPDA = await addMinter(program, tbtcKeys, authority, minterKeys, authority); - await checkMinter(program, tbtcKeys, minterKeys); - await checkState(program, tbtcKeys, authority, 1, 0, 0); - await removeMinter(program, tbtcKeys, authority, minterKeys, minterInfoPDA); - await checkState(program, tbtcKeys, authority, 0, 0, 0); + await checkState(program, authority, 2, 0, 1500); + const [minter2InfoPDA, _] = getMinterPDA(program, minter2Keys); + await checkMinter(program, minter2Keys); + await removeMinter(program, authority, minter2Keys, minter2InfoPDA); + await checkState(program, authority, 1, 0, 1500); }); it('won\'t remove minter', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - const minterInfoPDA = await addMinter(program, tbtcKeys, authority, minterKeys, authority); - await checkMinter(program, tbtcKeys, minterKeys); - await checkState(program, tbtcKeys, authority, 1, 0, 0); + await checkState(program, authority, 1, 0, 1500); + const [minterInfoPDA, _] = getMinterPDA(program, minterKeys); + await checkMinter(program, minterKeys); try { - await removeMinter(program, tbtcKeys, impostorKeys, minterKeys, minterInfoPDA); + await removeMinter(program, impostorKeys, minterKeys, minterInfoPDA); + chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; @@ -436,11 +467,12 @@ describe("tbtc", () => { expect(err.program.equals(program.programId)).is.true; } - await removeMinter(program, tbtcKeys, authority, minterKeys, minterInfoPDA); - await checkState(program, tbtcKeys, authority, 0, 0, 0); + await removeMinter(program, authority, minterKeys, minterInfoPDA); + await checkState(program, authority, 0, 0, 1500); try { - await removeMinter(program, tbtcKeys, authority, minterKeys, minterInfoPDA); + await removeMinter(program, authority, minterKeys, minterInfoPDA); + chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); const err: AnchorError = _err; @@ -450,15 +482,13 @@ describe("tbtc", () => { }); it('add guardian', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - await addGuardian(program, tbtcKeys, authority, guardianKeys, authority); - await checkGuardian(program, tbtcKeys, guardianKeys); - await checkState(program, tbtcKeys, authority, 0, 1, 0); + await checkState(program, authority, 0, 0, 1500); + await addGuardian(program, authority, guardianKeys, authority); + await checkGuardian(program, guardianKeys); + await checkState(program, authority, 0, 1, 1500); try { - await addGuardian(program, tbtcKeys, impostorKeys, guardian2Keys, authority); + await addGuardian(program, impostorKeys, guardian2Keys, authority); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -469,15 +499,12 @@ describe("tbtc", () => { }); it('remove guardian', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - const guardianInfoPDA = await addGuardian(program, tbtcKeys, authority, guardianKeys, authority); - await checkGuardian(program, tbtcKeys, guardianKeys); - await checkState(program, tbtcKeys, authority, 0, 1, 0); + await checkState(program, authority, 0, 1, 1500); + const [guardianInfoPDA, _] = getGuardianPDA(program, guardianKeys); + await checkGuardian(program, guardianKeys); try { - await removeGuardian(program, tbtcKeys, impostorKeys, guardianKeys, guardianInfoPDA); + await removeGuardian(program, impostorKeys, guardianKeys, guardianInfoPDA); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -486,11 +513,11 @@ describe("tbtc", () => { expect(err.program.equals(program.programId)).is.true; } - await removeGuardian(program, tbtcKeys, authority, guardianKeys, guardianInfoPDA); - await checkState(program, tbtcKeys, authority, 0, 0, 0); + await removeGuardian(program, authority, guardianKeys, guardianInfoPDA); + await checkState(program, authority, 0, 0, 1500); try { - await removeGuardian(program, tbtcKeys, authority, guardianKeys, guardianInfoPDA); + await removeGuardian(program, authority, guardianKeys, guardianInfoPDA); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -501,26 +528,21 @@ describe("tbtc", () => { }); it('pause', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await addGuardian(program, tbtcKeys, authority, guardianKeys, authority); - await checkPaused(program, tbtcKeys, false); - await pause(program, tbtcKeys, guardianKeys); - await checkPaused(program, tbtcKeys, true); + await checkState(program, authority, 0, 0, 1500); + await addGuardian(program, authority, guardianKeys, authority); + await checkPaused(program, false); + await pause(program, guardianKeys); + await checkPaused(program, true); }); it('unpause', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await addGuardian(program, tbtcKeys, authority, guardianKeys, authority); - await checkPaused(program, tbtcKeys, false); - await pause(program, tbtcKeys, guardianKeys); - await checkPaused(program, tbtcKeys, true); - await unpause(program, tbtcKeys, authority); - await checkPaused(program, tbtcKeys, false); + await checkState(program, authority, 0, 1, 1500); + await checkPaused(program, true); + await unpause(program, authority); + await checkPaused(program, false); try { - await unpause(program, tbtcKeys, authority); + await unpause(program, authority); chai.assert(false, "should've failed but didn't"); } catch (_err) { @@ -532,16 +554,13 @@ describe("tbtc", () => { }); it('won\'t mint when paused', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - const minterInfoPDA = await addMinter(program, tbtcKeys, authority, minterKeys, authority); - await addGuardian(program, tbtcKeys, authority, guardianKeys, authority); - await pause(program, tbtcKeys, guardianKeys); - // await setupMint(program, tbtcKeys, authority, recipientKeys); + await checkState(program, authority, 0, 1, 1500); + const minterInfoPDA = await addMinter(program, authority, minterKeys, authority); + await pause(program, guardianKeys); + // await setupMint(program, authority, recipientKeys); try { - await mint(program, tbtcKeys, minterKeys, minterInfoPDA, recipientKeys, 1000, authority); - + await mint(program, minterKeys, minterInfoPDA, recipientKeys, 1000, authority); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -549,22 +568,22 @@ describe("tbtc", () => { expect(err.error.errorCode.code).to.equal('IsPaused'); expect(err.program.equals(program.programId)).is.true; } + + await unpause(program, authority); + await checkPaused(program, false); }) it('use two guardians', async () => { - const tbtcKeys = anchor.web3.Keypair.generate(); - await setup(program, tbtcKeys, authority); - await checkState(program, tbtcKeys, authority, 0, 0, 0); - const guardianInfoPDA = await addGuardian(program, tbtcKeys, authority, guardianKeys, authority); - await addGuardian(program, tbtcKeys, authority, guardian2Keys, authority); - await checkGuardian(program, tbtcKeys, guardianKeys); - await checkGuardian(program, tbtcKeys, guardian2Keys); - await checkState(program, tbtcKeys, authority, 0, 2, 0); + await checkState(program, authority, 1, 1, 1500); + const [guardianInfoPDA, _] = getGuardianPDA(program, guardianKeys); + await checkGuardian(program, guardianKeys); + await addGuardian(program, authority, guardian2Keys, authority); + await checkGuardian(program, guardian2Keys); - await pause(program, tbtcKeys, guardianKeys); + await pause(program, guardianKeys); try { - await pause(program, tbtcKeys, guardian2Keys); + await pause(program, guardian2Keys); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError); @@ -573,14 +592,14 @@ describe("tbtc", () => { expect(err.program.equals(program.programId)).is.true; } - await unpause(program, tbtcKeys, authority); - await pause(program, tbtcKeys, guardian2Keys); - await checkPaused(program, tbtcKeys, true); - await unpause(program, tbtcKeys, authority); + await unpause(program, authority); + await pause(program, guardian2Keys); + await checkPaused(program, true); + await unpause(program, authority); // cannot remove guardian with wrong keys try { - await removeGuardian(program, tbtcKeys, authority, guardian2Keys, guardianInfoPDA); + await removeGuardian(program, authority, guardian2Keys, guardianInfoPDA); chai.assert(false, "should've failed but didn't"); } catch (_err) { expect(_err).to.be.instanceOf(AnchorError);