diff --git a/solana/programs/example-native-token-transfers/src/bitmap.rs b/solana/programs/example-native-token-transfers/src/bitmap.rs
index 572c3b331..b05d11f7c 100644
--- a/solana/programs/example-native-token-transfers/src/bitmap.rs
+++ b/solana/programs/example-native-token-transfers/src/bitmap.rs
@@ -48,8 +48,11 @@ impl Bitmap {
             .expect("Bitmap length must not exceed the bounds of u8")
     }
 
-    pub fn len(self) -> usize {
-        BM::<128>::from_value(self.map).len()
+    pub fn len(self) -> u8 {
+        BM::<128>::from_value(self.map)
+            .len()
+            .try_into()
+            .expect("Bitmap length must not exceed the bounds of u8")
     }
 
     pub fn is_empty(self) -> bool {
diff --git a/solana/programs/example-native-token-transfers/src/error.rs b/solana/programs/example-native-token-transfers/src/error.rs
index 2156b584a..9f1b957c9 100644
--- a/solana/programs/example-native-token-transfers/src/error.rs
+++ b/solana/programs/example-native-token-transfers/src/error.rs
@@ -61,6 +61,10 @@ pub enum NTTError {
     IncorrectRentPayer,
     #[msg("InvalidMultisig")]
     InvalidMultisig,
+    #[msg("ThresholdTooHigh")]
+    ThresholdTooHigh,
+    #[msg("InvalidTransceiverProgram")]
+    InvalidTransceiverProgram,
 }
 
 impl From<ScalingError> for NTTError {
diff --git a/solana/programs/example-native-token-transfers/src/instructions/admin/mod.rs b/solana/programs/example-native-token-transfers/src/instructions/admin/mod.rs
index a8cb12c5e..9229f7b38 100644
--- a/solana/programs/example-native-token-transfers/src/instructions/admin/mod.rs
+++ b/solana/programs/example-native-token-transfers/src/instructions/admin/mod.rs
@@ -3,6 +3,7 @@ use ntt_messages::chain_id::ChainId;
 
 use crate::{
     config::Config,
+    error::NTTError,
     peer::NttManagerPeer,
     queue::{inbox::InboxRateLimit, outbox::OutboxRateLimit, rate_limit::RateLimitState},
     registered_transceiver::RegisteredTransceiver,
@@ -76,7 +77,7 @@ pub fn set_peer(ctx: Context<SetPeer>, args: SetPeerArgs) -> Result<()> {
     Ok(())
 }
 
-// * Register transceivers
+// * Transceiver registration
 
 #[derive(Accounts)]
 pub struct RegisterTransceiver<'info> {
@@ -91,13 +92,16 @@ pub struct RegisterTransceiver<'info> {
     #[account(mut)]
     pub payer: Signer<'info>,
 
-    #[account(executable)]
+    #[account(
+        executable,
+        constraint = transceiver.key() != Pubkey::default() @ NTTError::InvalidTransceiverProgram
+    )]
     /// CHECK: transceiver is meant to be a transceiver program. Arguably a `Program` constraint could be
     /// used here that wraps the Transceiver account type.
     pub transceiver: UncheckedAccount<'info>,
 
     #[account(
-        init,
+        init_if_needed,
         space = 8 + RegisteredTransceiver::INIT_SPACE,
         payer = payer,
         seeds = [RegisteredTransceiver::SEED_PREFIX, transceiver.key().as_ref()],
@@ -109,17 +113,62 @@ pub struct RegisterTransceiver<'info> {
 }
 
 pub fn register_transceiver(ctx: Context<RegisterTransceiver>) -> Result<()> {
-    let id = ctx.accounts.config.next_transceiver_id;
-    ctx.accounts.config.next_transceiver_id += 1;
+    // initialize registered transceiver with new id on init
+    if ctx.accounts.registered_transceiver.transceiver_address == Pubkey::default() {
+        let id = ctx.accounts.config.next_transceiver_id;
+        ctx.accounts.config.next_transceiver_id += 1;
+        ctx.accounts
+            .registered_transceiver
+            .set_inner(RegisteredTransceiver {
+                bump: ctx.bumps.registered_transceiver,
+                id,
+                transceiver_address: ctx.accounts.transceiver.key(),
+            });
+    }
+
     ctx.accounts
-        .registered_transceiver
-        .set_inner(RegisteredTransceiver {
-            bump: ctx.bumps.registered_transceiver,
-            id,
-            transceiver_address: ctx.accounts.transceiver.key(),
-        });
-
-    ctx.accounts.config.enabled_transceivers.set(id, true)?;
+        .config
+        .enabled_transceivers
+        .set(ctx.accounts.registered_transceiver.id, true)?;
+    Ok(())
+}
+
+#[derive(Accounts)]
+pub struct DeregisterTransceiver<'info> {
+    #[account(
+        mut,
+        has_one = owner,
+    )]
+    pub config: Account<'info, Config>,
+
+    #[account(mut)]
+    pub owner: Signer<'info>,
+
+    #[account(executable)]
+    /// CHECK: transceiver is meant to be a transceiver program. Arguably a `Program` constraint could be
+    /// used here that wraps the Transceiver account type.
+    pub transceiver: UncheckedAccount<'info>,
+
+    #[account(
+        seeds = [RegisteredTransceiver::SEED_PREFIX, transceiver.key().as_ref()],
+        bump,
+        constraint = config.enabled_transceivers.get(registered_transceiver.id)? @ NTTError::DisabledTransceiver,
+    )]
+    pub registered_transceiver: Account<'info, RegisteredTransceiver>,
+}
+
+pub fn deregister_transceiver(ctx: Context<DeregisterTransceiver>) -> Result<()> {
+    ctx.accounts
+        .config
+        .enabled_transceivers
+        .set(ctx.accounts.registered_transceiver.id, false)?;
+
+    // decrement threshold if too high
+    let num_enabled_transceivers = ctx.accounts.config.enabled_transceivers.len();
+    if num_enabled_transceivers < ctx.accounts.config.threshold {
+        // threshold should be at least 1
+        ctx.accounts.config.threshold = num_enabled_transceivers.max(1);
+    }
     Ok(())
 }
 
