From d0769682aed0556648bd249bc2c031efcc3ed3c2 Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 3 Jan 2025 18:36:55 +0800 Subject: [PATCH 1/5] create new instruction for renouncing freeze authority --- .../programs/oft/src/instructions/mod.rs | 2 + .../oft/src/instructions/renounce_freeze.rs | 90 ++++++++++++++++++ examples/oft-solana/programs/oft/src/lib.rs | 9 +- examples/oft-solana/tasks/index.ts | 1 + examples/oft-solana/tasks/solana/index.ts | 4 +- .../oft-solana/tasks/solana/renounceFreeze.ts | 91 +++++++++++++++++++ 6 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 examples/oft-solana/programs/oft/src/instructions/renounce_freeze.rs create mode 100644 examples/oft-solana/tasks/solana/renounceFreeze.ts diff --git a/examples/oft-solana/programs/oft/src/instructions/mod.rs b/examples/oft-solana/programs/oft/src/instructions/mod.rs index 7030177db4..0d540fe485 100644 --- a/examples/oft-solana/programs/oft/src/instructions/mod.rs +++ b/examples/oft-solana/programs/oft/src/instructions/mod.rs @@ -3,6 +3,7 @@ pub mod lz_receive; pub mod lz_receive_types; pub mod quote_oft; pub mod quote_send; +pub mod renounce_freeze; pub mod send; pub mod set_oft_config; pub mod set_pause; @@ -14,6 +15,7 @@ pub use lz_receive::*; pub use lz_receive_types::*; pub use quote_oft::*; pub use quote_send::*; +pub use renounce_freeze::*; pub use send::*; pub use set_oft_config::*; pub use set_pause::*; diff --git a/examples/oft-solana/programs/oft/src/instructions/renounce_freeze.rs b/examples/oft-solana/programs/oft/src/instructions/renounce_freeze.rs new file mode 100644 index 0000000000..df137e82f5 --- /dev/null +++ b/examples/oft-solana/programs/oft/src/instructions/renounce_freeze.rs @@ -0,0 +1,90 @@ +use crate::*; +use anchor_lang::solana_program; +use anchor_spl::token_2022::spl_token_2022; +use anchor_spl::token_2022::spl_token_2022::instruction::AuthorityType; +use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; + +#[derive(Accounts)] +#[instruction()] +pub struct RenounceFreezeAuthority<'info> { + pub signer: Signer<'info>, + #[account( + mut, + seeds = [OFT_SEED, oft_store.token_escrow.as_ref()], + bump = oft_store.bump, + constraint = is_valid_signer(signer.key(), &oft_store) @OFTError::Unauthorized + )] + pub oft_store: Account<'info, OFTStore>, + #[account( + mut, + address = oft_store.token_escrow, + token::authority = oft_store, + token::mint = token_mint, + token::token_program = token_program + )] + pub token_escrow: InterfaceAccount<'info, TokenAccount>, + + #[account( + mut, + address = oft_store.token_mint, + mint::token_program = token_program + )] + pub token_mint: InterfaceAccount<'info, Mint>, + #[account(mut)] + pub current_authority: AccountInfo<'info>, + pub token_program: Interface<'info, TokenInterface>, +} + +// +impl RenounceFreezeAuthority<'_> { + pub fn apply(ctx: Context) -> Result<()> { + let mint = &ctx.accounts.token_mint; + + // Ensure freeze authority is set + require!( + mint.freeze_authority.is_some(), + CustomError::NoFreezeAuthority + ); + + let oft_store_seed = ctx.accounts.token_escrow.key(); + let seeds: &[&[u8]] = &[ + OFT_SEED, + oft_store_seed.as_ref(), + &[ctx.accounts.oft_store.bump], + ]; + + // Create the set_authority instruction directly + let ix = spl_token_2022::instruction::set_authority( + ctx.accounts.token_program.key, + &ctx.accounts.token_mint.key(), + None, // new authority + AuthorityType::FreezeAccount, + &ctx.accounts.current_authority.key(), + &[&ctx.accounts.oft_store.key()], + )?; + + // Invoke with signing + solana_program::program::invoke_signed( + &ix, + &[ + ctx.accounts.token_mint.to_account_info(), + ctx.accounts.current_authority.to_account_info(), + ctx.accounts.oft_store.to_account_info(), + ], + &[&seeds], + )?; + + Ok(()) + } +} + +// Custom error for validation +#[error_code] +pub enum CustomError { + #[msg("No freeze authority exists on this mint.")] + NoFreezeAuthority, +} + +fn is_valid_signer(signer: Pubkey, oft_store: &OFTStore) -> bool { + oft_store.admin == signer +} diff --git a/examples/oft-solana/programs/oft/src/lib.rs b/examples/oft-solana/programs/oft/src/lib.rs index 68b54b928f..74194d899e 100644 --- a/examples/oft-solana/programs/oft/src/lib.rs +++ b/examples/oft-solana/programs/oft/src/lib.rs @@ -32,7 +32,10 @@ pub mod oft { use super::*; pub fn oft_version(_ctx: Context) -> Result { - Ok(Version { interface: 2, message: 1 }) + Ok(Version { + interface: 2, + message: 1, + }) } pub fn init_oft(mut ctx: Context, params: InitOFTParams) -> Result<()> { @@ -62,6 +65,10 @@ pub mod oft { WithdrawFee::apply(&mut ctx, ¶ms) } + pub fn renounce_freeze(ctx: Context) -> Result<()> { + RenounceFreezeAuthority::apply(ctx) + } + // ============================== Public ============================== pub fn quote_oft(ctx: Context, params: QuoteOFTParams) -> Result { diff --git a/examples/oft-solana/tasks/index.ts b/examples/oft-solana/tasks/index.ts index 9008859b38..4314aef4ab 100644 --- a/examples/oft-solana/tasks/index.ts +++ b/examples/oft-solana/tasks/index.ts @@ -5,6 +5,7 @@ import './solana/createOFT' import './solana/createOFTAdapter' import './solana/sendOFT' import './solana/setAuthority' +import './solana/renounceFreeze' import './solana/getPrioFees' import './solana/base58' import './solana/setInboundRateLimit' diff --git a/examples/oft-solana/tasks/solana/index.ts b/examples/oft-solana/tasks/solana/index.ts index 69b2c6a898..7eb7976d5d 100644 --- a/examples/oft-solana/tasks/solana/index.ts +++ b/examples/oft-solana/tasks/solana/index.ts @@ -22,7 +22,7 @@ import { } from '@metaplex-foundation/umi' import { createUmi } from '@metaplex-foundation/umi-bundle-defaults' import { createWeb3JsEddsa } from '@metaplex-foundation/umi-eddsa-web3js' -import { toWeb3JsInstruction, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters' +import { toWeb3JsInstruction, toWeb3JsKeypair, toWeb3JsPublicKey } from '@metaplex-foundation/umi-web3js-adapters' import { AddressLookupTableAccount, Connection } from '@solana/web3.js' import { getSimulationComputeUnits } from '@solana-developers/helpers' import bs58 from 'bs58' @@ -63,12 +63,14 @@ export const deriveConnection = async (eid: EndpointId) => { const umi = createUmi(connection.rpcEndpoint).use(mplToolbox()) const umiWalletKeyPair = umi.eddsa.createKeypairFromSecretKey(bs58.decode(privateKey)) const umiWalletSigner = createSignerFromKeypair(umi, umiWalletKeyPair) + const web3JsKeypair = toWeb3JsKeypair(umiWalletKeyPair) umi.use(signerIdentity(umiWalletSigner)) return { connection, umi, umiWalletKeyPair, umiWalletSigner, + web3JsKeypair, } } diff --git a/examples/oft-solana/tasks/solana/renounceFreeze.ts b/examples/oft-solana/tasks/solana/renounceFreeze.ts new file mode 100644 index 0000000000..c7852adbbb --- /dev/null +++ b/examples/oft-solana/tasks/solana/renounceFreeze.ts @@ -0,0 +1,91 @@ +import { AnchorProvider, Program } from '@coral-xyz/anchor' +import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet' +import { TOKEN_PROGRAM_ID } from '@solana/spl-token' +import { PublicKey } from '@solana/web3.js' +import { task } from 'hardhat/config' + +import { types } from '@layerzerolabs/devtools-evm-hardhat' +import { EndpointId } from '@layerzerolabs/lz-definitions' + +import IDL from '../../target/idl/oft.json' +import { Oft } from '../../target/types/oft' + +import { deriveConnection, getExplorerTxLink } from './index' + +interface Args { + eid: EndpointId + programId: string + oftStore: string + tokenEscrow: string + tokenMint: string + tokenProgram: string + multisig: string + computeUnitPriceScaleFactor: number +} + +// TODO: no need to pass in oft store? since we can derive +task('lz:oft:solana:renounce-freeze', 'Renounce freeze authority for an OFT token') + .addParam('eid', 'The endpoint ID', undefined, types.eid) + .addParam('programId', 'The OFT program ID', undefined, types.string) + .addParam('tokenEscrow', 'The OFT token escrow public key', undefined, types.string) + .addParam('oftStore', 'The OFT Store public key', undefined, types.string) + .addParam('tokenMint', 'The OFT token mint public key', undefined, types.string) + .addParam('multisig', 'The multisig public key', undefined, types.string) + .addParam('computeUnitPriceScaleFactor', 'The compute unit price scale factor', 4, types.float, true) + .setAction( + async ({ + eid, + programId: programIdStr, + oftStore: oftStoreStr, + tokenEscrow: tokenEscrowStr, + tokenMint: tokenMintStr, + multisig: multiSigStr, + computeUnitPriceScaleFactor, + }: Args) => { + const { connection, umi, umiWalletSigner, web3JsKeypair } = await deriveConnection(eid) + + // TODO: clean up below block + const wallet = new NodeWallet(web3JsKeypair) + const provider = new AnchorProvider(connection, wallet, { + commitment: 'processed', + }) + + // @ts-ignore Anchor IDL error can be ignored (will only show up after IDL file is created) + const program = new Program(IDL, programIdStr, provider) + + const [oftStorePda, oftStoreBump] = PublicKey.findProgramAddressSync( + [Buffer.from('OFT'), new PublicKey(tokenEscrowStr).toBuffer()], + program.programId + ) + if (oftStorePda.toString() != oftStoreStr) { + throw new Error('Mismatch between Token Escrow address and derived OFT Store PDA') + } + + const oftStoreAccountData = await program.account.oftStore.fetch(oftStoreStr) + + const signerIsAdmin = umiWalletSigner.publicKey.toString() == oftStoreAccountData.admin.toString() + if (!signerIsAdmin) { + throw new Error('Your keypair is not the OFT Store admin.') + } + + // Call the method + try { + const tx = await program.methods + .renounceFreeze() // Method name + .accounts({ + signer: umiWalletSigner.publicKey, + oftStore: oftStorePda, + tokenEscrow: tokenEscrowStr, + tokenMint: tokenMintStr, + currentAuthority: multiSigStr, + tokenProgram: TOKEN_PROGRAM_ID.toBase58(), // currently only supports SPL Token standard + }) + .signers([web3JsKeypair]) + .rpc() + + console.log('Transaction successful:', getExplorerTxLink(tx, eid == EndpointId.SOLANA_V2_TESTNET)) + } catch (err) { + console.error('Transaction failed:', err) + } + } + ) From 9f414c04290ee78471a3983c13d949e5ee4bcbbc Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 3 Jan 2025 18:52:02 +0800 Subject: [PATCH 2/5] remove usage of program_id_from_env --- examples/oft-solana/programs/oft/src/lib.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/examples/oft-solana/programs/oft/src/lib.rs b/examples/oft-solana/programs/oft/src/lib.rs index 74194d899e..41cdf232cd 100644 --- a/examples/oft-solana/programs/oft/src/lib.rs +++ b/examples/oft-solana/programs/oft/src/lib.rs @@ -14,13 +14,9 @@ use oapp::{ endpoint::{MessagingFee, MessagingReceipt}, LzReceiveParams, }; -use solana_helper::program_id_from_env; use state::*; -declare_id!(Pubkey::new_from_array(program_id_from_env!( - "OFT_ID", - "9UovNrJD8pQyBLheeHNayuG1wJSEAoxkmM14vw5gcsTT" -))); +declare_id!("9UovNrJD8pQyBLheeHNayuG1wJSEAoxkmM14vw5gcsTT"); pub const OFT_SEED: &[u8] = b"OFT"; pub const PEER_SEED: &[u8] = b"Peer"; From 948444a3c860af20daa0cae09161ec6c2ee4897c Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 3 Jan 2025 18:53:26 +0800 Subject: [PATCH 3/5] remove now unnecessary instruction --- examples/oft-solana/README.md | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/examples/oft-solana/README.md b/examples/oft-solana/README.md index 03e074cb84..289fc9f1b5 100644 --- a/examples/oft-solana/README.md +++ b/examples/oft-solana/README.md @@ -100,24 +100,13 @@ anchor keys sync :warning: `--force` flag overwrites the existing keys with the ones you generate. -Run `anchor keys list` to view the generated programIds (public keys). The output should look something like this: +Optionally, run `anchor keys list` to view the generated programIds (public keys). The output should look something like this: ``` endpoint: oft: ``` -Copy the OFT's programId and go into [lib.rs](./programs/oft/src/lib.rs). Note the following snippet: - -``` -declare_id!(Pubkey::new_from_array(program_id_from_env!( - "OFT_ID", - "9UovNrJD8pQyBLheeHNayuG1wJSEAoxkmM14vw5gcsTT" -))); -``` - -Replace `9UovNrJD8pQyBLheeHNayuG1wJSEAoxkmM14vw5gcsTT` with the programId that you have copied. - ### Building and Deploying the Solana OFT Program Ensure you have Docker running before running the build command. From afde8f3d61ba15d3cfabc1604621c03f0104ae7b Mon Sep 17 00:00:00 2001 From: nazreen Date: Fri, 3 Jan 2025 19:27:53 +0800 Subject: [PATCH 4/5] use dynamic import typo --- examples/oft-solana/tasks/solana/renounceFreeze.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/examples/oft-solana/tasks/solana/renounceFreeze.ts b/examples/oft-solana/tasks/solana/renounceFreeze.ts index c7852adbbb..9daf2b595c 100644 --- a/examples/oft-solana/tasks/solana/renounceFreeze.ts +++ b/examples/oft-solana/tasks/solana/renounceFreeze.ts @@ -4,12 +4,8 @@ import { TOKEN_PROGRAM_ID } from '@solana/spl-token' import { PublicKey } from '@solana/web3.js' import { task } from 'hardhat/config' -import { types } from '@layerzerolabs/devtools-evm-hardhat' import { EndpointId } from '@layerzerolabs/lz-definitions' -import IDL from '../../target/idl/oft.json' -import { Oft } from '../../target/types/oft' - import { deriveConnection, getExplorerTxLink } from './index' interface Args { @@ -50,8 +46,11 @@ task('lz:oft:solana:renounce-freeze', 'Renounce freeze authority for an OFT toke commitment: 'processed', }) - // @ts-ignore Anchor IDL error can be ignored (will only show up after IDL file is created) - const program = new Program(IDL, programIdStr, provider) + const IDL = await import('../../target/idl/oft.json').then((module) => module.default) + const anchorTypes = await import('../../target/types/oft').then((module) => module) + + // @ts-ignore we can ignore the IDL type error, which is a quirk of Anchor + const program = new Program(IDL, programIdStr, provider) const [oftStorePda, oftStoreBump] = PublicKey.findProgramAddressSync( [Buffer.from('OFT'), new PublicKey(tokenEscrowStr).toBuffer()], From 81a98bf92d2676a138faefa2c413af6062de8075 Mon Sep 17 00:00:00 2001 From: nazreen Date: Thu, 9 Jan 2025 19:26:32 +0800 Subject: [PATCH 5/5] simplify --- .../oft/src/instructions/renounce_freeze.rs | 46 +++----- examples/oft-solana/programs/oft/src/lib.rs | 5 +- .../oft-solana/tasks/solana/renounceFreeze.ts | 106 ++++++++---------- 3 files changed, 63 insertions(+), 94 deletions(-) diff --git a/examples/oft-solana/programs/oft/src/instructions/renounce_freeze.rs b/examples/oft-solana/programs/oft/src/instructions/renounce_freeze.rs index df137e82f5..ca6e9e9ece 100644 --- a/examples/oft-solana/programs/oft/src/instructions/renounce_freeze.rs +++ b/examples/oft-solana/programs/oft/src/instructions/renounce_freeze.rs @@ -1,36 +1,33 @@ use crate::*; use anchor_lang::solana_program; -use anchor_spl::token_2022::spl_token_2022; -use anchor_spl::token_2022::spl_token_2022::instruction::AuthorityType; -use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; +use anchor_spl::{ + token_2022::{ + spl_token_2022::instruction::AuthorityType, + spl_token_2022::{self, solana_program::program_option::COption}, + }, + token_interface::{Mint, TokenInterface}, +}; #[derive(Accounts)] #[instruction()] pub struct RenounceFreezeAuthority<'info> { - pub signer: Signer<'info>, + pub admin: Signer<'info>, #[account( - mut, seeds = [OFT_SEED, oft_store.token_escrow.as_ref()], bump = oft_store.bump, - constraint = is_valid_signer(signer.key(), &oft_store) @OFTError::Unauthorized + has_one = admin @OFTError::Unauthorized )] pub oft_store: Account<'info, OFTStore>, #[account( mut, - address = oft_store.token_escrow, - token::authority = oft_store, - token::mint = token_mint, - token::token_program = token_program - )] - pub token_escrow: InterfaceAccount<'info, TokenAccount>, - - #[account( - mut, + constraint = token_mint.freeze_authority.is_some() @CustomError::NoFreezeAuthority, address = oft_store.token_mint, mint::token_program = token_program )] pub token_mint: InterfaceAccount<'info, Mint>, - #[account(mut)] + #[account( + constraint = token_mint.freeze_authority == COption::Some(current_authority.key()) @CustomError::InvalidFreezeAuthority + )] pub current_authority: AccountInfo<'info>, pub token_program: Interface<'info, TokenInterface>, } @@ -38,15 +35,7 @@ pub struct RenounceFreezeAuthority<'info> { // impl RenounceFreezeAuthority<'_> { pub fn apply(ctx: Context) -> Result<()> { - let mint = &ctx.accounts.token_mint; - - // Ensure freeze authority is set - require!( - mint.freeze_authority.is_some(), - CustomError::NoFreezeAuthority - ); - - let oft_store_seed = ctx.accounts.token_escrow.key(); + let oft_store_seed = ctx.accounts.oft_store.token_escrow.key(); let seeds: &[&[u8]] = &[ OFT_SEED, oft_store_seed.as_ref(), @@ -78,13 +67,10 @@ impl RenounceFreezeAuthority<'_> { } } -// Custom error for validation #[error_code] pub enum CustomError { #[msg("No freeze authority exists on this mint.")] NoFreezeAuthority, -} - -fn is_valid_signer(signer: Pubkey, oft_store: &OFTStore) -> bool { - oft_store.admin == signer + #[msg("The provided authority does not match the freeze authority.")] + InvalidFreezeAuthority, } diff --git a/examples/oft-solana/programs/oft/src/lib.rs b/examples/oft-solana/programs/oft/src/lib.rs index 41cdf232cd..8d4bb76ef7 100644 --- a/examples/oft-solana/programs/oft/src/lib.rs +++ b/examples/oft-solana/programs/oft/src/lib.rs @@ -28,10 +28,7 @@ pub mod oft { use super::*; pub fn oft_version(_ctx: Context) -> Result { - Ok(Version { - interface: 2, - message: 1, - }) + Ok(Version { interface: 2, message: 1 }) } pub fn init_oft(mut ctx: Context, params: InitOFTParams) -> Result<()> { diff --git a/examples/oft-solana/tasks/solana/renounceFreeze.ts b/examples/oft-solana/tasks/solana/renounceFreeze.ts index 9daf2b595c..140ab3c7f3 100644 --- a/examples/oft-solana/tasks/solana/renounceFreeze.ts +++ b/examples/oft-solana/tasks/solana/renounceFreeze.ts @@ -1,9 +1,9 @@ import { AnchorProvider, Program } from '@coral-xyz/anchor' import NodeWallet from '@coral-xyz/anchor/dist/cjs/nodewallet' -import { TOKEN_PROGRAM_ID } from '@solana/spl-token' -import { PublicKey } from '@solana/web3.js' +import { TOKEN_PROGRAM_ID, getMint } from '@solana/spl-token' import { task } from 'hardhat/config' +import { types } from '@layerzerolabs/devtools-evm-hardhat' import { EndpointId } from '@layerzerolabs/lz-definitions' import { deriveConnection, getExplorerTxLink } from './index' @@ -12,10 +12,6 @@ interface Args { eid: EndpointId programId: string oftStore: string - tokenEscrow: string - tokenMint: string - tokenProgram: string - multisig: string computeUnitPriceScaleFactor: number } @@ -23,68 +19,58 @@ interface Args { task('lz:oft:solana:renounce-freeze', 'Renounce freeze authority for an OFT token') .addParam('eid', 'The endpoint ID', undefined, types.eid) .addParam('programId', 'The OFT program ID', undefined, types.string) - .addParam('tokenEscrow', 'The OFT token escrow public key', undefined, types.string) .addParam('oftStore', 'The OFT Store public key', undefined, types.string) - .addParam('tokenMint', 'The OFT token mint public key', undefined, types.string) - .addParam('multisig', 'The multisig public key', undefined, types.string) .addParam('computeUnitPriceScaleFactor', 'The compute unit price scale factor', 4, types.float, true) - .setAction( - async ({ - eid, - programId: programIdStr, - oftStore: oftStoreStr, - tokenEscrow: tokenEscrowStr, - tokenMint: tokenMintStr, - multisig: multiSigStr, - computeUnitPriceScaleFactor, - }: Args) => { - const { connection, umi, umiWalletSigner, web3JsKeypair } = await deriveConnection(eid) + .setAction(async ({ eid, programId: programIdStr, oftStore: oftStoreStr, computeUnitPriceScaleFactor }: Args) => { + const { connection, umiWalletSigner, web3JsKeypair } = await deriveConnection(eid) - // TODO: clean up below block - const wallet = new NodeWallet(web3JsKeypair) - const provider = new AnchorProvider(connection, wallet, { - commitment: 'processed', - }) + // TODO: clean up below block + const wallet = new NodeWallet(web3JsKeypair) + const provider = new AnchorProvider(connection, wallet, { + commitment: 'processed', + }) - const IDL = await import('../../target/idl/oft.json').then((module) => module.default) - const anchorTypes = await import('../../target/types/oft').then((module) => module) + const IDL = await import('../../target/idl/oft.json').then((module) => module.default) + const types = await import('../../target/types/oft').then((module) => module) - // @ts-ignore we can ignore the IDL type error, which is a quirk of Anchor - const program = new Program(IDL, programIdStr, provider) + // @ts-expect-error we can ignore the IDL type error, which is a quick or Anchor + const program = new Program(IDL, programIdStr, provider) - const [oftStorePda, oftStoreBump] = PublicKey.findProgramAddressSync( - [Buffer.from('OFT'), new PublicKey(tokenEscrowStr).toBuffer()], - program.programId - ) - if (oftStorePda.toString() != oftStoreStr) { - throw new Error('Mismatch between Token Escrow address and derived OFT Store PDA') - } + const oftStoreAccount = await program.account.oftStore.fetch(oftStoreStr) + const tokenMint = oftStoreAccount.tokenMint - const oftStoreAccountData = await program.account.oftStore.fetch(oftStoreStr) + const mintAccount = await getMint(connection, tokenMint) // to support Token-2022, pass in program ID as third param here - const signerIsAdmin = umiWalletSigner.publicKey.toString() == oftStoreAccountData.admin.toString() - if (!signerIsAdmin) { - throw new Error('Your keypair is not the OFT Store admin.') - } + // we assume that the mint authority is set to the multisig + const multisig = mintAccount.mintAuthority - // Call the method - try { - const tx = await program.methods - .renounceFreeze() // Method name - .accounts({ - signer: umiWalletSigner.publicKey, - oftStore: oftStorePda, - tokenEscrow: tokenEscrowStr, - tokenMint: tokenMintStr, - currentAuthority: multiSigStr, - tokenProgram: TOKEN_PROGRAM_ID.toBase58(), // currently only supports SPL Token standard - }) - .signers([web3JsKeypair]) - .rpc() + if (!multisig) { + throw new Error('Mint authority is not set.') + } + + const oftStoreAccountData = await program.account.oftStore.fetch(oftStoreStr) + + const signerIsAdmin = umiWalletSigner.publicKey.toString() == oftStoreAccountData.admin.toString() + if (!signerIsAdmin) { + throw new Error('Your keypair is not the OFT Store admin.') + } + + // Call the method + try { + const tx = await program.methods + .renounceFreeze() // Method name + .accounts({ + signer: umiWalletSigner.publicKey, + oftStore: oftStoreStr, + tokenMint: tokenMint, + currentAuthority: multisig, + tokenProgram: TOKEN_PROGRAM_ID.toBase58(), // currently only supports SPL Token standard + }) + .signers([web3JsKeypair]) + .rpc() - console.log('Transaction successful:', getExplorerTxLink(tx, eid == EndpointId.SOLANA_V2_TESTNET)) - } catch (err) { - console.error('Transaction failed:', err) - } + console.log('Transaction successful:', getExplorerTxLink(tx, eid == EndpointId.SOLANA_V2_TESTNET)) + } catch (err) { + console.error('Transaction failed:', err) } - ) + })