@@ -200,3 +249,26 @@ pub fn set_paused(ctx: Context<SetPaused>, paused: bool) -> Result<()> {
     ctx.accounts.config.paused = paused;
     Ok(())
 }
+
+// * Set Threshold
+
+#[derive(Accounts)]
+#[instruction(threshold: u8)]
+pub struct SetThreshold<'info> {
+    pub owner: Signer<'info>,
+
+    #[account(
+        mut,
+        has_one = owner,
+        constraint = threshold <= config.enabled_transceivers.len() @ NTTError::ThresholdTooHigh
+    )]
+    pub config: Account<'info, Config>,
+}
+
+pub fn set_threshold(ctx: Context<SetThreshold>, threshold: u8) -> Result<()> {
+    if threshold == 0 {
+        return Err(NTTError::ZeroThreshold.into());
+    }
+    ctx.accounts.config.threshold = threshold;
+    Ok(())
+}
diff --git a/solana/programs/example-native-token-transfers/src/instructions/initialize.rs b/solana/programs/example-native-token-transfers/src/instructions/initialize.rs
index dfa889bb9..f87896e3d 100644
--- a/solana/programs/example-native-token-transfers/src/instructions/initialize.rs
+++ b/solana/programs/example-native-token-transfers/src/instructions/initialize.rs
@@ -155,7 +155,7 @@ fn initialize_config_and_rate_limit(
         pending_owner: None,
         paused: false,
         next_transceiver_id: 0,
-        // NOTE: can't be changed for now
+        // NOTE: can be changed via `set_threshold` ix
         threshold: 1,
         enabled_transceivers: Bitmap::new(),
         custody: common.custody.key(),
diff --git a/solana/programs/example-native-token-transfers/src/lib.rs b/solana/programs/example-native-token-transfers/src/lib.rs
index f3aed6dfb..c581a35dd 100644
--- a/solana/programs/example-native-token-transfers/src/lib.rs
+++ b/solana/programs/example-native-token-transfers/src/lib.rs
@@ -188,6 +188,10 @@ pub mod example_native_token_transfers {
         instructions::register_transceiver(ctx)
     }
 
+    pub fn deregister_transceiver(ctx: Context<DeregisterTransceiver>) -> Result<()> {
+        instructions::deregister_transceiver(ctx)
+    }
+
     pub fn set_outbound_limit(
         ctx: Context<SetOutboundLimit>,
         args: SetOutboundLimitArgs,
@@ -206,6 +210,10 @@ pub mod example_native_token_transfers {
         instructions::mark_outbox_item_as_released(ctx)
     }
 
+    pub fn set_threshold(ctx: Context<SetThreshold>, threshold: u8) -> Result<()> {
+        instructions::set_threshold(ctx, threshold)
+    }
+
     // standalone transceiver stuff
 
     pub fn set_wormhole_peer(
diff --git a/solana/programs/example-native-token-transfers/tests/admin.rs b/solana/programs/example-native-token-transfers/tests/admin.rs
new file mode 100644
index 000000000..8db1b0eb6
--- /dev/null
+++ b/solana/programs/example-native-token-transfers/tests/admin.rs
@@ -0,0 +1,222 @@
+#![cfg(feature = "test-sbf")]
+#![feature(type_changing_struct_update)]
+
+use anchor_lang::{prelude::Pubkey, system_program::System, Id};
+use example_native_token_transfers::{
+    config::Config, error::NTTError, registered_transceiver::RegisteredTransceiver,
+};
+use ntt_messages::mode::Mode;
+use solana_program_test::*;
+use solana_sdk::{instruction::InstructionError, signer::Signer, transaction::TransactionError};
+
+use crate::{
+    common::{
+        query::GetAccountDataAnchor,
+        setup::{setup, TestData},
+        submit::Submittable,
+    },
+    sdk::instructions::admin::{
+        deregister_transceiver, register_transceiver, set_threshold, DeregisterTransceiver,
+        RegisterTransceiver, SetThreshold,
+    },
+};
+
+pub mod common;
+pub mod sdk;
+
+async fn assert_threshold(
+    ctx: &mut ProgramTestContext,
+    test_data: &TestData,
+    expected_threshold: u8,
+) {
+    let config_account: Config = ctx.get_account_data_anchor(test_data.ntt.config()).await;
+    assert_eq!(config_account.threshold, expected_threshold);
+}
+
+async fn assert_transceiver_id(
+    ctx: &mut ProgramTestContext,
+    test_data: &TestData,
+    transceiver: &Pubkey,
+    expected_id: u8,
+) {
+    let registered_transceiver_account: RegisteredTransceiver = ctx
+        .get_account_data_anchor(test_data.ntt.registered_transceiver(transceiver))
+        .await;
+    assert_eq!(
+        registered_transceiver_account.transceiver_address,
+        *transceiver
+    );
+    assert_eq!(registered_transceiver_account.id, expected_id);
+}
+
+#[tokio::test]
+async fn test_invalid_transceiver() {
+    let (mut ctx, test_data) = setup(Mode::Locking).await;
+
+    // try registering system program
+    let err = register_transceiver(
+        &test_data.ntt,
+        RegisterTransceiver {
+            payer: ctx.payer.pubkey(),
+            owner: test_data.program_owner.pubkey(),
+            transceiver: System::id(),
+        },
+    )
+    .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+    .await
+    .unwrap_err();
+    assert_eq!(
+        err.unwrap(),
+        TransactionError::InstructionError(
+            0,
+            InstructionError::Custom(NTTError::InvalidTransceiverProgram.into())
+        )
+    );
+}
+
+#[tokio::test]
+async fn test_reregister_all_transceivers() {
+    let (mut ctx, test_data) = setup(Mode::Locking).await;
+
+    // Transceivers are expected to be executable which requires them to be added on setup
+    // Thus, we pass all available executable program IDs as dummy_transceivers
+    let dummy_transceivers = vec![
+        wormhole_anchor_sdk::wormhole::program::ID,
+        wormhole_governance::ID,
+    ];
+    let num_dummy_transceivers: u8 = dummy_transceivers.len().try_into().unwrap();
+
+    // register dummy transceivers
+    for (idx, transceiver) in dummy_transceivers.iter().enumerate() {
+        register_transceiver(
+            &test_data.ntt,
+            RegisterTransceiver {
+                payer: ctx.payer.pubkey(),
+                owner: test_data.program_owner.pubkey(),
+                transceiver: *transceiver,
+            },
+        )
+        .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+        .await
+        .unwrap();
+        assert_transceiver_id(&mut ctx, &test_data, transceiver, idx as u8 + 1).await;
+    }
+
+    // set threshold = 1 (for baked-in transceiver) + num_dummy_transceivers
+    set_threshold(
+        &test_data.ntt,
+        SetThreshold {
+            owner: test_data.program_owner.pubkey(),
+        },
+        1 + num_dummy_transceivers,
+    )
+    .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+    .await
+    .unwrap();
+
+    // deregister dummy transceivers
+    for (idx, transceiver) in dummy_transceivers.iter().enumerate() {
+        deregister_transceiver(
+            &test_data.ntt,
+            DeregisterTransceiver {
+                owner: test_data.program_owner.pubkey(),
+                transceiver: *transceiver,
+            },
+        )
+        .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+        .await
+        .unwrap();
+        assert_threshold(&mut ctx, &test_data, num_dummy_transceivers - idx as u8).await;
+    }
+
+    // deregister baked-in transceiver
+    deregister_transceiver(
+        &test_data.ntt,
+        DeregisterTransceiver {
+            owner: test_data.program_owner.pubkey(),
+            transceiver: example_native_token_transfers::ID,
+        },
+    )
+    .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+    .await
+    .unwrap();
+    assert_threshold(&mut ctx, &test_data, 1).await;
+
+    // reregister dummy transceiver
+    for (idx, transceiver) in dummy_transceivers.iter().enumerate() {
+        register_transceiver(
+            &test_data.ntt,
+            RegisterTransceiver {
+                payer: ctx.payer.pubkey(),
+                owner: test_data.program_owner.pubkey(),
+                transceiver: *transceiver,
+            },
+        )
+        .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+        .await
+        .unwrap();
+        assert_transceiver_id(&mut ctx, &test_data, transceiver, idx as u8 + 1).await;
+        assert_threshold(&mut ctx, &test_data, 1).await;
+    }
+
+    // reregister baked-in transceiver
+    register_transceiver(
+        &test_data.ntt,
+        RegisterTransceiver {
+            payer: ctx.payer.pubkey(),
+            owner: test_data.program_owner.pubkey(),
+            transceiver: example_native_token_transfers::ID,
+        },
+    )
+    .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+    .await
+    .unwrap();
+    assert_transceiver_id(&mut ctx, &test_data, &example_native_token_transfers::ID, 0).await;
+    assert_threshold(&mut ctx, &test_data, 1).await;
+}
+
+#[tokio::test]
+async fn test_zero_threshold() {
+    let (mut ctx, test_data) = setup(Mode::Locking).await;
+
+    let err = set_threshold(
+        &test_data.ntt,
+        SetThreshold {
+            owner: test_data.program_owner.pubkey(),
+        },
+        0,
+    )
+    .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+    .await
+    .unwrap_err();
+    assert_eq!(
+        err.unwrap(),
+        TransactionError::InstructionError(
+            0,
+            InstructionError::Custom(NTTError::ZeroThreshold.into())
+        )
+    );
+}
+
+#[tokio::test]
+async fn test_threshold_too_high() {
+    let (mut ctx, test_data) = setup(Mode::Burning).await;
+
+    let err = set_threshold(
+        &test_data.ntt,
+        SetThreshold {
+            owner: test_data.program_owner.pubkey(),
+        },
+        2,
+    )
+    .submit_with_signers(&[&test_data.program_owner], &mut ctx)
+    .await
+    .unwrap_err();
+    assert_eq!(
+        err.unwrap(),
+        TransactionError::InstructionError(
+            0,
+            InstructionError::Custom(NTTError::ThresholdTooHigh.into())
+        )
+    );
+}
diff --git a/solana/programs/example-native-token-transfers/tests/sdk/instructions/admin.rs b/solana/programs/example-native-token-transfers/tests/sdk/instructions/admin.rs
index 2f860b439..6ebf892ed 100644
--- a/solana/programs/example-native-token-transfers/tests/sdk/instructions/admin.rs
+++ b/solana/programs/example-native-token-transfers/tests/sdk/instructions/admin.rs
@@ -73,3 +73,44 @@ pub fn register_transceiver(ntt: &NTT, accounts: RegisterTransceiver) -> Instruc
         data: data.data(),
     }
 }
+
+pub struct DeregisterTransceiver {
+    pub owner: Pubkey,
+    pub transceiver: Pubkey,
+}
+
+pub fn deregister_transceiver(ntt: &NTT, accounts: DeregisterTransceiver) -> Instruction {
+    let data = example_native_token_transfers::instruction::DeregisterTransceiver {};
+
+    let accounts = example_native_token_transfers::accounts::DeregisterTransceiver {
+        config: ntt.config(),
+        owner: accounts.owner,
+        transceiver: accounts.transceiver,
+        registered_transceiver: ntt.registered_transceiver(&accounts.transceiver),
+    };
+
+    Instruction {
+        program_id: ntt.program,
+        accounts: accounts.to_account_metas(None),
+        data: data.data(),
+    }
+}
+
+pub struct SetThreshold {
+    pub owner: Pubkey,
+}
+
+pub fn set_threshold(ntt: &NTT, accounts: SetThreshold, threshold: u8) -> Instruction {
+    let data = example_native_token_transfers::instruction::SetThreshold { threshold };
+
+    let accounts = example_native_token_transfers::accounts::SetThreshold {
+        config: ntt.config(),
+        owner: accounts.owner,
+    };
+
+    Instruction {
+        program_id: example_native_token_transfers::ID,
+        accounts: accounts.to_account_metas(None),
+        data: data.data(),
+    }
+}
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 457cfd5f8..5c81a0e1a 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
@@ -1350,6 +1350,35 @@
       ],
       "args": []
     },
+    {
+      "name": "deregisterTransceiver",
+      "accounts": [
+        {
+          "name": "config",
+          "isMut": true,
+          "isSigner": false
+        },
+        {
+          "name": "owner",
+          "isMut": true,
+          "isSigner": true
+        },
+        {
+          "name": "transceiver",
+          "isMut": false,
+          "isSigner": false,
+          "docs": [
+            "used here that wraps the Transceiver account type."
+          ]
+        },
+        {
+          "name": "registeredTransceiver",
+          "isMut": false,
+          "isSigner": false
+        }
+      ],
+      "args": []
+    },
     {
       "name": "setOutboundLimit",
       "accounts": [
@@ -1438,6 +1467,27 @@
       "args": [],
       "returns": "bool"
     },
+    {
+      "name": "setThreshold",
+      "accounts": [
+        {
+          "name": "owner",
+          "isMut": false,
+          "isSigner": true
+        },
+        {
+          "name": "config",
+          "isMut": true,
+          "isSigner": false
+        }
+      ],
+      "args": [
+        {
+          "name": "threshold",
+          "type": "u8"
+        }
+      ]
+    },
     {
       "name": "setWormholePeer",
       "accounts": [
@@ -2587,6 +2637,16 @@
       "code": 6027,
       "name": "InvalidMultisig",
       "msg": "InvalidMultisig"
+    },
+    {
+      "code": 6028,
+      "name": "ThresholdTooHigh",
+      "msg": "ThresholdTooHigh"
+    },
+    {
+      "code": 6029,
+      "name": "InvalidTransceiverProgram",
+      "msg": "InvalidTransceiverProgram"
     }
   ]
 }
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 e0305fc6b..f49692f66 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
@@ -1350,6 +1350,35 @@ export type ExampleNativeTokenTransfers = {
       ],
       "args": []
     },
+    {
+      "name": "deregisterTransceiver",
+      "accounts": [
+        {
+          "name": "config",
+          "isMut": true,
+          "isSigner": false
+        },
+        {
+          "name": "owner",
+          "isMut": true,
+          "isSigner": true
+        },
+        {
+          "name": "transceiver",
+          "isMut": false,
+          "isSigner": false,
+          "docs": [
+            "used here that wraps the Transceiver account type."
+          ]
+        },
+        {
+          "name": "registeredTransceiver",
+          "isMut": false,
+          "isSigner": false
+        }
+      ],
+      "args": []
+    },
     {
       "name": "setOutboundLimit",
       "accounts": [
@@ -1438,6 +1467,27 @@ export type ExampleNativeTokenTransfers = {
       "args": [],
       "returns": "bool"
     },
+    {
+      "name": "setThreshold",
+      "accounts": [
+        {
+          "name": "owner",
+          "isMut": false,
+          "isSigner": true
+        },
+        {
+          "name": "config",
+          "isMut": true,
+          "isSigner": false
+        }
+      ],
+      "args": [
+        {
+          "name": "threshold",
+          "type": "u8"
+        }
+      ]
+    },
     {
       "name": "setWormholePeer",
       "accounts": [
@@ -2587,6 +2637,16 @@ export type ExampleNativeTokenTransfers = {
       "code": 6027,
       "name": "InvalidMultisig",
       "msg": "InvalidMultisig"
+    },
+    {
+      "code": 6028,
+      "name": "ThresholdTooHigh",
+      "msg": "ThresholdTooHigh"
+    },
+    {
+      "code": 6029,
+      "name": "InvalidTransceiverProgram",
+      "msg": "InvalidTransceiverProgram"
     }
   ]
 }
@@ -3942,6 +4002,35 @@ export const IDL: ExampleNativeTokenTransfers = {
       ],
       "args": []
     },
+    {
+      "name": "deregisterTransceiver",
+      "accounts": [
+        {
+          "name": "config",
+          "isMut": true,
+          "isSigner": false
+        },
+        {
+          "name": "owner",
+          "isMut": true,
+          "isSigner": true
+        },
+        {
+          "name": "transceiver",
+          "isMut": false,
+          "isSigner": false,
+          "docs": [
+            "used here that wraps the Transceiver account type."
+          ]
+        },
+        {
+          "name": "registeredTransceiver",
+          "isMut": false,
+          "isSigner": false
+        }
+      ],
+      "args": []
+    },
     {
       "name": "setOutboundLimit",
       "accounts": [
@@ -4030,6 +4119,27 @@ export const IDL: ExampleNativeTokenTransfers = {
       "args": [],
       "returns": "bool"
     },
+    {
+      "name": "setThreshold",
+      "accounts": [
+        {
+          "name": "owner",
+          "isMut": false,
+          "isSigner": true
+        },
+        {
+          "name": "config",
+          "isMut": true,
+          "isSigner": false
+        }
+      ],
+      "args": [
+        {
+          "name": "threshold",
+          "type": "u8"
+        }
+      ]
+    },
     {
       "name": "setWormholePeer",
       "accounts": [
@@ -5179,6 +5289,16 @@ export const IDL: ExampleNativeTokenTransfers = {
       "code": 6027,
       "name": "InvalidMultisig",
       "msg": "InvalidMultisig"
+    },
+    {
+      "code": 6028,
+      "name": "ThresholdTooHigh",
+      "msg": "ThresholdTooHigh"
+    },
+    {
+      "code": 6029,
+      "name": "InvalidTransceiverProgram",
+      "msg": "InvalidTransceiverProgram"
     }
   ]
 }
diff --git a/solana/ts/lib/ntt.ts b/solana/ts/lib/ntt.ts
index 26d0f2d59..67ec69bff 100644
--- a/solana/ts/lib/ntt.ts
+++ b/solana/ts/lib/ntt.ts
@@ -1103,6 +1103,24 @@ export namespace NTT {
       .instruction();
   }
 
+  export async function createSetThresholdInstruction(
+    program: Program<NttBindings.NativeTokenTransfer<IdlVersion>>,
+    args: {
+      owner: PublicKey;
+      threshold: number;
+    },
+    pdas?: Pdas
+  ) {
+    pdas = pdas ?? NTT.pdas(program.programId);
+    return program.methods
+      .setThreshold(args.threshold)
+      .accountsStrict({
+        owner: args.owner,
+        config: pdas.configAccount(),
+      })
+      .instruction();
+  }
+
   export async function createSetOutboundLimitInstruction(
     program: Program<NttBindings.NativeTokenTransfer<IdlVersion>>,
     args: {
diff --git a/solana/ts/sdk/ntt.ts b/solana/ts/sdk/ntt.ts
index 5a4577de0..62d78b532 100644
--- a/solana/ts/sdk/ntt.ts
+++ b/solana/ts/sdk/ntt.ts
@@ -759,6 +759,28 @@ export class SolanaNtt<N extends Network, C extends SolanaChains>
       .instruction();
   }
 
+  async createDeregisterTransceiverIx(
+    ix: number,
+    owner: web3.PublicKey
+  ): Promise<web3.TransactionInstruction> {
+    const transceiver = await this.getTransceiver(ix);
+    if (!transceiver) {
+      throw new Error(`Transceiver not found`);
+    }
+    const transceiverProgramId = transceiver.programId;
+
+    return this.program.methods
+      .deregisterTransceiver()
+      .accountsStrict({
+        owner,
+        config: this.pdas.configAccount(),
+        transceiver: transceiverProgramId,
+        registeredTransceiver:
+          this.pdas.registeredTransceiver(transceiverProgramId),
+      })
+      .instruction();
+  }
+
   async *setWormholeTransceiverPeer(
     peer: ChainAddress,
     payer: AccountAddress<C>