浏览代码

Added a pinocchio block list (#398)

* Added pinocchio block list

* Added pinocchio subfolder
Tiago Carvalho 2 月之前
父节点
当前提交
911ecb7ffb
共有 100 个文件被更改,包括 11836 次插入0 次删除
  1. 3 0
      tokens/token-2022/transfer-hook/pblock-list/.gitignore
  2. 7 0
      tokens/token-2022/transfer-hook/pblock-list/Cargo.toml
  3. 25 0
      tokens/token-2022/transfer-hook/pblock-list/cli/Cargo.toml
  4. 493 0
      tokens/token-2022/transfer-hook/pblock-list/cli/src/main.rs
  5. 294 0
      tokens/token-2022/transfer-hook/pblock-list/codama.ts
  6. 25 0
      tokens/token-2022/transfer-hook/pblock-list/package.json
  7. 7 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/Cargo.toml
  8. 25 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/cli/Cargo.toml
  9. 493 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/cli/src/main.rs
  10. 294 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/codama.ts
  11. 25 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/package.json
  12. 15 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/Cargo.toml
  13. 25 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/error.rs
  14. 87 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/block_wallet.rs
  15. 85 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/init.rs
  16. 11 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/mod.rs
  17. 133 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/setup_extra_metas.rs
  18. 135 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/tx_hook.rs
  19. 69 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/unblock_wallet.rs
  20. 42 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/lib.rs
  21. 24 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/config.rs
  22. 64 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/mod.rs
  23. 21 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/wallet_block.rs
  24. 57 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/token2022_utils.rs
  25. 143 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/readme.md
  26. 12 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/Cargo.toml
  27. 163 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/config.rs
  28. 155 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/extra_metas.rs
  29. 14 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/mod.rs
  30. 163 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/wallet_block.rs
  31. 6 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/errors/mod.rs
  32. 447 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/block_wallet.rs
  33. 370 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/init.rs
  34. 16 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/mod.rs
  35. 483 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/setup_extra_metas.rs
  36. 410 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/unblock_wallet.rs
  37. 14 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/mod.rs
  38. 11 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/programs.rs
  39. 21 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/shared.rs
  40. 2 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/lib.rs
  41. 148 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/config.ts
  42. 118 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/extraMetas.ts
  43. 11 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/index.ts
  44. 131 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/walletBlock.ts
  45. 12 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/index.ts
  46. 314 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/blockWallet.ts
  47. 12 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/index.ts
  48. 266 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/init.ts
  49. 344 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/setupExtraMetas.ts
  50. 292 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/unblockWallet.ts
  51. 26 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/config.ts
  52. 35 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/extraMetas.ts
  53. 11 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/index.ts
  54. 35 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/walletBlock.ts
  55. 88 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/programs/blockList.ts
  56. 9 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/programs/index.ts
  57. 164 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/shared/index.ts
  58. 13 0
      tokens/token-2022/transfer-hook/pblock-list/pinocchio/tsconfig.json
  59. 15 0
      tokens/token-2022/transfer-hook/pblock-list/program/Cargo.toml
  60. 25 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/error.rs
  61. 87 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/block_wallet.rs
  62. 85 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/init.rs
  63. 11 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/mod.rs
  64. 133 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/setup_extra_metas.rs
  65. 135 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/tx_hook.rs
  66. 69 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/unblock_wallet.rs
  67. 42 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/lib.rs
  68. 24 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/state/config.rs
  69. 64 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/state/mod.rs
  70. 21 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/state/wallet_block.rs
  71. 57 0
      tokens/token-2022/transfer-hook/pblock-list/program/src/token2022_utils.rs
  72. 143 0
      tokens/token-2022/transfer-hook/pblock-list/readme.md
  73. 12 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/Cargo.toml
  74. 163 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/config.rs
  75. 155 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/extra_metas.rs
  76. 14 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/mod.rs
  77. 163 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/wallet_block.rs
  78. 6 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/errors/mod.rs
  79. 447 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/block_wallet.rs
  80. 370 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/init.rs
  81. 16 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/mod.rs
  82. 483 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/setup_extra_metas.rs
  83. 410 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/unblock_wallet.rs
  84. 14 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/mod.rs
  85. 11 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/programs.rs
  86. 21 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/shared.rs
  87. 2 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/lib.rs
  88. 148 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/config.ts
  89. 118 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/extraMetas.ts
  90. 11 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/index.ts
  91. 131 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/walletBlock.ts
  92. 12 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/index.ts
  93. 314 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/blockWallet.ts
  94. 12 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/index.ts
  95. 266 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/init.ts
  96. 344 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/setupExtraMetas.ts
  97. 292 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/unblockWallet.ts
  98. 26 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/config.ts
  99. 35 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/extraMetas.ts
  100. 11 0
      tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/index.ts

+ 3 - 0
tokens/token-2022/transfer-hook/pblock-list/.gitignore

@@ -0,0 +1,3 @@
+target/
+test-ledger/
+node_modules/

+ 7 - 0
tokens/token-2022/transfer-hook/pblock-list/Cargo.toml

@@ -0,0 +1,7 @@
+[workspace]
+members = ["program", "cli", "sdk/rust"]
+resolver = "2"
+
+
+[workspace.dependencies]
+block-list-client = { path = "sdk/rust"}

+ 25 - 0
tokens/token-2022/transfer-hook/pblock-list/cli/Cargo.toml

@@ -0,0 +1,25 @@
+[package]
+name = "block-list-cli"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+clap = { version = "3", features = ["cargo"] }
+futures-util = "0.3.31"
+solana-clap-v3-utils = "2.2.0"
+solana-cli-config = "2.2.0"
+solana-client = "2.2.0"
+solana-logger = "2.2.0"
+solana-remote-wallet = "2.2.0"
+solana-sdk = "2.2.0"
+spl-token-client = { version = "0.13.0" }
+tokio = { version = "1", features = ["full"] }
+block-list-client = { workspace = true }
+spl-tlv-account-resolution = "0.8.1"
+spl-transfer-hook-interface = { version = "0.8.2" }
+
+
+
+[[bin]]
+name = "block-list-cli"
+path = "src/main.rs"

+ 493 - 0
tokens/token-2022/transfer-hook/pblock-list/cli/src/main.rs

@@ -0,0 +1,493 @@
+use {
+    clap::{builder::BoolishValueParser, crate_description, crate_name, crate_version, Arg, Command}, solana_clap_v3_utils::{
+        input_parsers::{
+            parse_url_or_moniker,
+            signer::{SignerSource, SignerSourceParserBuilder},
+        },
+        input_validators::normalize_to_url_if_moniker,
+        keypair::signer_from_path,
+    }, solana_client::nonblocking::rpc_client::RpcClient, solana_remote_wallet::remote_wallet::RemoteWalletManager, solana_sdk::{
+        commitment_config::CommitmentConfig,
+        message::Message,
+        pubkey::Pubkey,
+        signature::{Signature, Signer},
+        transaction::Transaction,
+    }, spl_tlv_account_resolution::{account::ExtraAccountMeta, seeds::Seed, state::ExtraAccountMetaList}, spl_transfer_hook_interface::instruction::ExecuteInstruction, std::{error::Error, process::exit, rc::Rc, sync::Arc}
+};
+
+struct Config {
+    commitment_config: CommitmentConfig,
+    payer: Arc<dyn Signer>,
+    json_rpc_url: String,
+    verbose: bool,
+}
+
+pub fn get_extra_account_metas_with_source_wallet_block() -> Vec<ExtraAccountMeta> {
+    vec![
+        // [5] wallet_block for source token account wallet
+        ExtraAccountMeta::new_with_seeds(
+            &[
+                Seed::Literal {
+                    bytes: b"wallet_block".to_vec(),
+                },
+                Seed::AccountData {
+                    account_index: 0,
+                    data_index: 32,
+                    length: 32,
+                },
+            ],
+            false,
+            false,
+        ).unwrap(), 
+    ]
+}
+pub fn get_extra_account_metas_with_both_wallet_blocks() -> Vec<ExtraAccountMeta> {
+    vec![
+        // [5] wallet_block for source token account wallet
+        ExtraAccountMeta::new_with_seeds(
+            &[
+                Seed::Literal {
+                    bytes: b"wallet_block".to_vec(),
+                },
+                Seed::AccountData {
+                    account_index: 0,
+                    data_index: 32,
+                    length: 32,
+                },
+            ],
+            false,
+            false,
+        ).unwrap(), 
+        // [6] wallet_block for destination token account wallet
+        ExtraAccountMeta::new_with_seeds(
+            &[
+                Seed::Literal {
+                    bytes: b"wallet_block".to_vec(),
+                },
+                Seed::AccountData {
+                    account_index: 2,
+                    data_index: 32,
+                    length: 32,
+                },
+            ],
+            false,
+            false,
+        ).unwrap(), 
+    ]
+}
+
+
+fn create_empty_extra_metas() -> Vec<u8> {
+    let size = ExtraAccountMetaList::size_of(0).unwrap();
+    let metas: Vec<ExtraAccountMeta> = vec![];
+    let mut data = vec![0; size];
+    ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
+    data
+}
+
+fn create_extra_metas_with_source_wallet_block() -> Vec<u8> {
+    let metas: Vec<ExtraAccountMeta> = get_extra_account_metas_with_source_wallet_block();
+    let size = ExtraAccountMetaList::size_of(metas.len()).unwrap();
+    let mut data = vec![0; size];
+    ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
+    data
+}
+fn create_extra_metas_with_both_wallet_blocks() -> Vec<u8> {
+    let metas: Vec<ExtraAccountMeta> = get_extra_account_metas_with_both_wallet_blocks();
+    let size = ExtraAccountMetaList::size_of(metas.len()).unwrap();
+    let mut data = vec![0; size];
+    ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
+    data
+}
+
+fn get_extra_metas_account_data() {
+    let data_empty = create_empty_extra_metas();
+    let data_source_wallet_block = create_extra_metas_with_source_wallet_block();
+    let data_both_wallet_blocks = create_extra_metas_with_both_wallet_blocks();
+
+    println!("data empty: {:?}", data_empty);
+    println!("data source wallet block: {:?}", data_source_wallet_block);
+    println!("data both wallet blocks: {:?}", data_both_wallet_blocks);
+}
+
+async fn get_config(rpc_client: &Arc<RpcClient>) {
+    let config = block_list_client::accounts::Config::find_pda().0;
+    let data = rpc_client.get_account_data(&config).await.unwrap();
+    println!("config: {:?}", data);
+
+    let config = block_list_client::accounts::Config::from_bytes(&data).unwrap();
+    println!("config: {:?}", config);
+}
+
+async fn get_extra_metas(rpc_client: &Arc<RpcClient>, mint_address: &Pubkey) {
+    let extra_metas = block_list_client::accounts::ExtraMetas::find_pda(mint_address).0;
+    let data = rpc_client.get_account_data(&extra_metas).await.unwrap();
+    println!("extra_metas: {:?}", data);
+
+}
+
+async fn process_setup_extra_metas(
+    rpc_client: &Arc<RpcClient>,
+    payer: &Arc<dyn Signer>,
+    mint_address: &Pubkey,
+    check_both_wallets: bool,
+) -> Result<Signature, Box<dyn Error>> {
+    let ix = block_list_client::instructions::SetupExtraMetasBuilder::new()
+        .authority(payer.pubkey())
+        .config(block_list_client::accounts::Config::find_pda().0)
+        .mint(*mint_address)
+        .extra_metas(block_list_client::accounts::ExtraMetas::find_pda(mint_address).0)
+        .check_both_wallets(check_both_wallets)
+        .instruction();
+
+    let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
+    
+    let blockhash = rpc_client
+        .get_latest_blockhash()
+        .await
+        .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
+
+    transaction
+        .try_sign(&[payer], blockhash)
+        .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
+
+    let signature = rpc_client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .map_err(|err| format!("error: send transaction: {}", err))?;
+
+    Ok(signature)
+}
+
+async fn process_init(
+    rpc_client: &Arc<RpcClient>,
+    payer: &Arc<dyn Signer>,
+) -> Result<Signature, Box<dyn Error>> {
+
+    let ix = block_list_client::instructions::InitBuilder::new()
+        .authority(payer.pubkey())
+        .config(block_list_client::accounts::Config::find_pda().0)
+        .instruction();
+
+    let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
+    
+    let blockhash = rpc_client
+        .get_latest_blockhash()
+        .await
+        .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
+
+    transaction
+        .try_sign(&[payer], blockhash)
+        .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
+
+    let signature = rpc_client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .map_err(|err| format!("error: send transaction: {}", err))?;
+
+    Ok(signature)
+}
+
+async fn process_block_wallet(
+    rpc_client: &Arc<RpcClient>,
+    payer: &Arc<dyn Signer>,
+    wallet_address: &Pubkey,
+) -> Result<Signature, Box<dyn Error>> {
+    
+    let ix = block_list_client::instructions::BlockWalletBuilder::new()
+        .authority(payer.pubkey())
+        .config(block_list_client::accounts::Config::find_pda().0)
+        .wallet(*wallet_address)
+        .wallet_block(block_list_client::accounts::WalletBlock::find_pda(wallet_address).0)
+        .instruction();
+
+    let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
+    
+    let blockhash = rpc_client
+        .get_latest_blockhash()
+        .await
+        .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
+
+    transaction
+        .try_sign(&[payer], blockhash)
+        .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
+
+    let signature = rpc_client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .map_err(|err| format!("error: send transaction: {}", err))?;
+
+    Ok(signature)
+}
+
+async fn process_unblock_wallet(
+    rpc_client: &Arc<RpcClient>,
+    payer: &Arc<dyn Signer>,
+    wallet_address: &Pubkey,
+) -> Result<Signature, Box<dyn Error>> {
+    let ix = block_list_client::instructions::UnblockWalletBuilder::new()
+        .authority(payer.pubkey())
+        .config(block_list_client::accounts::Config::find_pda().0)
+        .wallet_block(block_list_client::accounts::WalletBlock::find_pda(wallet_address).0)
+        .instruction();
+
+    let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
+    
+    let blockhash = rpc_client
+        .get_latest_blockhash()
+        .await
+        .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
+
+    transaction
+        .try_sign(&[payer], blockhash)
+        .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
+
+    let signature = rpc_client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .map_err(|err| format!("error: send transaction: {}", err))?;
+
+    Ok(signature)
+}
+
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+    let app_matches = Command::new(crate_name!())
+        .about(crate_description!())
+        .version(crate_version!())
+        .subcommand_required(true)
+        .arg_required_else_help(true)
+        .arg({
+            let arg = Arg::new("config_file")
+                .short('C')
+                .long("config")
+                .value_name("PATH")
+                .takes_value(true)
+                .global(true)
+                .help("Configuration file to use");
+            if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
+                arg.default_value(config_file)
+            } else {
+                arg
+            }
+        })
+        .arg(
+            Arg::new("payer")
+                .long("payer")
+                .value_name("KEYPAIR")
+                .value_parser(SignerSourceParserBuilder::default().allow_all().build())
+                .takes_value(true)
+                .global(true)
+                .help("Filepath or URL to a keypair [default: client keypair]"),
+        )
+        .arg(
+            Arg::new("verbose")
+                .long("verbose")
+                .short('v')
+                .takes_value(false)
+                .global(true)
+                .help("Show additional information"),
+        )
+        .arg(
+            Arg::new("json_rpc_url")
+                .short('u')
+                .long("url")
+                .value_name("URL")
+                .takes_value(true)
+                .global(true)
+                .value_parser(parse_url_or_moniker)
+                .help("JSON RPC URL for the cluster [default: value from configuration file]"),
+        )
+        .subcommand(
+            Command::new("init").about("Initializes the blocklist")
+        )
+        .subcommand(
+            Command::new("block-wallet").about("Blocks a wallet")
+            .arg(
+                Arg::new("wallet_address")
+                    .value_name("WALLET_ADDRESS")
+                    .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
+                    .takes_value(true)
+                    .index(1)
+                    .help("Specify the wallet address to block"),
+            )
+        )
+        .subcommand(
+            Command::new("unblock-wallet").about("Unblocks a wallet")
+            .arg(
+                Arg::new("wallet_address")
+                    .value_name("WALLET_ADDRESS")
+                    .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
+                    .takes_value(true)
+                    .index(1)
+                    .help("Specify the wallet address to unblock"),
+            )
+        )
+        .subcommand(
+            Command::new("get-extra-metas-account-data").about("Gets the extra metas account data")
+        )
+        .subcommand(
+            Command::new("get-config").about("Gets the config account data")
+        )
+        .subcommand(
+            Command::new("get-extra-metas").about("Gets the extra metas account data")
+            .arg(
+                Arg::new("mint_address")
+                .value_name("MINT_ADDRESS")
+                .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
+                .takes_value(true)
+                .index(1)
+                .help("Specify the mint address"),
+            )
+        )
+        .subcommand(
+            Command::new("setup-extra-metas").about("Setup the extra metas account")
+            .arg(
+                Arg::new("mint_address")
+                .value_name("MINT_ADDRESS")
+                .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
+                .takes_value(true)
+                .index(1)
+                .help("Specify the mint address"),
+            )
+            .arg(
+                Arg::new("check-both-wallets")
+                .long("check-both-wallets")
+                .short('b')
+                .help("Specify if both wallets should be checked"),
+            )
+        )
+        .get_matches();
+
+    let (command, matches) = app_matches.subcommand().unwrap();
+    let mut wallet_manager: Option<Rc<RemoteWalletManager>> = None;
+
+    let config = {
+        let cli_config = if let Some(config_file) = matches.try_get_one::<String>("config_file")? {
+            solana_cli_config::Config::load(config_file).unwrap_or_default()
+        } else {
+            solana_cli_config::Config::default()
+        };
+
+        let payer = if let Ok(Some((signer, _))) =
+            SignerSource::try_get_signer(matches, "payer", &mut wallet_manager)
+        {
+            Box::new(signer)
+        } else {
+            signer_from_path(
+                matches,
+                &cli_config.keypair_path,
+                "payer",
+                &mut wallet_manager,
+            )?
+        };
+
+        let json_rpc_url = normalize_to_url_if_moniker(
+            matches
+                .get_one::<String>("json_rpc_url")
+                .unwrap_or(&cli_config.json_rpc_url),
+        );
+
+        Config {
+            commitment_config: CommitmentConfig::confirmed(),
+            payer: Arc::from(payer),
+            json_rpc_url,
+            verbose: matches.try_contains_id("verbose")?,
+        }
+    };
+    solana_logger::setup_with_default("solana=info");
+
+    if config.verbose {
+        println!("JSON RPC URL: {}", config.json_rpc_url);
+    }
+    let rpc_client = Arc::new(RpcClient::new_with_commitment(
+        config.json_rpc_url.clone(),
+        config.commitment_config,
+    ));
+
+    match (command, matches) {
+        ("init", _arg_matches) => {
+            let response = process_init(
+                &rpc_client,
+                &config.payer,
+            )
+            .await
+            .unwrap_or_else(|err| {
+                eprintln!("error: init: {}", err);
+                exit(1);
+            });
+            println!("{}", response);
+        }
+        ("block-wallet", arg_matches) => {
+            let wallet_address =
+                SignerSource::try_get_pubkey(arg_matches, "wallet_address", &mut wallet_manager)
+                    .unwrap()
+                    .unwrap();
+            let response = process_block_wallet(
+                &rpc_client,
+                &config.payer,
+                &wallet_address,
+            )
+            .await
+            .unwrap_or_else(|err| {
+                eprintln!("error: init: {}", err);
+                exit(1);
+            });
+            println!("{}", response);
+        }
+        ("unblock-wallet", arg_matches) => {
+            let wallet_address =
+                SignerSource::try_get_pubkey(arg_matches, "wallet_address", &mut wallet_manager)
+                    .unwrap()
+                    .unwrap();
+            let response = process_unblock_wallet(
+                &rpc_client,
+                &config.payer,
+                &wallet_address,
+            )
+            .await
+            .unwrap_or_else(|err| {
+                eprintln!("error: init: {}", err);
+                exit(1);
+            });
+            println!("{}", response);
+        }
+        ("get-extra-metas-account-data", _arg_matches) => {
+            get_extra_metas_account_data();
+        }
+        ("get-config", _arg_matches) => {
+            get_config(&rpc_client).await;
+        }
+        ("get-extra-metas", arg_matches) => {
+            let mint_address =
+                SignerSource::try_get_pubkey(arg_matches, "mint_address", &mut wallet_manager)
+                    .unwrap()
+                    .unwrap();
+            get_extra_metas(&rpc_client, &mint_address).await;
+        }
+        ("setup-extra-metas", arg_matches) => {
+            let mint_address =
+                SignerSource::try_get_pubkey(arg_matches, "mint_address", &mut wallet_manager)
+                    .unwrap()
+                    .unwrap();
+            let check_both_wallets = arg_matches.contains_id("check-both-wallets");
+            let response = process_setup_extra_metas(
+                &rpc_client,
+                &config.payer,
+                &mint_address,
+                check_both_wallets,
+            )
+            .await
+            .unwrap_or_else(|err| {
+                eprintln!("error: setup_extra_metas: {}", err);
+                exit(1);
+            });
+            println!("{}", response);
+        }
+        _ => unreachable!(),
+    };
+
+    Ok(())
+}
+    

+ 294 - 0
tokens/token-2022/transfer-hook/pblock-list/codama.ts

@@ -0,0 +1,294 @@
+import { renderJavaScriptUmiVisitor, renderJavaScriptVisitor, renderRustVisitor } from '@codama/renderers';
+import { accountLinkNode, accountNode, booleanTypeNode, booleanValueNode, constantDiscriminatorNode, constantPdaSeedNodeFromString, constantValueNode, createFromRoot, instructionAccountLinkNode, instructionAccountNode, instructionArgumentNode, instructionNode, numberTypeNode, numberValueNode, optionTypeNode, pdaLinkNode, pdaNode, pdaSeedValueNode, pdaValueNode, programNode, publicKeyTypeNode, publicKeyValueNode, resolverValueNode, rootNode, sizeDiscriminatorNode, sizePrefixTypeNode, stringTypeNode, stringValueNode, structFieldTypeNode, structTypeNode, variablePdaSeedNode } from "codama"
+import path from "path";
+import fs from "fs";
+
+const rustClientsDir = path.join(__dirname, "..", "sdk", "rust");
+const typescriptClientsDir = path.join(
+  __dirname,
+  "..",
+  "sdk",
+  "ts",
+);
+
+const root = rootNode(
+    programNode({
+        name: "block-list",
+        publicKey: "BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf",
+        version: "1.0.0",
+        accounts: [
+          accountNode({
+            name: "config",
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0))),
+            ],
+            size: 41,
+            pda: pdaLinkNode("config"),
+            docs: ["The config PDA account"],
+            data: structTypeNode([
+              structFieldTypeNode({
+                name: "discriminator",
+                type: numberTypeNode("u8"),
+                defaultValueStrategy: "omitted",
+              }),
+              structFieldTypeNode({
+                name: "authority",
+                type: publicKeyTypeNode(),
+              }),
+              structFieldTypeNode({
+                name: "blocked_wallets_count",
+                type: numberTypeNode("u64"),
+              }),
+            ]),
+          }),
+          accountNode({
+            name: "walletBlock",
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(1))),
+            ],
+            size: 33,
+            pda: pdaLinkNode("walletBlock"),
+            docs: ["The config PDA account"],
+            data: structTypeNode([
+              structFieldTypeNode({
+                name: "authority",
+                type: publicKeyTypeNode(),
+              }),
+            ])
+          }),
+          accountNode({
+            name: "extraMetas",
+            pda: pdaLinkNode("extraMetas"),
+            docs: ["The extra metas PDA account"],
+          })
+        ],
+        instructions: [
+          instructionNode({
+            name: "init",
+            arguments: [
+              instructionArgumentNode({
+                  name: 'discriminator',
+                  type: numberTypeNode('u8'),
+                  defaultValue: numberValueNode(0xF1),
+                  defaultValueStrategy: 'omitted',
+              }),
+            ],
+            accounts: [
+              instructionAccountNode({
+                name: "authority",
+                isSigner: true,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "config",
+                isSigner: false,
+                isWritable: true,
+                defaultValue: pdaValueNode(pdaLinkNode("config"))
+              }),
+              instructionAccountNode({
+                name: "systemProgram",
+                defaultValue: publicKeyValueNode("11111111111111111111111111111111", "systemProgram"),
+                isSigner: false,
+                isWritable: false,
+              })
+            ],
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0xF1))),
+            ],
+            docs: ["Initialize the config PDA account"],
+          }),
+          instructionNode({
+            name: "blockWallet",
+            arguments: [
+              instructionArgumentNode({
+                  name: 'discriminator',
+                  type: numberTypeNode('u8'),
+                  defaultValue: numberValueNode(0xF2),
+                  defaultValueStrategy: 'omitted',
+              }),
+            ],
+            accounts: [
+              instructionAccountNode({
+                name: "authority",
+                isSigner: true,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "config",
+                isSigner: false,
+                isWritable: true,
+                defaultValue: pdaValueNode(pdaLinkNode("config"))
+              }),
+              instructionAccountNode({
+                name: "wallet",
+                isSigner: false,
+                isWritable: false,
+              }),
+              instructionAccountNode({
+                name: "walletBlock",
+                isSigner: false,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "systemProgram",
+                defaultValue: publicKeyValueNode("11111111111111111111111111111111", "systemProgram"),
+                isSigner: false,
+                isWritable: false,
+              })
+            ],
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0xF2))),
+            ],
+            docs: ["Block a wallet"],
+          }),
+          instructionNode({
+            name: "unblockWallet",
+            arguments: [
+              instructionArgumentNode({
+                  name: 'discriminator',
+                  type: numberTypeNode('u8'),
+                  defaultValue: numberValueNode(0xF3),
+                  defaultValueStrategy: 'omitted',
+              }),
+            ],
+            accounts: [
+              instructionAccountNode({
+                name: "authority",
+                isSigner: true,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "config",
+                isSigner: false,
+                isWritable: true,
+                defaultValue: pdaValueNode(pdaLinkNode("config"))
+              }),
+              instructionAccountNode({
+                name: "walletBlock",
+                isSigner: false,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "systemProgram",
+                defaultValue: publicKeyValueNode("11111111111111111111111111111111", "systemProgram"),
+                isSigner: false,
+                isWritable: false,
+              })
+            ],
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0xF3))),
+            ],
+            docs: ["Unblock a wallet"],
+          }),
+          instructionNode({
+            name: "setupExtraMetas",
+            arguments: [
+              instructionArgumentNode({
+                  name: 'discriminator',
+                  type: numberTypeNode('u8'),
+                  defaultValue: numberValueNode(0x6A),
+                  defaultValueStrategy: 'omitted',
+              }),
+              instructionArgumentNode({
+                name: 'checkBothWallets',
+                type: booleanTypeNode(),
+                defaultValue: booleanValueNode(false),
+                defaultValueStrategy: 'optional',
+              }),
+            ],
+            accounts: [
+              instructionAccountNode({
+                name: "authority",
+                isSigner: true,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "config",
+                isSigner: false,
+                isWritable: false,
+                defaultValue: pdaValueNode(pdaLinkNode("config"))
+              }),
+              instructionAccountNode({
+                name: "mint",
+                isSigner: false,
+                isWritable: false,
+              }),
+              instructionAccountNode({
+                name: "extraMetas",
+                isSigner: false,
+                isWritable: true,
+                defaultValue: pdaValueNode(pdaLinkNode("extraMetas"))
+              }),
+              instructionAccountNode({
+                name: "systemProgram",
+                defaultValue: publicKeyValueNode("11111111111111111111111111111111", "systemProgram"),
+                isSigner: false,
+                isWritable: false,
+              })
+            ],
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0x6A))),
+            ],
+            docs: ["Unblock a wallet"],
+          }),
+        ],
+        pdas: [
+          pdaNode({
+            name: "config",
+            seeds: [constantPdaSeedNodeFromString("utf8", "config")],
+            docs: ["The config PDA account"],
+          }),
+          pdaNode({
+            name: "walletBlock",
+            seeds: [
+              constantPdaSeedNodeFromString("utf8", "wallet_block"),
+              variablePdaSeedNode("wallet", publicKeyTypeNode()),
+            ],
+            docs: ["The wallet block PDA account"],
+          }),
+          pdaNode({
+            name: "extraMetas",
+            seeds: [
+              constantPdaSeedNodeFromString("utf8", "extra-account-metas"),
+              variablePdaSeedNode("mint", publicKeyTypeNode()),
+            ],
+            docs: ["The extra metas PDA account"],
+          }),
+        ]
+    })
+);
+
+
+function preserveConfigFiles() {
+    const filesToPreserve = ['package.json', 'tsconfig.json', '.npmignore', 'pnpm-lock.yaml', 'Cargo.toml'];
+    const preservedFiles = new Map();
+    
+    filesToPreserve.forEach(filename => {
+      const filePath = path.join(typescriptClientsDir, filename);
+      const tempPath = path.join(typescriptClientsDir, `${filename}.temp`);
+      
+      if (fs.existsSync(filePath)) {
+        fs.copyFileSync(filePath, tempPath);
+        preservedFiles.set(filename, tempPath);
+      }
+    });
+    
+    return {
+      restore: () => {
+        preservedFiles.forEach((tempPath, filename) => {
+          const filePath = path.join(typescriptClientsDir, filename);
+          if (fs.existsSync(tempPath)) {
+            fs.copyFileSync(tempPath, filePath);
+            fs.unlinkSync(tempPath);
+          }
+        });
+      }
+    };
+  }
+
+const codama = createFromRoot(root)
+
+const configPreserver = preserveConfigFiles();
+
+codama.accept(renderJavaScriptVisitor('sdk/ts/src', { formatCode: true }));
+codama.accept(renderRustVisitor('sdk/rust/src/client', { crateFolder: 'sdk/rust/', formatCode: true }));

+ 25 - 0
tokens/token-2022/transfer-hook/pblock-list/package.json

@@ -0,0 +1,25 @@
+{
+  "name": "block-list",
+  "version": "1.0.0",
+  "main": "index.js",
+  "license": "MIT",
+  "engines": {
+    "node": ">=20.18.0"
+  },
+  "scripts": {
+    "generate-sdks": "pnpx tsx codama.ts",
+    "test": "mocha --import=tsx ./tests/test.spec.ts"
+  },
+  "dependencies": {
+    "@codama/renderers": "^1.0.19",
+    "@types/mocha": "^10.0.10",
+    "codama": "^1.2.11",
+    "litesvm": "^0.2.0",
+    "mocha": "^11.1.0",
+    "solana-bankrun": "^0.4.0",
+    "tsx": "^4.19.3"
+  },
+  "devDependencies": {
+    "@types/node": "^22.15.29"
+  }
+}

+ 7 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/Cargo.toml

@@ -0,0 +1,7 @@
+[workspace]
+members = ["program", "cli", "sdk/rust"]
+resolver = "2"
+
+
+[workspace.dependencies]
+block-list-client = { path = "sdk/rust"}

+ 25 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/cli/Cargo.toml

@@ -0,0 +1,25 @@
+[package]
+name = "block-list-cli"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+clap = { version = "3", features = ["cargo"] }
+futures-util = "0.3.31"
+solana-clap-v3-utils = "2.2.0"
+solana-cli-config = "2.2.0"
+solana-client = "2.2.0"
+solana-logger = "2.2.0"
+solana-remote-wallet = "2.2.0"
+solana-sdk = "2.2.0"
+spl-token-client = { version = "0.13.0" }
+tokio = { version = "1", features = ["full"] }
+block-list-client = { workspace = true }
+spl-tlv-account-resolution = "0.8.1"
+spl-transfer-hook-interface = { version = "0.8.2" }
+
+
+
+[[bin]]
+name = "block-list-cli"
+path = "src/main.rs"

+ 493 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/cli/src/main.rs

@@ -0,0 +1,493 @@
+use {
+    clap::{builder::BoolishValueParser, crate_description, crate_name, crate_version, Arg, Command}, solana_clap_v3_utils::{
+        input_parsers::{
+            parse_url_or_moniker,
+            signer::{SignerSource, SignerSourceParserBuilder},
+        },
+        input_validators::normalize_to_url_if_moniker,
+        keypair::signer_from_path,
+    }, solana_client::nonblocking::rpc_client::RpcClient, solana_remote_wallet::remote_wallet::RemoteWalletManager, solana_sdk::{
+        commitment_config::CommitmentConfig,
+        message::Message,
+        pubkey::Pubkey,
+        signature::{Signature, Signer},
+        transaction::Transaction,
+    }, spl_tlv_account_resolution::{account::ExtraAccountMeta, seeds::Seed, state::ExtraAccountMetaList}, spl_transfer_hook_interface::instruction::ExecuteInstruction, std::{error::Error, process::exit, rc::Rc, sync::Arc}
+};
+
+struct Config {
+    commitment_config: CommitmentConfig,
+    payer: Arc<dyn Signer>,
+    json_rpc_url: String,
+    verbose: bool,
+}
+
+pub fn get_extra_account_metas_with_source_wallet_block() -> Vec<ExtraAccountMeta> {
+    vec![
+        // [5] wallet_block for source token account wallet
+        ExtraAccountMeta::new_with_seeds(
+            &[
+                Seed::Literal {
+                    bytes: b"wallet_block".to_vec(),
+                },
+                Seed::AccountData {
+                    account_index: 0,
+                    data_index: 32,
+                    length: 32,
+                },
+            ],
+            false,
+            false,
+        ).unwrap(), 
+    ]
+}
+pub fn get_extra_account_metas_with_both_wallet_blocks() -> Vec<ExtraAccountMeta> {
+    vec![
+        // [5] wallet_block for source token account wallet
+        ExtraAccountMeta::new_with_seeds(
+            &[
+                Seed::Literal {
+                    bytes: b"wallet_block".to_vec(),
+                },
+                Seed::AccountData {
+                    account_index: 0,
+                    data_index: 32,
+                    length: 32,
+                },
+            ],
+            false,
+            false,
+        ).unwrap(), 
+        // [6] wallet_block for destination token account wallet
+        ExtraAccountMeta::new_with_seeds(
+            &[
+                Seed::Literal {
+                    bytes: b"wallet_block".to_vec(),
+                },
+                Seed::AccountData {
+                    account_index: 2,
+                    data_index: 32,
+                    length: 32,
+                },
+            ],
+            false,
+            false,
+        ).unwrap(), 
+    ]
+}
+
+
+fn create_empty_extra_metas() -> Vec<u8> {
+    let size = ExtraAccountMetaList::size_of(0).unwrap();
+    let metas: Vec<ExtraAccountMeta> = vec![];
+    let mut data = vec![0; size];
+    ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
+    data
+}
+
+fn create_extra_metas_with_source_wallet_block() -> Vec<u8> {
+    let metas: Vec<ExtraAccountMeta> = get_extra_account_metas_with_source_wallet_block();
+    let size = ExtraAccountMetaList::size_of(metas.len()).unwrap();
+    let mut data = vec![0; size];
+    ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
+    data
+}
+fn create_extra_metas_with_both_wallet_blocks() -> Vec<u8> {
+    let metas: Vec<ExtraAccountMeta> = get_extra_account_metas_with_both_wallet_blocks();
+    let size = ExtraAccountMetaList::size_of(metas.len()).unwrap();
+    let mut data = vec![0; size];
+    ExtraAccountMetaList::init::<ExecuteInstruction>(&mut data, &metas).unwrap();
+    data
+}
+
+fn get_extra_metas_account_data() {
+    let data_empty = create_empty_extra_metas();
+    let data_source_wallet_block = create_extra_metas_with_source_wallet_block();
+    let data_both_wallet_blocks = create_extra_metas_with_both_wallet_blocks();
+
+    println!("data empty: {:?}", data_empty);
+    println!("data source wallet block: {:?}", data_source_wallet_block);
+    println!("data both wallet blocks: {:?}", data_both_wallet_blocks);
+}
+
+async fn get_config(rpc_client: &Arc<RpcClient>) {
+    let config = block_list_client::accounts::Config::find_pda().0;
+    let data = rpc_client.get_account_data(&config).await.unwrap();
+    println!("config: {:?}", data);
+
+    let config = block_list_client::accounts::Config::from_bytes(&data).unwrap();
+    println!("config: {:?}", config);
+}
+
+async fn get_extra_metas(rpc_client: &Arc<RpcClient>, mint_address: &Pubkey) {
+    let extra_metas = block_list_client::accounts::ExtraMetas::find_pda(mint_address).0;
+    let data = rpc_client.get_account_data(&extra_metas).await.unwrap();
+    println!("extra_metas: {:?}", data);
+
+}
+
+async fn process_setup_extra_metas(
+    rpc_client: &Arc<RpcClient>,
+    payer: &Arc<dyn Signer>,
+    mint_address: &Pubkey,
+    check_both_wallets: bool,
+) -> Result<Signature, Box<dyn Error>> {
+    let ix = block_list_client::instructions::SetupExtraMetasBuilder::new()
+        .authority(payer.pubkey())
+        .config(block_list_client::accounts::Config::find_pda().0)
+        .mint(*mint_address)
+        .extra_metas(block_list_client::accounts::ExtraMetas::find_pda(mint_address).0)
+        .check_both_wallets(check_both_wallets)
+        .instruction();
+
+    let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
+    
+    let blockhash = rpc_client
+        .get_latest_blockhash()
+        .await
+        .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
+
+    transaction
+        .try_sign(&[payer], blockhash)
+        .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
+
+    let signature = rpc_client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .map_err(|err| format!("error: send transaction: {}", err))?;
+
+    Ok(signature)
+}
+
+async fn process_init(
+    rpc_client: &Arc<RpcClient>,
+    payer: &Arc<dyn Signer>,
+) -> Result<Signature, Box<dyn Error>> {
+
+    let ix = block_list_client::instructions::InitBuilder::new()
+        .authority(payer.pubkey())
+        .config(block_list_client::accounts::Config::find_pda().0)
+        .instruction();
+
+    let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
+    
+    let blockhash = rpc_client
+        .get_latest_blockhash()
+        .await
+        .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
+
+    transaction
+        .try_sign(&[payer], blockhash)
+        .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
+
+    let signature = rpc_client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .map_err(|err| format!("error: send transaction: {}", err))?;
+
+    Ok(signature)
+}
+
+async fn process_block_wallet(
+    rpc_client: &Arc<RpcClient>,
+    payer: &Arc<dyn Signer>,
+    wallet_address: &Pubkey,
+) -> Result<Signature, Box<dyn Error>> {
+    
+    let ix = block_list_client::instructions::BlockWalletBuilder::new()
+        .authority(payer.pubkey())
+        .config(block_list_client::accounts::Config::find_pda().0)
+        .wallet(*wallet_address)
+        .wallet_block(block_list_client::accounts::WalletBlock::find_pda(wallet_address).0)
+        .instruction();
+
+    let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
+    
+    let blockhash = rpc_client
+        .get_latest_blockhash()
+        .await
+        .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
+
+    transaction
+        .try_sign(&[payer], blockhash)
+        .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
+
+    let signature = rpc_client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .map_err(|err| format!("error: send transaction: {}", err))?;
+
+    Ok(signature)
+}
+
+async fn process_unblock_wallet(
+    rpc_client: &Arc<RpcClient>,
+    payer: &Arc<dyn Signer>,
+    wallet_address: &Pubkey,
+) -> Result<Signature, Box<dyn Error>> {
+    let ix = block_list_client::instructions::UnblockWalletBuilder::new()
+        .authority(payer.pubkey())
+        .config(block_list_client::accounts::Config::find_pda().0)
+        .wallet_block(block_list_client::accounts::WalletBlock::find_pda(wallet_address).0)
+        .instruction();
+
+    let mut transaction = Transaction::new_unsigned(Message::new(&[ix], Some(&payer.pubkey())));
+    
+    let blockhash = rpc_client
+        .get_latest_blockhash()
+        .await
+        .map_err(|err| format!("error: unable to get latest blockhash: {}", err))?;
+
+    transaction
+        .try_sign(&[payer], blockhash)
+        .map_err(|err| format!("error: failed to sign transaction: {}", err))?;
+
+    let signature = rpc_client
+        .send_and_confirm_transaction_with_spinner(&transaction)
+        .await
+        .map_err(|err| format!("error: send transaction: {}", err))?;
+
+    Ok(signature)
+}
+
+
+#[tokio::main]
+async fn main() -> Result<(), Box<dyn Error>> {
+    let app_matches = Command::new(crate_name!())
+        .about(crate_description!())
+        .version(crate_version!())
+        .subcommand_required(true)
+        .arg_required_else_help(true)
+        .arg({
+            let arg = Arg::new("config_file")
+                .short('C')
+                .long("config")
+                .value_name("PATH")
+                .takes_value(true)
+                .global(true)
+                .help("Configuration file to use");
+            if let Some(ref config_file) = *solana_cli_config::CONFIG_FILE {
+                arg.default_value(config_file)
+            } else {
+                arg
+            }
+        })
+        .arg(
+            Arg::new("payer")
+                .long("payer")
+                .value_name("KEYPAIR")
+                .value_parser(SignerSourceParserBuilder::default().allow_all().build())
+                .takes_value(true)
+                .global(true)
+                .help("Filepath or URL to a keypair [default: client keypair]"),
+        )
+        .arg(
+            Arg::new("verbose")
+                .long("verbose")
+                .short('v')
+                .takes_value(false)
+                .global(true)
+                .help("Show additional information"),
+        )
+        .arg(
+            Arg::new("json_rpc_url")
+                .short('u')
+                .long("url")
+                .value_name("URL")
+                .takes_value(true)
+                .global(true)
+                .value_parser(parse_url_or_moniker)
+                .help("JSON RPC URL for the cluster [default: value from configuration file]"),
+        )
+        .subcommand(
+            Command::new("init").about("Initializes the blocklist")
+        )
+        .subcommand(
+            Command::new("block-wallet").about("Blocks a wallet")
+            .arg(
+                Arg::new("wallet_address")
+                    .value_name("WALLET_ADDRESS")
+                    .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
+                    .takes_value(true)
+                    .index(1)
+                    .help("Specify the wallet address to block"),
+            )
+        )
+        .subcommand(
+            Command::new("unblock-wallet").about("Unblocks a wallet")
+            .arg(
+                Arg::new("wallet_address")
+                    .value_name("WALLET_ADDRESS")
+                    .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
+                    .takes_value(true)
+                    .index(1)
+                    .help("Specify the wallet address to unblock"),
+            )
+        )
+        .subcommand(
+            Command::new("get-extra-metas-account-data").about("Gets the extra metas account data")
+        )
+        .subcommand(
+            Command::new("get-config").about("Gets the config account data")
+        )
+        .subcommand(
+            Command::new("get-extra-metas").about("Gets the extra metas account data")
+            .arg(
+                Arg::new("mint_address")
+                .value_name("MINT_ADDRESS")
+                .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
+                .takes_value(true)
+                .index(1)
+                .help("Specify the mint address"),
+            )
+        )
+        .subcommand(
+            Command::new("setup-extra-metas").about("Setup the extra metas account")
+            .arg(
+                Arg::new("mint_address")
+                .value_name("MINT_ADDRESS")
+                .value_parser(SignerSourceParserBuilder::default().allow_pubkey().build())
+                .takes_value(true)
+                .index(1)
+                .help("Specify the mint address"),
+            )
+            .arg(
+                Arg::new("check-both-wallets")
+                .long("check-both-wallets")
+                .short('b')
+                .help("Specify if both wallets should be checked"),
+            )
+        )
+        .get_matches();
+
+    let (command, matches) = app_matches.subcommand().unwrap();
+    let mut wallet_manager: Option<Rc<RemoteWalletManager>> = None;
+
+    let config = {
+        let cli_config = if let Some(config_file) = matches.try_get_one::<String>("config_file")? {
+            solana_cli_config::Config::load(config_file).unwrap_or_default()
+        } else {
+            solana_cli_config::Config::default()
+        };
+
+        let payer = if let Ok(Some((signer, _))) =
+            SignerSource::try_get_signer(matches, "payer", &mut wallet_manager)
+        {
+            Box::new(signer)
+        } else {
+            signer_from_path(
+                matches,
+                &cli_config.keypair_path,
+                "payer",
+                &mut wallet_manager,
+            )?
+        };
+
+        let json_rpc_url = normalize_to_url_if_moniker(
+            matches
+                .get_one::<String>("json_rpc_url")
+                .unwrap_or(&cli_config.json_rpc_url),
+        );
+
+        Config {
+            commitment_config: CommitmentConfig::confirmed(),
+            payer: Arc::from(payer),
+            json_rpc_url,
+            verbose: matches.try_contains_id("verbose")?,
+        }
+    };
+    solana_logger::setup_with_default("solana=info");
+
+    if config.verbose {
+        println!("JSON RPC URL: {}", config.json_rpc_url);
+    }
+    let rpc_client = Arc::new(RpcClient::new_with_commitment(
+        config.json_rpc_url.clone(),
+        config.commitment_config,
+    ));
+
+    match (command, matches) {
+        ("init", _arg_matches) => {
+            let response = process_init(
+                &rpc_client,
+                &config.payer,
+            )
+            .await
+            .unwrap_or_else(|err| {
+                eprintln!("error: init: {}", err);
+                exit(1);
+            });
+            println!("{}", response);
+        }
+        ("block-wallet", arg_matches) => {
+            let wallet_address =
+                SignerSource::try_get_pubkey(arg_matches, "wallet_address", &mut wallet_manager)
+                    .unwrap()
+                    .unwrap();
+            let response = process_block_wallet(
+                &rpc_client,
+                &config.payer,
+                &wallet_address,
+            )
+            .await
+            .unwrap_or_else(|err| {
+                eprintln!("error: init: {}", err);
+                exit(1);
+            });
+            println!("{}", response);
+        }
+        ("unblock-wallet", arg_matches) => {
+            let wallet_address =
+                SignerSource::try_get_pubkey(arg_matches, "wallet_address", &mut wallet_manager)
+                    .unwrap()
+                    .unwrap();
+            let response = process_unblock_wallet(
+                &rpc_client,
+                &config.payer,
+                &wallet_address,
+            )
+            .await
+            .unwrap_or_else(|err| {
+                eprintln!("error: init: {}", err);
+                exit(1);
+            });
+            println!("{}", response);
+        }
+        ("get-extra-metas-account-data", _arg_matches) => {
+            get_extra_metas_account_data();
+        }
+        ("get-config", _arg_matches) => {
+            get_config(&rpc_client).await;
+        }
+        ("get-extra-metas", arg_matches) => {
+            let mint_address =
+                SignerSource::try_get_pubkey(arg_matches, "mint_address", &mut wallet_manager)
+                    .unwrap()
+                    .unwrap();
+            get_extra_metas(&rpc_client, &mint_address).await;
+        }
+        ("setup-extra-metas", arg_matches) => {
+            let mint_address =
+                SignerSource::try_get_pubkey(arg_matches, "mint_address", &mut wallet_manager)
+                    .unwrap()
+                    .unwrap();
+            let check_both_wallets = arg_matches.contains_id("check-both-wallets");
+            let response = process_setup_extra_metas(
+                &rpc_client,
+                &config.payer,
+                &mint_address,
+                check_both_wallets,
+            )
+            .await
+            .unwrap_or_else(|err| {
+                eprintln!("error: setup_extra_metas: {}", err);
+                exit(1);
+            });
+            println!("{}", response);
+        }
+        _ => unreachable!(),
+    };
+
+    Ok(())
+}
+    

+ 294 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/codama.ts

@@ -0,0 +1,294 @@
+import { renderJavaScriptUmiVisitor, renderJavaScriptVisitor, renderRustVisitor } from '@codama/renderers';
+import { accountLinkNode, accountNode, booleanTypeNode, booleanValueNode, constantDiscriminatorNode, constantPdaSeedNodeFromString, constantValueNode, createFromRoot, instructionAccountLinkNode, instructionAccountNode, instructionArgumentNode, instructionNode, numberTypeNode, numberValueNode, optionTypeNode, pdaLinkNode, pdaNode, pdaSeedValueNode, pdaValueNode, programNode, publicKeyTypeNode, publicKeyValueNode, resolverValueNode, rootNode, sizeDiscriminatorNode, sizePrefixTypeNode, stringTypeNode, stringValueNode, structFieldTypeNode, structTypeNode, variablePdaSeedNode } from "codama"
+import path from "path";
+import fs from "fs";
+
+const rustClientsDir = path.join(__dirname, "..", "sdk", "rust");
+const typescriptClientsDir = path.join(
+  __dirname,
+  "..",
+  "sdk",
+  "ts",
+);
+
+const root = rootNode(
+    programNode({
+        name: "block-list",
+        publicKey: "BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf",
+        version: "1.0.0",
+        accounts: [
+          accountNode({
+            name: "config",
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0))),
+            ],
+            size: 41,
+            pda: pdaLinkNode("config"),
+            docs: ["The config PDA account"],
+            data: structTypeNode([
+              structFieldTypeNode({
+                name: "discriminator",
+                type: numberTypeNode("u8"),
+                defaultValueStrategy: "omitted",
+              }),
+              structFieldTypeNode({
+                name: "authority",
+                type: publicKeyTypeNode(),
+              }),
+              structFieldTypeNode({
+                name: "blocked_wallets_count",
+                type: numberTypeNode("u64"),
+              }),
+            ]),
+          }),
+          accountNode({
+            name: "walletBlock",
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(1))),
+            ],
+            size: 33,
+            pda: pdaLinkNode("walletBlock"),
+            docs: ["The config PDA account"],
+            data: structTypeNode([
+              structFieldTypeNode({
+                name: "authority",
+                type: publicKeyTypeNode(),
+              }),
+            ])
+          }),
+          accountNode({
+            name: "extraMetas",
+            pda: pdaLinkNode("extraMetas"),
+            docs: ["The extra metas PDA account"],
+          })
+        ],
+        instructions: [
+          instructionNode({
+            name: "init",
+            arguments: [
+              instructionArgumentNode({
+                  name: 'discriminator',
+                  type: numberTypeNode('u8'),
+                  defaultValue: numberValueNode(0xF1),
+                  defaultValueStrategy: 'omitted',
+              }),
+            ],
+            accounts: [
+              instructionAccountNode({
+                name: "authority",
+                isSigner: true,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "config",
+                isSigner: false,
+                isWritable: true,
+                defaultValue: pdaValueNode(pdaLinkNode("config"))
+              }),
+              instructionAccountNode({
+                name: "systemProgram",
+                defaultValue: publicKeyValueNode("11111111111111111111111111111111", "systemProgram"),
+                isSigner: false,
+                isWritable: false,
+              })
+            ],
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0xF1))),
+            ],
+            docs: ["Initialize the config PDA account"],
+          }),
+          instructionNode({
+            name: "blockWallet",
+            arguments: [
+              instructionArgumentNode({
+                  name: 'discriminator',
+                  type: numberTypeNode('u8'),
+                  defaultValue: numberValueNode(0xF2),
+                  defaultValueStrategy: 'omitted',
+              }),
+            ],
+            accounts: [
+              instructionAccountNode({
+                name: "authority",
+                isSigner: true,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "config",
+                isSigner: false,
+                isWritable: true,
+                defaultValue: pdaValueNode(pdaLinkNode("config"))
+              }),
+              instructionAccountNode({
+                name: "wallet",
+                isSigner: false,
+                isWritable: false,
+              }),
+              instructionAccountNode({
+                name: "walletBlock",
+                isSigner: false,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "systemProgram",
+                defaultValue: publicKeyValueNode("11111111111111111111111111111111", "systemProgram"),
+                isSigner: false,
+                isWritable: false,
+              })
+            ],
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0xF2))),
+            ],
+            docs: ["Block a wallet"],
+          }),
+          instructionNode({
+            name: "unblockWallet",
+            arguments: [
+              instructionArgumentNode({
+                  name: 'discriminator',
+                  type: numberTypeNode('u8'),
+                  defaultValue: numberValueNode(0xF3),
+                  defaultValueStrategy: 'omitted',
+              }),
+            ],
+            accounts: [
+              instructionAccountNode({
+                name: "authority",
+                isSigner: true,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "config",
+                isSigner: false,
+                isWritable: true,
+                defaultValue: pdaValueNode(pdaLinkNode("config"))
+              }),
+              instructionAccountNode({
+                name: "walletBlock",
+                isSigner: false,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "systemProgram",
+                defaultValue: publicKeyValueNode("11111111111111111111111111111111", "systemProgram"),
+                isSigner: false,
+                isWritable: false,
+              })
+            ],
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0xF3))),
+            ],
+            docs: ["Unblock a wallet"],
+          }),
+          instructionNode({
+            name: "setupExtraMetas",
+            arguments: [
+              instructionArgumentNode({
+                  name: 'discriminator',
+                  type: numberTypeNode('u8'),
+                  defaultValue: numberValueNode(0x6A),
+                  defaultValueStrategy: 'omitted',
+              }),
+              instructionArgumentNode({
+                name: 'checkBothWallets',
+                type: booleanTypeNode(),
+                defaultValue: booleanValueNode(false),
+                defaultValueStrategy: 'optional',
+              }),
+            ],
+            accounts: [
+              instructionAccountNode({
+                name: "authority",
+                isSigner: true,
+                isWritable: true,
+              }),
+              instructionAccountNode({
+                name: "config",
+                isSigner: false,
+                isWritable: false,
+                defaultValue: pdaValueNode(pdaLinkNode("config"))
+              }),
+              instructionAccountNode({
+                name: "mint",
+                isSigner: false,
+                isWritable: false,
+              }),
+              instructionAccountNode({
+                name: "extraMetas",
+                isSigner: false,
+                isWritable: true,
+                defaultValue: pdaValueNode(pdaLinkNode("extraMetas"))
+              }),
+              instructionAccountNode({
+                name: "systemProgram",
+                defaultValue: publicKeyValueNode("11111111111111111111111111111111", "systemProgram"),
+                isSigner: false,
+                isWritable: false,
+              })
+            ],
+            discriminators: [
+              constantDiscriminatorNode(constantValueNode(numberTypeNode("u8"), numberValueNode(0x6A))),
+            ],
+            docs: ["Unblock a wallet"],
+          }),
+        ],
+        pdas: [
+          pdaNode({
+            name: "config",
+            seeds: [constantPdaSeedNodeFromString("utf8", "config")],
+            docs: ["The config PDA account"],
+          }),
+          pdaNode({
+            name: "walletBlock",
+            seeds: [
+              constantPdaSeedNodeFromString("utf8", "wallet_block"),
+              variablePdaSeedNode("wallet", publicKeyTypeNode()),
+            ],
+            docs: ["The wallet block PDA account"],
+          }),
+          pdaNode({
+            name: "extraMetas",
+            seeds: [
+              constantPdaSeedNodeFromString("utf8", "extra-account-metas"),
+              variablePdaSeedNode("mint", publicKeyTypeNode()),
+            ],
+            docs: ["The extra metas PDA account"],
+          }),
+        ]
+    })
+);
+
+
+function preserveConfigFiles() {
+    const filesToPreserve = ['package.json', 'tsconfig.json', '.npmignore', 'pnpm-lock.yaml', 'Cargo.toml'];
+    const preservedFiles = new Map();
+    
+    filesToPreserve.forEach(filename => {
+      const filePath = path.join(typescriptClientsDir, filename);
+      const tempPath = path.join(typescriptClientsDir, `${filename}.temp`);
+      
+      if (fs.existsSync(filePath)) {
+        fs.copyFileSync(filePath, tempPath);
+        preservedFiles.set(filename, tempPath);
+      }
+    });
+    
+    return {
+      restore: () => {
+        preservedFiles.forEach((tempPath, filename) => {
+          const filePath = path.join(typescriptClientsDir, filename);
+          if (fs.existsSync(tempPath)) {
+            fs.copyFileSync(tempPath, filePath);
+            fs.unlinkSync(tempPath);
+          }
+        });
+      }
+    };
+  }
+
+const codama = createFromRoot(root)
+
+const configPreserver = preserveConfigFiles();
+
+codama.accept(renderJavaScriptVisitor('sdk/ts/src', { formatCode: true }));
+codama.accept(renderRustVisitor('sdk/rust/src/client', { crateFolder: 'sdk/rust/', formatCode: true }));

+ 25 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/package.json

@@ -0,0 +1,25 @@
+{
+  "name": "block-list",
+  "version": "1.0.0",
+  "main": "index.js",
+  "license": "MIT",
+  "engines": {
+    "node": ">=20.18.0"
+  },
+  "scripts": {
+    "generate-sdks": "pnpx tsx codama.ts",
+    "test": "mocha --import=tsx ./tests/test.spec.ts"
+  },
+  "dependencies": {
+    "@codama/renderers": "^1.0.19",
+    "@types/mocha": "^10.0.10",
+    "codama": "^1.2.11",
+    "litesvm": "^0.2.0",
+    "mocha": "^11.1.0",
+    "solana-bankrun": "^0.4.0",
+    "tsx": "^4.19.3"
+  },
+  "devDependencies": {
+    "@types/node": "^22.15.29"
+  }
+}

+ 15 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/Cargo.toml

@@ -0,0 +1,15 @@
+[package]
+name = "block-list"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["lib", "cdylib"]
+name = "block_list"
+
+[dependencies]
+bytemuck = "1.23.0"
+pinocchio = "0.8.4"
+pinocchio-pubkey = "0.2.4"
+pinocchio-system = "0.2.3"
+pinocchio-log = "0.4.0"

+ 25 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/error.rs

@@ -0,0 +1,25 @@
+use pinocchio::program_error::ProgramError;
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum BlockListError {
+    InvalidInstruction,
+
+    InvalidAuthority,
+    AccountBlocked,
+    NotEnoughAccounts,
+    InvalidAccountData,
+    UninitializedAccount,
+    InvalidSystemProgram,
+    InvalidConfigAccount,
+    AccountNotWritable,
+    InvalidMint,
+    InvalidExtraMetasAccount,
+    ImmutableOwnerExtensionMissing,
+}
+
+
+impl From<BlockListError> for ProgramError {
+    fn from(e: BlockListError) -> Self {
+        ProgramError::Custom(e as u32)
+    }
+}

+ 87 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/block_wallet.rs

@@ -0,0 +1,87 @@
+use pinocchio::{account_info::AccountInfo, instruction::Signer, program_error::ProgramError, pubkey::find_program_address, seeds, sysvars::{rent::Rent, Sysvar}, ProgramResult};
+
+use crate::{load, load_mut_unchecked, BlockListError, Config, Discriminator, Transmutable, WalletBlock};
+
+
+pub struct BlockWallet<'a> {
+    pub authority: &'a AccountInfo,
+    pub config: &'a AccountInfo,
+    pub wallet: &'a AccountInfo,
+    pub wallet_block: &'a AccountInfo,
+    pub system_program: &'a AccountInfo,
+    pub wallet_block_bump: u8,
+}
+
+impl<'a> BlockWallet<'a> {
+    pub fn process(&self) -> ProgramResult {
+        let lamports = Rent::get()?.minimum_balance(WalletBlock::LEN);
+
+        let bump_seed = [self.wallet_block_bump];
+        let seeds = seeds!(WalletBlock::SEED_PREFIX, self.wallet.key(), &bump_seed);
+        let signer = Signer::from(&seeds);
+            
+        pinocchio_system::instructions::CreateAccount {
+            from: self.authority,
+            to: self.wallet_block,
+            lamports,
+            space: WalletBlock::LEN as u64,
+            owner: &crate::ID,
+        }.invoke_signed(&[signer])?;
+
+        let mut data = self.wallet_block.try_borrow_mut_data()?;
+        let wallet_block = unsafe { 
+            load_mut_unchecked::<WalletBlock>(&mut data)? 
+        };
+        wallet_block.discriminator = WalletBlock::DISCRIMINATOR;
+        wallet_block.address = *self.wallet.key();
+
+        let config = unsafe { load_mut_unchecked::<Config>(self.config.borrow_mut_data_unchecked())? };
+        config.blocked_wallets_count = config.blocked_wallets_count.checked_add(1).ok_or(ProgramError::ArithmeticOverflow)?;
+        
+        Ok(())
+    }
+}
+
+impl<'a> Discriminator for BlockWallet<'a> {
+    const DISCRIMINATOR: u8 = 0xF2;
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for BlockWallet<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+        let [authority, config, wallet, wallet_block, system_program] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        let cfg = unsafe { load::<Config>(config.borrow_data_unchecked())? };
+        
+        if !config.is_owned_by(&crate::ID) {
+            return Err(BlockListError::InvalidConfigAccount);
+        }
+
+        if !authority.is_signer() || cfg.authority.ne(authority.key()) {
+            return Err(BlockListError::InvalidAuthority);
+        }
+
+        if !config.is_writable() && !wallet_block.is_writable() {
+            return Err(BlockListError::AccountNotWritable);
+        }
+
+        let (_, wallet_block_bump) = find_program_address(&[WalletBlock::SEED_PREFIX, wallet.key()], &crate::ID);
+
+        // check if system program is valid
+        if system_program.key().ne(&pinocchio_system::ID) {
+            return Err(BlockListError::InvalidSystemProgram);
+        }
+
+        Ok(Self {
+            authority,
+            config,
+            wallet,
+            wallet_block,
+            system_program,
+            wallet_block_bump,
+        })
+    }
+}

+ 85 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/init.rs

@@ -0,0 +1,85 @@
+use pinocchio::{account_info::AccountInfo, instruction::Signer, pubkey::find_program_address, seeds, sysvars::{rent::Rent, Sysvar}, ProgramResult};
+
+use crate::{load_mut_unchecked, BlockListError, Config, Discriminator, Transmutable};
+
+
+
+pub struct Init<'a> {
+    pub authority: &'a AccountInfo,
+    pub config: &'a AccountInfo,
+    pub system_program: &'a AccountInfo,
+    pub config_bump: u8,
+}
+
+impl<'a> Discriminator for Init<'a> {
+    const DISCRIMINATOR: u8 = 0xF1;
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for Init<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+        let [authority, config, system_program] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        if !authority.is_signer() {
+            return Err(BlockListError::InvalidAuthority);
+        }
+
+        /* do we really need to check this? its going to fail silently if not writable
+        if !config.is_writable {
+            return Err(BlockListError::InvalidInstruction);
+        }*/
+
+
+        // derive config account
+        let (_, config_bump) = find_program_address(&[Config::SEED_PREFIX], &crate::ID);
+        // no need to check if address is valid
+        // cpi call with config as signer, runtime will check if the right account has been signer escalated
+
+        //if config_account.ne(config.key()) {
+        //    return Err(BlockListError::InvalidConfigAccount);
+        //}
+
+        // check if system program is valid
+        if system_program.key().ne(&pinocchio_system::ID) {
+            return Err(BlockListError::InvalidSystemProgram);
+        }
+
+
+        Ok(Self {
+            authority,
+            config,
+            system_program,
+            config_bump,
+        })
+    }
+}
+
+impl<'a> Init<'a> {
+    pub fn process(&self) -> ProgramResult {
+        let lamports = Rent::get()?.minimum_balance(Config::LEN);
+
+        let bump_seed = [self.config_bump];
+        let seeds = seeds!(Config::SEED_PREFIX, &bump_seed);
+        let signer = Signer::from(&seeds);
+            
+        pinocchio_system::instructions::CreateAccount {
+            from: self.authority,
+            to: self.config,
+            lamports,
+            space: Config::LEN as u64,
+            owner: &crate::ID,
+        }.invoke_signed(&[signer])?;
+
+        let mut data = self.config.try_borrow_mut_data()?;
+        let config = unsafe { 
+            load_mut_unchecked::<Config>(&mut data)? 
+        };
+        config.discriminator = Config::DISCRIMINATOR;
+        config.authority = *self.authority.key();
+
+        Ok(())
+    }
+}

+ 11 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/mod.rs

@@ -0,0 +1,11 @@
+pub mod tx_hook;
+pub mod init;
+pub mod block_wallet;
+pub mod unblock_wallet;
+pub mod setup_extra_metas;
+
+pub use tx_hook::*;
+pub use init::*;
+pub use block_wallet::*;
+pub use unblock_wallet::*;
+pub use setup_extra_metas::*;

+ 133 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/setup_extra_metas.rs

@@ -0,0 +1,133 @@
+use pinocchio::{account_info::AccountInfo, instruction::Signer, memory::sol_memcpy, pubkey::find_program_address, seeds, sysvars::{rent::Rent, Sysvar}, ProgramResult};
+
+use crate::{load, token2022_utils::{get_transfer_hook_authority, EXTRA_METAS_SEED, is_token_2022_mint}, BlockListError, Config, Discriminator};
+
+
+pub struct SetupExtraMetas<'a> {
+    pub authority: &'a AccountInfo,
+    pub config: &'a AccountInfo,
+    pub mint: &'a AccountInfo,
+    pub extra_metas: &'a AccountInfo,
+    pub system_program: &'a AccountInfo,
+    pub extra_metas_bump: u8,
+}
+
+impl<'a> Discriminator for SetupExtraMetas<'a> {
+    const DISCRIMINATOR: u8 = 0x6A;
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for SetupExtraMetas<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+        let [authority, config, mint, extra_metas, system_program] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        if !authority.is_signer() {
+            return Err(BlockListError::InvalidAuthority);
+        }
+
+        if !is_token_2022_mint(mint) {
+            return Err(BlockListError::InvalidMint);
+        }
+
+        let transfer_hook_authority = get_transfer_hook_authority(unsafe { mint.borrow_data_unchecked() });
+        if transfer_hook_authority.is_none() || !transfer_hook_authority.unwrap().eq(authority.key()) {
+            return Err(BlockListError::InvalidAuthority);
+        }
+
+        // derive extra_metas account
+        let (extra_metas_address, extra_metas_bump) = find_program_address(&[EXTRA_METAS_SEED, mint.key()], &crate::ID);
+
+        if extra_metas_address.ne(extra_metas.key()) {
+            return Err(BlockListError::InvalidExtraMetasAccount);
+        }
+
+        // check if system program is valid
+        if system_program.key().ne(&pinocchio_system::ID) {
+            return Err(BlockListError::InvalidSystemProgram);
+        }
+
+        Ok(Self {
+            authority,
+            config,
+            mint,
+            extra_metas,
+            system_program,
+            extra_metas_bump,
+        })
+    }
+}
+
+impl<'a> SetupExtraMetas<'a> {
+    pub fn process(&self, remaining_data: &[u8]) -> ProgramResult {
+        let config = unsafe { load::<Config>(&self.config.borrow_data_unchecked())? };
+        
+        let data = if config.blocked_wallets_count == 0 {
+            EXTRA_METAS_EMPTY_DEPENDENCIES
+        } else if remaining_data.len() == 1 && remaining_data[0] == 1 {
+            EXTRA_METAS_BOTH_DEPENDENCIES
+        } else {
+            EXTRA_METAS_SOURCE_DEPENDENCY
+        };
+
+        let min_lamports = Rent::get()?.minimum_balance(data.len());
+
+        if self.extra_metas.is_owned_by(&crate::ID) {
+            let current_lamports = self.extra_metas.lamports();
+            let auth_lamports = self.authority.lamports();
+
+            // just resize
+            self.extra_metas.realloc(data.len(), false)?;
+
+            if current_lamports < min_lamports {
+                // transfer to extra
+                let diff = min_lamports - current_lamports;
+                pinocchio_system::instructions::Transfer {
+                    from: self.authority,
+                    to: self.extra_metas,
+                    lamports: diff,
+                }.invoke()?;
+            } else if current_lamports > min_lamports {
+                // transfer from extra
+                let diff = current_lamports - min_lamports;
+                unsafe {
+                    *self.extra_metas.borrow_mut_lamports_unchecked() = min_lamports;
+                    *self.authority.borrow_mut_lamports_unchecked() = auth_lamports.checked_add(diff).unwrap();
+                }
+            }
+        } else {
+            // create new account
+
+            let bump_seed = [self.extra_metas_bump];
+            let seeds = seeds!(EXTRA_METAS_SEED, self.mint.key(), &bump_seed);
+            let signer = Signer::from(&seeds);
+                
+            pinocchio_system::instructions::CreateAccount {
+                from: self.authority,
+                to: self.extra_metas,
+                lamports: min_lamports,
+                space: data.len() as u64,
+                owner: &crate::ID,
+            }.invoke_signed(&[signer])?;
+        }
+
+        // overwrite state depending on config
+
+        let extra_metas_data = unsafe { self.extra_metas.borrow_mut_data_unchecked() };
+
+        unsafe { sol_memcpy(extra_metas_data, data, data.len()); }
+
+        Ok(())
+    }
+}
+
+
+/// HOW TO GET THESE MAGIC VALUES
+/// run the CLI using `block-list-cli get-extra-metas-account-data`
+/// it will generate the 3 arrays without needing to add more dependencies (bloat) to the program
+pub const EXTRA_METAS_EMPTY_DEPENDENCIES: &[u8] = &[105, 37, 101, 197, 75, 251, 102, 26, 4, 0, 0, 0, 0, 0, 0, 0];
+pub const EXTRA_METAS_SOURCE_DEPENDENCY: &[u8] = &[105, 37, 101, 197, 75, 251, 102, 26, 39, 0, 0, 0, 1, 0, 0, 0, 1, 1, 12, 119, 97, 108, 108, 101, 116, 95, 98, 108, 111, 99, 107, 4, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+pub const EXTRA_METAS_BOTH_DEPENDENCIES: &[u8] = &[105, 37, 101, 197, 75, 251, 102, 26, 74, 0, 0, 0, 2, 0, 0, 0, 1, 1, 12, 119, 97, 108, 108, 101, 116, 95, 98, 108, 111, 99, 107, 4, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 12, 119, 97, 108, 108, 101, 116, 95, 98, 108, 111, 99, 107, 4, 2, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+

+ 135 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/tx_hook.rs

@@ -0,0 +1,135 @@
+use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio_log::logger::Logger;
+
+use crate::{load, token2022_utils::has_immutable_owner_extension, BlockListError, WalletBlock};
+
+///
+/// SECURITY ASSUMPTIONS OVER TX-HOOK
+/// 
+/// 1- its called by the token-2022 program
+/// 2- if some other program is calling it, we don't care as we don't write state here
+/// 2- its inputs are already sanitized by the token-2022 program
+/// 3- if some other program is calling it with invalid inputs, we don't care as we only read state and return ok/nok
+/// 4- there may be 3 different extra metas setup
+/// 4.1- no extra accounts
+/// 4.2- only source wallet block
+/// 4.3- both source and destination wallet blocks
+/// 5- given all the above we can skip a lot of type and owner checks
+
+pub struct TxHook<'a> {
+    pub source: &'a AccountInfo,
+    pub mint: &'a AccountInfo,
+    pub destination: &'a AccountInfo,
+    pub authority: &'a AccountInfo,
+    pub source_wallet_block: Option<&'a AccountInfo>,
+    pub destination_wallet_block: Option<&'a AccountInfo>,
+    //pub remaining_accounts: &'a [AccountInfo],
+}
+
+impl<'a> TxHook<'a> {
+    pub const DISCRIMINATOR: u8 = 0x69;
+
+    pub fn process(&self) -> ProgramResult {
+        // check if there is a wallet block for the source account
+        if let Some(source_wallet_block) = self.source_wallet_block {
+            let source_data = unsafe {self.source.borrow_data_unchecked()};
+            // without the immutable owner extension, TA owners could bypass wallet blocks
+            // by changing the owner to a different wallet controlled by the same entity
+            if !has_immutable_owner_extension(source_data) {
+                let mut logger = Logger::<64>::default();
+                logger.append("Transfer Blocked: Source TA - ImmutableOwnerExtensionMissing");
+                logger.log();
+                return Err(BlockListError::ImmutableOwnerExtensionMissing.into());
+            }
+
+            if !source_wallet_block.data_is_empty() {
+
+                let _ = unsafe { load::<WalletBlock>(source_wallet_block.borrow_data_unchecked())? };
+
+                // its a potential blocked wallet
+                // lets check if authority is not the owner nor the delegate
+                // this implies its the permanent delegate
+                // alternatively we can decode the mint and get the permanent delegate
+
+                let owner = unsafe { &*(source_data[32..64].as_ptr() as *const Pubkey) };
+                let delegate = unsafe { &*(source_data[76..108].as_ptr() as *const Pubkey) };
+
+                if owner.eq(self.authority.key()) || delegate.eq(self.authority.key()) {
+                    let mut logger = Logger::<64>::default();
+                    logger.append("Transfer Blocked:  Source TA - AccountBlocked");
+                    logger.log();
+                    return Err(BlockListError::AccountBlocked.into());
+                }
+                
+            }
+
+        }
+
+        // check if there is a wallet block for the destination account
+        if let Some(destination_wallet_block) = self.destination_wallet_block {
+
+            if !has_immutable_owner_extension(unsafe {self.destination.borrow_data_unchecked()}) {
+                let mut logger = Logger::<64>::default();
+                logger.append("Transfer Blocked: Destination TA - ImmutableOwnerExtensionMissing");
+                logger.log();
+                return Err(BlockListError::ImmutableOwnerExtensionMissing.into());
+            }
+
+            if !destination_wallet_block.data_is_empty() {
+
+                let _ = unsafe { load::<WalletBlock>(destination_wallet_block.borrow_data_unchecked())? };
+                
+                let mut logger = Logger::<64>::default();
+                logger.append("Transfer Blocked:  Destination TA - AccountBlocked");
+                logger.log();
+
+                return Err(BlockListError::AccountBlocked.into());
+            }
+
+        }
+
+        Ok(())
+    }
+
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for TxHook<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+
+        /*
+        TX HOOK GETS CALLED WITH:
+         1- source TA
+         2- mint
+         3- destination TA
+         4- authority (either src owner or src delegate)
+         5- extra account metas
+         6- (optional) source wallet block 
+         7- (optional) destination wallet block 
+         */
+
+        let [source, mint, destination, authority, remaining_accounts @ ..] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        let (source_wallet_block, destination_wallet_block) = if remaining_accounts.len() == 2 {
+            (Some(&remaining_accounts[1]), None)
+        } else if remaining_accounts.len() == 3 {
+            (Some(&remaining_accounts[1]), Some(&remaining_accounts[2]))
+        } else {
+            (None, None)
+        };
+
+
+
+        Ok(Self {
+            source,
+            destination,
+            mint,
+            authority,
+            source_wallet_block,
+            destination_wallet_block,
+        })
+    }
+}

+ 69 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/unblock_wallet.rs

@@ -0,0 +1,69 @@
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
+
+use crate::{load, load_mut_unchecked, BlockListError, Config, Discriminator, WalletBlock};
+
+
+pub struct UnblockWallet<'a> {
+    pub authority: &'a AccountInfo,
+    pub config: &'a AccountInfo,
+    pub wallet_block: &'a AccountInfo,
+    pub system_program: &'a AccountInfo,
+}
+
+impl<'a> UnblockWallet<'a> {
+    pub fn process(&self) -> ProgramResult {
+        
+        let destination_lamports = self.authority.lamports();
+
+        unsafe {
+            *self.authority.borrow_mut_lamports_unchecked() = destination_lamports
+                .checked_add(self.wallet_block.lamports())
+                .ok_or(ProgramError::ArithmeticOverflow)?;
+            self.wallet_block.close_unchecked();
+        }
+        
+        let config = unsafe { load_mut_unchecked::<Config>(self.config.borrow_mut_data_unchecked())? };
+        config.blocked_wallets_count = config.blocked_wallets_count.checked_sub(1).ok_or(ProgramError::ArithmeticOverflow)?;
+
+        Ok(())
+    }
+}
+
+impl<'a> Discriminator for UnblockWallet<'a> {
+    const DISCRIMINATOR: u8 = 0xF3;
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for UnblockWallet<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+        let [authority, config, wallet_block, system_program] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        let cfg = unsafe { load::<Config>(config.borrow_data_unchecked())? };
+        
+        if !config.is_owned_by(&crate::ID) {
+            return Err(BlockListError::InvalidConfigAccount);
+        }
+
+        if !authority.is_signer() || cfg.authority.ne(authority.key()) {
+            return Err(BlockListError::InvalidAuthority);
+        }
+        
+        if !config.is_writable() && !wallet_block.is_writable() {
+            return Err(BlockListError::AccountNotWritable);
+        }
+
+        if unsafe { load::<WalletBlock>(wallet_block.borrow_data_unchecked()).is_err() }{
+            return Err(BlockListError::InvalidAccountData);
+        }
+
+        Ok(Self {
+            authority,
+            config,
+            wallet_block,
+            system_program,
+        })
+    }
+}

+ 42 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/lib.rs

@@ -0,0 +1,42 @@
+#![no_std]
+ 
+use pinocchio::{account_info::AccountInfo, no_allocator, nostd_panic_handler, program_entrypoint, program_error::ProgramError, pubkey::Pubkey, ProgramResult};
+use pinocchio_pubkey::declare_id;
+ 
+program_entrypoint!(process_instruction);
+// Do not allocate memory.
+no_allocator!();
+// Use the no_std panic handler.
+nostd_panic_handler!();
+ 
+pub mod instructions;
+pub use instructions::*;
+pub mod error;
+pub use error::*;
+pub mod state;
+pub use state::*;
+mod token2022_utils;
+
+declare_id!("BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf");
+
+ 
+#[inline(always)]
+fn process_instruction(
+    _program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    let [disc, remaining_data @ ..] = instruction_data else {
+        return Err(BlockListError::InvalidInstruction.into());
+    };
+    
+    
+    match *disc {
+        TxHook::DISCRIMINATOR => TxHook::try_from(accounts)?.process(),
+        Init::DISCRIMINATOR => Init::try_from(accounts)?.process(),
+        BlockWallet::DISCRIMINATOR => BlockWallet::try_from(accounts)?.process(),
+        UnblockWallet::DISCRIMINATOR => UnblockWallet::try_from(accounts)?.process(),
+        SetupExtraMetas::DISCRIMINATOR => SetupExtraMetas::try_from(accounts)?.process(remaining_data),
+        _ => Err(ProgramError::InvalidInstructionData),
+    }
+}

+ 24 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/config.rs

@@ -0,0 +1,24 @@
+use pinocchio::pubkey::Pubkey;
+
+use super::{Discriminator, Transmutable};
+
+
+#[repr(C)]
+pub struct Config {
+    pub discriminator: u8,
+    pub authority: Pubkey,
+    pub blocked_wallets_count: u64,
+}
+
+impl Config {
+    pub const SEED_PREFIX: &'static [u8] = b"config";
+}
+
+impl Transmutable for Config {
+    const LEN: usize = 1 + 32 + 8;
+}
+
+impl Discriminator for Config {
+    const DISCRIMINATOR: u8 = 0x01;
+}
+

+ 64 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/mod.rs

@@ -0,0 +1,64 @@
+pub mod config;
+pub mod wallet_block;
+pub use config::*;
+pub use wallet_block::*;
+
+use crate::BlockListError;
+
+pub trait Transmutable {
+    const LEN: usize;
+}
+
+pub trait Discriminator {
+    const DISCRIMINATOR: u8;
+}
+
+/// Return a reference for an initialized `T` from the given bytes.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load<T: Discriminator + Transmutable>(bytes: &[u8]) -> Result<&T, BlockListError> {
+    load_unchecked(bytes).and_then(|t: &T| {
+        // checks if the data is initialized
+        if bytes[0] == T::DISCRIMINATOR {
+            Ok(t)
+        } else {
+            Err(BlockListError::InvalidAccountData)
+        }
+    })
+}
+
+/// Return a `T` reference from the given bytes.
+///
+/// This function does not check if the data is initialized.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load_unchecked<T: Transmutable>(bytes: &[u8]) -> Result<&T, BlockListError> {
+    if bytes.len() != T::LEN {
+        return Err(BlockListError::InvalidAccountData);
+    }
+    Ok(&*(bytes.as_ptr() as *const T))
+}
+
+/// Return a mutable `T` reference from the given bytes.
+///
+/// This function does not check if the data is initialized.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load_mut_unchecked<T: Transmutable>(
+    bytes: &mut [u8],
+) -> Result<&mut T, BlockListError> {
+    if bytes.len() != T::LEN {
+        return Err(BlockListError::InvalidAccountData);
+    }
+    Ok(&mut *(bytes.as_mut_ptr() as *mut T))
+}
+

+ 21 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/wallet_block.rs

@@ -0,0 +1,21 @@
+use pinocchio::pubkey::Pubkey;
+
+use super::{Discriminator, Transmutable};
+
+#[repr(C)]
+pub struct WalletBlock {
+    pub discriminator: u8,
+    pub address: Pubkey,
+}
+
+impl WalletBlock {
+    pub const SEED_PREFIX: &'static [u8] = b"wallet_block";
+}
+
+impl Transmutable for WalletBlock {
+    const LEN: usize = 1 + 32;
+}
+
+impl Discriminator for WalletBlock {
+    const DISCRIMINATOR: u8 = 0x02;
+}

+ 57 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/token2022_utils.rs

@@ -0,0 +1,57 @@
+use pinocchio::{account_info::AccountInfo, pubkey::Pubkey};
+use pinocchio_pubkey::from_str;
+
+pub const TOKEN_2022_PROGRAM_ID: Pubkey = from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
+
+pub const EXTRA_METAS_SEED: &[u8] = b"extra-account-metas";
+
+const MINT_LEN: usize = 82;
+const EXTENSIONS_PADDING: usize = 83;
+const EXTENSION_START_OFFSET: usize = 1;
+const EXTENSION_LENGTH_LEN: usize = 2;
+const EXTENSION_TYPE_LEN: usize = 2;
+const TRANSFER_HOOK_EXTENSION_TYPE: u16 = 14;
+const IMMUTABLE_OWNER_EXTENSION_TYPE: u16 = 7;
+const TYPE_BYTE_OFFSET: usize = MINT_LEN + EXTENSIONS_PADDING;
+const EXTENSION_DATA_OFFSET: usize = TYPE_BYTE_OFFSET + EXTENSION_START_OFFSET;
+const MINT_TYPE_BYTE: u8 = 1;
+
+pub fn get_transfer_hook_authority(acc_data_bytes: &[u8]) -> Option<&Pubkey> {
+    let extension_data = get_extension_data_(acc_data_bytes, TRANSFER_HOOK_EXTENSION_TYPE);
+    if let Some(data) = extension_data {
+        return Some( unsafe { &*(data.as_ptr() as *const Pubkey) });
+    }
+    None
+}
+
+fn get_extension_data_(acc_data_bytes: &[u8], extension_type: u16) -> Option<&[u8]> {
+    let ext_bytes = &acc_data_bytes[EXTENSION_DATA_OFFSET..];
+    let mut start = 0;
+    let end = ext_bytes.len();
+    while start < end {
+        let ext_type_idx = start;
+        let ext_len_idx = ext_type_idx + 2;
+        let ext_data_idx = ext_len_idx + EXTENSION_LENGTH_LEN;
+
+        let ext_type = unsafe { &*(ext_bytes[ext_type_idx..].as_ptr() as *const u16) };
+        let ext_len = unsafe { &*(ext_bytes[ext_len_idx..].as_ptr() as *const u16) };
+
+        if *ext_type == extension_type {
+            return Some(&ext_bytes[ext_data_idx..ext_data_idx + *ext_len as usize]);
+        }
+
+        start = start + EXTENSION_TYPE_LEN + EXTENSION_LENGTH_LEN + *ext_len as usize;
+    }
+    None
+}
+
+pub fn has_immutable_owner_extension(acc_data_bytes: &[u8]) -> bool {
+    let extension_data = get_extension_data_(acc_data_bytes, IMMUTABLE_OWNER_EXTENSION_TYPE);
+    extension_data.is_some()
+}
+
+pub fn is_token_2022_mint(mint: &AccountInfo) -> bool {
+    let data = unsafe { mint.borrow_data_unchecked() };
+    let mint_type_byte = data[TYPE_BYTE_OFFSET];
+    data.len() > TYPE_BYTE_OFFSET && mint_type_byte == MINT_TYPE_BYTE && mint.is_owned_by(&TOKEN_2022_PROGRAM_ID)
+}

+ 143 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/readme.md

@@ -0,0 +1,143 @@
+# Block List 
+
+This is a Block List program that implements the Token2022 Transfer-hook execute instruction.
+It allows a centralized authority to defined a block list - a collection of wallets that are blocked.
+Token issuers (transfer-hook extension authorities), can then setup this program as the hook to be used and choose an operation mode (either filter source wallet, or both source and destination).
+
+## Operation Mode
+
+The Block list has different operation modes depending whether the block list is empty or not and the issuer choice. These modes are achieved by building a different `extra-account-metas` account for the token mint (see `setup_extra_metas` bellow). When the list gets the first blocked wallet, the issuer needs to re-set the `extra-account-metas`.
+The modes are the following: 
+- Empty extra metas - default behaviour when config account counter is 0
+- Check Source - default behaviour when config account counter is above 0
+- Check both source and destination - optional behaviour when config account counter is above 0
+
+## Accounts
+
+### Config
+- Defines the block list authority. 
+- Tracks the number of blocked wallets.
+
+### WalletBlock
+- Defines a wallet as blocked
+
+## Instructions
+
+### init
+
+Initializes the global `Config` account with a given authority to control the block list.
+
+### block_wallet
+
+Adds a given wallet address to the blocked wallets. This creates a `WalletBlock` reccord account.
+
+### unblock_wallet
+
+Removes a given wallet address from the blocked wallets. This removes a `WalletBlock` reccord account.
+
+### setup_extra_metas
+
+Sets up the `extra-account-metas` account dependency for the Transfer-Hook extension. Receives an optional bool value to switch operation modes when the blocked wallet counter is non zero.
+Note: once wallets are added to the block list, the issuer needs to call this method again to setup one of the blocking modes.
+
+### tx_hook
+
+The hook that is executed during token transfers.
+
+## Repo contents
+
+### Smart Contract
+
+A pinocchio based Block List smart contract under the [program](program/) folder.
+
+### SDKs
+
+Codama generated rust and ts [SDKs](sdk/). 
+
+### CLI
+
+A rust CLI to interact with the contract.
+
+## Building
+
+First install dependencies:
+```
+pnpm install
+```
+
+To build the smart contract:
+```
+cd program
+cargo build-sbf
+```
+
+To deploy the smart contract:
+```
+solana program deploy --program-id <your_program_keypair.json> target/deploy/block_list.so
+```
+
+To generate the SDKs:
+```
+pnpm run generate-sdks
+```
+
+To build the CLI:
+```
+cd cli
+cargo build
+```
+
+## Setup
+
+### Block List
+
+Initialize the block list and defined the authority:
+```
+target/debug/block-list-cli init
+```
+
+Add a wallet to the block list:
+```
+target/debug/block-list-cli block-wallet <wallet_address>
+```
+
+Remove a wallet from the block list:
+```
+target/debug/block-list-cli unblock-wallet <wallet_address>
+```
+
+
+### Token Mint
+
+Initialize a new token mint:
+```
+spl-token create-token --program-2022 --transfer-hook BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf
+```
+
+Initialize the extra account metas:
+```
+target/debug/block-list-cli setup-extra-metas <wallet_address>
+```
+
+Change the extra account metas to filter both source and destination token account wallets:
+```
+target/debug/block-list-cli setup-extra-metas --check-both-wallets <wallet_address>
+```
+
+## Devnet deployment
+
+Smart contract was deployed to devnet at address `BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf`.
+
+Test transfer with empty block list [here](https://explorer.solana.com/tx/2EnQD5mFZvrR3EAyFamCfxJDS3yAtZQxNVhFtK46PanCgbX6rpvgcQ961ZAs8H3auawJZPaVZMpAxoj3qZK55mHT?cluster=devnet&customUrl=http%3A%2F%2Flocalhost%3A8899).
+
+Test transfer with non empty block list only checking source TA [here](https://explorer.solana.com/tx/4pmx31Lx5mXS7FWUtRjAxdRiwKZKCwJv3Du2qGhbLpQUenBuRxRUbrCaGGVjLjeDtpt4AXHzoNex1ppBsmKWSS7r?cluster=devnet&customUrl=http%3A%2F%2Flocalhost%3A8899).
+
+Test transfer with non empty block list checking both source and destination TAs [here](https://explorer.solana.com/tx/Q5Bk6GjGQ9TJtwS5zjDKp7GiFZK6efmGNCcxjqcmzf1YoZZJVE3rQkkSgSBNo7tst4hjUX6SJMsmEGXQ2NAdBjF?cluster=devnet&customUrl=http%3A%2F%2Flocalhost%3A8899).
+
+Simulated transaction that fails due to destination TA owner being blocked [here](https://explorer.solana.com/tx/inspector?cluster=devnet&signatures=%255B%25221111111111111111111111111111111111111111111111111111111111111111%2522%255D&message=AQAHCgqDBmqk%252FDMT5D9rK85EOwBVSTyxwkSJNDGhjodJl5A8fkyFjtMOw8TOzjiallL3mM8ylDy3Dmf4kPO6zjRCB5meTp%252FmYh4SPAIwzTHZRyKqrqiz%252FskDcCP4xKa5KaJaNQKmMSi6syOX%252BagX8jS6oj8o9glIci7jjFsFtVKThVTSAwZGb%252BUhFzL%252F7K26csOb57yM5bvF9xJrLEObOkAAAAC1QoHXoRYodtouw5cKbwI1AuPk%252BVWEpzwvoAzgkyTWD7vvmloKSuwS0IrUHLk7n0Yfp3DOKmgbjiyFpaYfufnS5xfqCyGJ%252BEpC8iKMH9T%252FdgnUADYw6SCHmevlcTztM6TwOn%252FMbMOP4VGXJKhkykzArfWQd9JuJlU%252B0GDnERJVAQbd9uHudY%252FeGEJdvORszdq2GvxNg7kNJ%252F69%252BSjYoYv8sm6yFK1CM9Gp2RvGj6wbHdQmQ4vCDR59WzHPZ5aOHbIDBAAJA9i4BQAAAAAABAAFAkANAwAJCQEIAgAABQcDBgoMAMqaOwAAAAAJ) (press simulate to see logs).
+
+Simulated transaction that fails due to source TA owner being blocked [here](https://explorer.solana.com/tx/inspector?cluster=devnet&signatures=%255B%25221111111111111111111111111111111111111111111111111111111111111111%2522%255D&message=AQAHCrod5ZzEG06%252BJzr8OnDqiGNK2oQt0Rghykcx3Sw51mE4cZQ%252BDFc%252BtWThZi0XGFuhfdEKDoUp3bkLE8gIYc3DR2N%252BTIWO0w7DxM7OOJqWUveYzzKUPLcOZ%252FiQ87rONEIHmQKmMSi6syOX%252BagX8jS6oj8o9glIci7jjFsFtVKThVTSAwZGb%252BUhFzL%252F7K26csOb57yM5bvF9xJrLEObOkAAAAC1QoHXoRYodtouw5cKbwI1AuPk%252BVWEpzwvoAzgkyTWD7vvmloKSuwS0IrUHLk7n0Yfp3DOKmgbjiyFpaYfufnS8Dp%252FzGzDj%252BFRlySoZMpMwK31kHfSbiZVPtBg5xESVQH3LKeXpXVZHuJ4gl0YZu2j5%252FXT6SUfgp2Znq1tIs7tSwbd9uHudY%252FeGEJdvORszdq2GvxNg7kNJ%252F69%252BSjYoYv8tp02GkX6M1fpsk76QI9ZgGPx%252BxaMNWlOk82JXeuOngcDBAAJA9i4BQAAAAAABAAFAkANAwAJCQEHAgAACAUDBgoMAMqaOwAAAAAJ) (press simulate to see logs).
+
+## DISCLAIMER
+
+THIS CODE IS NOT AUDITED NOR REVIEWED. USE AT YOUR OWN DISCRETION.

+ 12 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "block-list-client"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "block_list_client"
+
+[dependencies]
+solana-program = "2.2.1"
+kaigan = ">=0.2.6"
+borsh = "^0.10"

+ 163 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/config.rs

@@ -0,0 +1,163 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+use solana_program::pubkey::Pubkey;
+
+/// The config PDA account
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct Config {
+    pub discriminator: u8,
+    #[cfg_attr(
+        feature = "serde",
+        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
+    )]
+    pub authority: Pubkey,
+    pub blocked_wallets_count: u64,
+}
+
+impl Config {
+    pub const LEN: usize = 41;
+
+    /// Prefix values used to generate a PDA for this account.
+    ///
+    /// Values are positional and appear in the following order:
+    ///
+    ///   0. `Config::PREFIX`
+    pub const PREFIX: &'static [u8] = "config".as_bytes();
+
+    pub fn create_pda(
+        bump: u8,
+    ) -> Result<solana_program::pubkey::Pubkey, solana_program::pubkey::PubkeyError> {
+        solana_program::pubkey::Pubkey::create_program_address(
+            &["config".as_bytes(), &[bump]],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    pub fn find_pda() -> (solana_program::pubkey::Pubkey, u8) {
+        solana_program::pubkey::Pubkey::find_program_address(
+            &["config".as_bytes()],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    #[inline(always)]
+    pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
+        let mut data = data;
+        Self::deserialize(&mut data)
+    }
+}
+
+impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Config {
+    type Error = std::io::Error;
+
+    fn try_from(
+        account_info: &solana_program::account_info::AccountInfo<'a>,
+    ) -> Result<Self, Self::Error> {
+        let mut data: &[u8] = &(*account_info.data).borrow();
+        Self::deserialize(&mut data)
+    }
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_config(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::DecodedAccount<Config>, std::io::Error> {
+    let accounts = fetch_all_config(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_config(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::DecodedAccount<Config>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::DecodedAccount<Config>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        let account = accounts[i].as_ref().ok_or(std::io::Error::new(
+            std::io::ErrorKind::Other,
+            format!("Account not found: {}", address),
+        ))?;
+        let data = Config::from_bytes(&account.data)?;
+        decoded_accounts.push(crate::shared::DecodedAccount {
+            address,
+            account: account.clone(),
+            data,
+        });
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_maybe_config(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::MaybeAccount<Config>, std::io::Error> {
+    let accounts = fetch_all_maybe_config(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_maybe_config(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::MaybeAccount<Config>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::MaybeAccount<Config>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        if let Some(account) = accounts[i].as_ref() {
+            let data = Config::from_bytes(&account.data)?;
+            decoded_accounts.push(crate::shared::MaybeAccount::Exists(
+                crate::shared::DecodedAccount {
+                    address,
+                    account: account.clone(),
+                    data,
+                },
+            ));
+        } else {
+            decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address));
+        }
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountDeserialize for Config {
+    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
+        Ok(Self::deserialize(buf)?)
+    }
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountSerialize for Config {}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::Owner for Config {
+    fn owner() -> Pubkey {
+        crate::BLOCK_LIST_ID
+    }
+}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::IdlBuild for Config {}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::Discriminator for Config {
+    const DISCRIMINATOR: [u8; 8] = [0; 8];
+}

+ 155 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/extra_metas.rs

@@ -0,0 +1,155 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+use solana_program::pubkey::Pubkey;
+
+/// The extra metas PDA account
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct ExtraMetas {}
+
+impl ExtraMetas {
+    /// Prefix values used to generate a PDA for this account.
+    ///
+    /// Values are positional and appear in the following order:
+    ///
+    ///   0. `ExtraMetas::PREFIX`
+    ///   1. mint (`Pubkey`)
+    pub const PREFIX: &'static [u8] = "extra-account-metas".as_bytes();
+
+    pub fn create_pda(
+        mint: Pubkey,
+        bump: u8,
+    ) -> Result<solana_program::pubkey::Pubkey, solana_program::pubkey::PubkeyError> {
+        solana_program::pubkey::Pubkey::create_program_address(
+            &["extra-account-metas".as_bytes(), mint.as_ref(), &[bump]],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    pub fn find_pda(mint: &Pubkey) -> (solana_program::pubkey::Pubkey, u8) {
+        solana_program::pubkey::Pubkey::find_program_address(
+            &["extra-account-metas".as_bytes(), mint.as_ref()],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    #[inline(always)]
+    pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
+        let mut data = data;
+        Self::deserialize(&mut data)
+    }
+}
+
+impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for ExtraMetas {
+    type Error = std::io::Error;
+
+    fn try_from(
+        account_info: &solana_program::account_info::AccountInfo<'a>,
+    ) -> Result<Self, Self::Error> {
+        let mut data: &[u8] = &(*account_info.data).borrow();
+        Self::deserialize(&mut data)
+    }
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_extra_metas(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::DecodedAccount<ExtraMetas>, std::io::Error> {
+    let accounts = fetch_all_extra_metas(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_extra_metas(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::DecodedAccount<ExtraMetas>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::DecodedAccount<ExtraMetas>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        let account = accounts[i].as_ref().ok_or(std::io::Error::new(
+            std::io::ErrorKind::Other,
+            format!("Account not found: {}", address),
+        ))?;
+        let data = ExtraMetas::from_bytes(&account.data)?;
+        decoded_accounts.push(crate::shared::DecodedAccount {
+            address,
+            account: account.clone(),
+            data,
+        });
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_maybe_extra_metas(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::MaybeAccount<ExtraMetas>, std::io::Error> {
+    let accounts = fetch_all_maybe_extra_metas(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_maybe_extra_metas(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::MaybeAccount<ExtraMetas>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::MaybeAccount<ExtraMetas>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        if let Some(account) = accounts[i].as_ref() {
+            let data = ExtraMetas::from_bytes(&account.data)?;
+            decoded_accounts.push(crate::shared::MaybeAccount::Exists(
+                crate::shared::DecodedAccount {
+                    address,
+                    account: account.clone(),
+                    data,
+                },
+            ));
+        } else {
+            decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address));
+        }
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountDeserialize for ExtraMetas {
+    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
+        Ok(Self::deserialize(buf)?)
+    }
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountSerialize for ExtraMetas {}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::Owner for ExtraMetas {
+    fn owner() -> Pubkey {
+        crate::BLOCK_LIST_ID
+    }
+}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::IdlBuild for ExtraMetas {}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::Discriminator for ExtraMetas {
+    const DISCRIMINATOR: [u8; 8] = [0; 8];
+}

+ 14 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/mod.rs

@@ -0,0 +1,14 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+pub(crate) mod r#config;
+pub(crate) mod r#extra_metas;
+pub(crate) mod r#wallet_block;
+
+pub use self::r#config::*;
+pub use self::r#extra_metas::*;
+pub use self::r#wallet_block::*;

+ 163 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/wallet_block.rs

@@ -0,0 +1,163 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+use solana_program::pubkey::Pubkey;
+
+/// The config PDA account
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct WalletBlock {
+    #[cfg_attr(
+        feature = "serde",
+        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
+    )]
+    pub authority: Pubkey,
+}
+
+impl WalletBlock {
+    pub const LEN: usize = 33;
+
+    /// Prefix values used to generate a PDA for this account.
+    ///
+    /// Values are positional and appear in the following order:
+    ///
+    ///   0. `WalletBlock::PREFIX`
+    ///   1. wallet (`Pubkey`)
+    pub const PREFIX: &'static [u8] = "wallet_block".as_bytes();
+
+    pub fn create_pda(
+        wallet: Pubkey,
+        bump: u8,
+    ) -> Result<solana_program::pubkey::Pubkey, solana_program::pubkey::PubkeyError> {
+        solana_program::pubkey::Pubkey::create_program_address(
+            &["wallet_block".as_bytes(), wallet.as_ref(), &[bump]],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    pub fn find_pda(wallet: &Pubkey) -> (solana_program::pubkey::Pubkey, u8) {
+        solana_program::pubkey::Pubkey::find_program_address(
+            &["wallet_block".as_bytes(), wallet.as_ref()],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    #[inline(always)]
+    pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
+        let mut data = data;
+        Self::deserialize(&mut data)
+    }
+}
+
+impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for WalletBlock {
+    type Error = std::io::Error;
+
+    fn try_from(
+        account_info: &solana_program::account_info::AccountInfo<'a>,
+    ) -> Result<Self, Self::Error> {
+        let mut data: &[u8] = &(*account_info.data).borrow();
+        Self::deserialize(&mut data)
+    }
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_wallet_block(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::DecodedAccount<WalletBlock>, std::io::Error> {
+    let accounts = fetch_all_wallet_block(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_wallet_block(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::DecodedAccount<WalletBlock>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::DecodedAccount<WalletBlock>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        let account = accounts[i].as_ref().ok_or(std::io::Error::new(
+            std::io::ErrorKind::Other,
+            format!("Account not found: {}", address),
+        ))?;
+        let data = WalletBlock::from_bytes(&account.data)?;
+        decoded_accounts.push(crate::shared::DecodedAccount {
+            address,
+            account: account.clone(),
+            data,
+        });
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_maybe_wallet_block(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::MaybeAccount<WalletBlock>, std::io::Error> {
+    let accounts = fetch_all_maybe_wallet_block(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_maybe_wallet_block(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::MaybeAccount<WalletBlock>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::MaybeAccount<WalletBlock>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        if let Some(account) = accounts[i].as_ref() {
+            let data = WalletBlock::from_bytes(&account.data)?;
+            decoded_accounts.push(crate::shared::MaybeAccount::Exists(
+                crate::shared::DecodedAccount {
+                    address,
+                    account: account.clone(),
+                    data,
+                },
+            ));
+        } else {
+            decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address));
+        }
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountDeserialize for WalletBlock {
+    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
+        Ok(Self::deserialize(buf)?)
+    }
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountSerialize for WalletBlock {}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::Owner for WalletBlock {
+    fn owner() -> Pubkey {
+        crate::BLOCK_LIST_ID
+    }
+}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::IdlBuild for WalletBlock {}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::Discriminator for WalletBlock {
+    const DISCRIMINATOR: [u8; 8] = [0; 8];
+}

+ 6 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/errors/mod.rs

@@ -0,0 +1,6 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!

+ 447 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/block_wallet.rs

@@ -0,0 +1,447 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+
+/// Accounts.
+#[derive(Debug)]
+pub struct BlockWallet {
+    pub authority: solana_program::pubkey::Pubkey,
+
+    pub config: solana_program::pubkey::Pubkey,
+
+    pub wallet: solana_program::pubkey::Pubkey,
+
+    pub wallet_block: solana_program::pubkey::Pubkey,
+
+    pub system_program: solana_program::pubkey::Pubkey,
+}
+
+impl BlockWallet {
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        self.instruction_with_remaining_accounts(&[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn instruction_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[solana_program::instruction::AccountMeta],
+    ) -> solana_program::instruction::Instruction {
+        let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.authority,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.config,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.wallet,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.wallet_block,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.system_program,
+            false,
+        ));
+        accounts.extend_from_slice(remaining_accounts);
+        let data = borsh::to_vec(&BlockWalletInstructionData::new()).unwrap();
+
+        solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        }
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct BlockWalletInstructionData {
+    discriminator: u8,
+}
+
+impl BlockWalletInstructionData {
+    pub fn new() -> Self {
+        Self { discriminator: 242 }
+    }
+}
+
+impl Default for BlockWalletInstructionData {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// Instruction builder for `BlockWallet`.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[]` wallet
+///   3. `[writable]` wallet_block
+///   4. `[optional]` system_program (default to `11111111111111111111111111111111`)
+#[derive(Clone, Debug, Default)]
+pub struct BlockWalletBuilder {
+    authority: Option<solana_program::pubkey::Pubkey>,
+    config: Option<solana_program::pubkey::Pubkey>,
+    wallet: Option<solana_program::pubkey::Pubkey>,
+    wallet_block: Option<solana_program::pubkey::Pubkey>,
+    system_program: Option<solana_program::pubkey::Pubkey>,
+    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
+}
+
+impl BlockWalletBuilder {
+    pub fn new() -> Self {
+        Self::default()
+    }
+    #[inline(always)]
+    pub fn authority(&mut self, authority: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet(&mut self, wallet: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.wallet = Some(wallet);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet_block(&mut self, wallet_block: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.wallet_block = Some(wallet_block);
+        self
+    }
+    /// `[optional account, default to '11111111111111111111111111111111']`
+    #[inline(always)]
+    pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: solana_program::instruction::AccountMeta,
+    ) -> &mut Self {
+        self.__remaining_accounts.push(account);
+        self
+    }
+    /// Add additional accounts to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[solana_program::instruction::AccountMeta],
+    ) -> &mut Self {
+        self.__remaining_accounts.extend_from_slice(accounts);
+        self
+    }
+    #[allow(clippy::clone_on_copy)]
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        let accounts = BlockWallet {
+            authority: self.authority.expect("authority is not set"),
+            config: self.config.expect("config is not set"),
+            wallet: self.wallet.expect("wallet is not set"),
+            wallet_block: self.wallet_block.expect("wallet_block is not set"),
+            system_program: self
+                .system_program
+                .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")),
+        };
+
+        accounts.instruction_with_remaining_accounts(&self.__remaining_accounts)
+    }
+}
+
+/// `block_wallet` CPI accounts.
+pub struct BlockWalletCpiAccounts<'a, 'b> {
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+/// `block_wallet` CPI instruction.
+pub struct BlockWalletCpi<'a, 'b> {
+    /// The program to invoke.
+    pub __program: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+impl<'a, 'b> BlockWalletCpi<'a, 'b> {
+    pub fn new(
+        program: &'b solana_program::account_info::AccountInfo<'a>,
+        accounts: BlockWalletCpiAccounts<'a, 'b>,
+    ) -> Self {
+        Self {
+            __program: program,
+            authority: accounts.authority,
+            config: accounts.config,
+            wallet: accounts.wallet,
+            wallet_block: accounts.wallet_block,
+            system_program: accounts.system_program,
+        }
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], &[])
+    }
+    #[inline(always)]
+    pub fn invoke_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
+    }
+    #[inline(always)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed_with_remaining_accounts(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.authority.key,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.config.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.wallet.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.wallet_block.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.system_program.key,
+            false,
+        ));
+        remaining_accounts.iter().for_each(|remaining_account| {
+            accounts.push(solana_program::instruction::AccountMeta {
+                pubkey: *remaining_account.0.key,
+                is_signer: remaining_account.1,
+                is_writable: remaining_account.2,
+            })
+        });
+        let data = borsh::to_vec(&BlockWalletInstructionData::new()).unwrap();
+
+        let instruction = solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        };
+        let mut account_infos = Vec::with_capacity(6 + remaining_accounts.len());
+        account_infos.push(self.__program.clone());
+        account_infos.push(self.authority.clone());
+        account_infos.push(self.config.clone());
+        account_infos.push(self.wallet.clone());
+        account_infos.push(self.wallet_block.clone());
+        account_infos.push(self.system_program.clone());
+        remaining_accounts
+            .iter()
+            .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
+
+        if signers_seeds.is_empty() {
+            solana_program::program::invoke(&instruction, &account_infos)
+        } else {
+            solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
+        }
+    }
+}
+
+/// Instruction builder for `BlockWallet` via CPI.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[]` wallet
+///   3. `[writable]` wallet_block
+///   4. `[]` system_program
+#[derive(Clone, Debug)]
+pub struct BlockWalletCpiBuilder<'a, 'b> {
+    instruction: Box<BlockWalletCpiBuilderInstruction<'a, 'b>>,
+}
+
+impl<'a, 'b> BlockWalletCpiBuilder<'a, 'b> {
+    pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
+        let instruction = Box::new(BlockWalletCpiBuilderInstruction {
+            __program: program,
+            authority: None,
+            config: None,
+            wallet: None,
+            wallet_block: None,
+            system_program: None,
+            __remaining_accounts: Vec::new(),
+        });
+        Self { instruction }
+    }
+    #[inline(always)]
+    pub fn authority(
+        &mut self,
+        authority: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(
+        &mut self,
+        config: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet(
+        &mut self,
+        wallet: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.wallet = Some(wallet);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet_block(
+        &mut self,
+        wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.wallet_block = Some(wallet_block);
+        self
+    }
+    #[inline(always)]
+    pub fn system_program(
+        &mut self,
+        system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: &'b solana_program::account_info::AccountInfo<'a>,
+        is_writable: bool,
+        is_signer: bool,
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .push((account, is_writable, is_signer));
+        self
+    }
+    /// Add additional accounts to the instruction.
+    ///
+    /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not,
+    /// and a `bool` indicating whether the account is a signer or not.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .extend_from_slice(accounts);
+        self
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed(&[])
+    }
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let instruction = BlockWalletCpi {
+            __program: self.instruction.__program,
+
+            authority: self.instruction.authority.expect("authority is not set"),
+
+            config: self.instruction.config.expect("config is not set"),
+
+            wallet: self.instruction.wallet.expect("wallet is not set"),
+
+            wallet_block: self
+                .instruction
+                .wallet_block
+                .expect("wallet_block is not set"),
+
+            system_program: self
+                .instruction
+                .system_program
+                .expect("system_program is not set"),
+        };
+        instruction.invoke_signed_with_remaining_accounts(
+            signers_seeds,
+            &self.instruction.__remaining_accounts,
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
+struct BlockWalletCpiBuilderInstruction<'a, 'b> {
+    __program: &'b solana_program::account_info::AccountInfo<'a>,
+    authority: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    config: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    wallet: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    wallet_block: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
+    __remaining_accounts: Vec<(
+        &'b solana_program::account_info::AccountInfo<'a>,
+        bool,
+        bool,
+    )>,
+}

+ 370 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/init.rs

@@ -0,0 +1,370 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+
+/// Accounts.
+#[derive(Debug)]
+pub struct Init {
+    pub authority: solana_program::pubkey::Pubkey,
+
+    pub config: solana_program::pubkey::Pubkey,
+
+    pub system_program: solana_program::pubkey::Pubkey,
+}
+
+impl Init {
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        self.instruction_with_remaining_accounts(&[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn instruction_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[solana_program::instruction::AccountMeta],
+    ) -> solana_program::instruction::Instruction {
+        let mut accounts = Vec::with_capacity(3 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.authority,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.config,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.system_program,
+            false,
+        ));
+        accounts.extend_from_slice(remaining_accounts);
+        let data = borsh::to_vec(&InitInstructionData::new()).unwrap();
+
+        solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        }
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct InitInstructionData {
+    discriminator: u8,
+}
+
+impl InitInstructionData {
+    pub fn new() -> Self {
+        Self { discriminator: 241 }
+    }
+}
+
+impl Default for InitInstructionData {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// Instruction builder for `Init`.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[optional]` system_program (default to `11111111111111111111111111111111`)
+#[derive(Clone, Debug, Default)]
+pub struct InitBuilder {
+    authority: Option<solana_program::pubkey::Pubkey>,
+    config: Option<solana_program::pubkey::Pubkey>,
+    system_program: Option<solana_program::pubkey::Pubkey>,
+    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
+}
+
+impl InitBuilder {
+    pub fn new() -> Self {
+        Self::default()
+    }
+    #[inline(always)]
+    pub fn authority(&mut self, authority: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.config = Some(config);
+        self
+    }
+    /// `[optional account, default to '11111111111111111111111111111111']`
+    #[inline(always)]
+    pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: solana_program::instruction::AccountMeta,
+    ) -> &mut Self {
+        self.__remaining_accounts.push(account);
+        self
+    }
+    /// Add additional accounts to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[solana_program::instruction::AccountMeta],
+    ) -> &mut Self {
+        self.__remaining_accounts.extend_from_slice(accounts);
+        self
+    }
+    #[allow(clippy::clone_on_copy)]
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        let accounts = Init {
+            authority: self.authority.expect("authority is not set"),
+            config: self.config.expect("config is not set"),
+            system_program: self
+                .system_program
+                .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")),
+        };
+
+        accounts.instruction_with_remaining_accounts(&self.__remaining_accounts)
+    }
+}
+
+/// `init` CPI accounts.
+pub struct InitCpiAccounts<'a, 'b> {
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+/// `init` CPI instruction.
+pub struct InitCpi<'a, 'b> {
+    /// The program to invoke.
+    pub __program: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+impl<'a, 'b> InitCpi<'a, 'b> {
+    pub fn new(
+        program: &'b solana_program::account_info::AccountInfo<'a>,
+        accounts: InitCpiAccounts<'a, 'b>,
+    ) -> Self {
+        Self {
+            __program: program,
+            authority: accounts.authority,
+            config: accounts.config,
+            system_program: accounts.system_program,
+        }
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], &[])
+    }
+    #[inline(always)]
+    pub fn invoke_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
+    }
+    #[inline(always)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed_with_remaining_accounts(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let mut accounts = Vec::with_capacity(3 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.authority.key,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.config.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.system_program.key,
+            false,
+        ));
+        remaining_accounts.iter().for_each(|remaining_account| {
+            accounts.push(solana_program::instruction::AccountMeta {
+                pubkey: *remaining_account.0.key,
+                is_signer: remaining_account.1,
+                is_writable: remaining_account.2,
+            })
+        });
+        let data = borsh::to_vec(&InitInstructionData::new()).unwrap();
+
+        let instruction = solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        };
+        let mut account_infos = Vec::with_capacity(4 + remaining_accounts.len());
+        account_infos.push(self.__program.clone());
+        account_infos.push(self.authority.clone());
+        account_infos.push(self.config.clone());
+        account_infos.push(self.system_program.clone());
+        remaining_accounts
+            .iter()
+            .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
+
+        if signers_seeds.is_empty() {
+            solana_program::program::invoke(&instruction, &account_infos)
+        } else {
+            solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
+        }
+    }
+}
+
+/// Instruction builder for `Init` via CPI.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[]` system_program
+#[derive(Clone, Debug)]
+pub struct InitCpiBuilder<'a, 'b> {
+    instruction: Box<InitCpiBuilderInstruction<'a, 'b>>,
+}
+
+impl<'a, 'b> InitCpiBuilder<'a, 'b> {
+    pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
+        let instruction = Box::new(InitCpiBuilderInstruction {
+            __program: program,
+            authority: None,
+            config: None,
+            system_program: None,
+            __remaining_accounts: Vec::new(),
+        });
+        Self { instruction }
+    }
+    #[inline(always)]
+    pub fn authority(
+        &mut self,
+        authority: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(
+        &mut self,
+        config: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn system_program(
+        &mut self,
+        system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: &'b solana_program::account_info::AccountInfo<'a>,
+        is_writable: bool,
+        is_signer: bool,
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .push((account, is_writable, is_signer));
+        self
+    }
+    /// Add additional accounts to the instruction.
+    ///
+    /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not,
+    /// and a `bool` indicating whether the account is a signer or not.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .extend_from_slice(accounts);
+        self
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed(&[])
+    }
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let instruction = InitCpi {
+            __program: self.instruction.__program,
+
+            authority: self.instruction.authority.expect("authority is not set"),
+
+            config: self.instruction.config.expect("config is not set"),
+
+            system_program: self
+                .instruction
+                .system_program
+                .expect("system_program is not set"),
+        };
+        instruction.invoke_signed_with_remaining_accounts(
+            signers_seeds,
+            &self.instruction.__remaining_accounts,
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
+struct InitCpiBuilderInstruction<'a, 'b> {
+    __program: &'b solana_program::account_info::AccountInfo<'a>,
+    authority: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    config: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
+    __remaining_accounts: Vec<(
+        &'b solana_program::account_info::AccountInfo<'a>,
+        bool,
+        bool,
+    )>,
+}

+ 16 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/mod.rs

@@ -0,0 +1,16 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+pub(crate) mod r#block_wallet;
+pub(crate) mod r#init;
+pub(crate) mod r#setup_extra_metas;
+pub(crate) mod r#unblock_wallet;
+
+pub use self::r#block_wallet::*;
+pub use self::r#init::*;
+pub use self::r#setup_extra_metas::*;
+pub use self::r#unblock_wallet::*;

+ 483 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/setup_extra_metas.rs

@@ -0,0 +1,483 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+
+/// Accounts.
+#[derive(Debug)]
+pub struct SetupExtraMetas {
+    pub authority: solana_program::pubkey::Pubkey,
+
+    pub config: solana_program::pubkey::Pubkey,
+
+    pub mint: solana_program::pubkey::Pubkey,
+
+    pub extra_metas: solana_program::pubkey::Pubkey,
+
+    pub system_program: solana_program::pubkey::Pubkey,
+}
+
+impl SetupExtraMetas {
+    pub fn instruction(
+        &self,
+        args: SetupExtraMetasInstructionArgs,
+    ) -> solana_program::instruction::Instruction {
+        self.instruction_with_remaining_accounts(args, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn instruction_with_remaining_accounts(
+        &self,
+        args: SetupExtraMetasInstructionArgs,
+        remaining_accounts: &[solana_program::instruction::AccountMeta],
+    ) -> solana_program::instruction::Instruction {
+        let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.authority,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.config,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.mint, false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.extra_metas,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.system_program,
+            false,
+        ));
+        accounts.extend_from_slice(remaining_accounts);
+        let mut data = borsh::to_vec(&SetupExtraMetasInstructionData::new()).unwrap();
+        let mut args = borsh::to_vec(&args).unwrap();
+        data.append(&mut args);
+
+        solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        }
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct SetupExtraMetasInstructionData {
+    discriminator: u8,
+}
+
+impl SetupExtraMetasInstructionData {
+    pub fn new() -> Self {
+        Self { discriminator: 106 }
+    }
+}
+
+impl Default for SetupExtraMetasInstructionData {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct SetupExtraMetasInstructionArgs {
+    pub check_both_wallets: bool,
+}
+
+/// Instruction builder for `SetupExtraMetas`.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[]` config
+///   2. `[]` mint
+///   3. `[writable]` extra_metas
+///   4. `[optional]` system_program (default to `11111111111111111111111111111111`)
+#[derive(Clone, Debug, Default)]
+pub struct SetupExtraMetasBuilder {
+    authority: Option<solana_program::pubkey::Pubkey>,
+    config: Option<solana_program::pubkey::Pubkey>,
+    mint: Option<solana_program::pubkey::Pubkey>,
+    extra_metas: Option<solana_program::pubkey::Pubkey>,
+    system_program: Option<solana_program::pubkey::Pubkey>,
+    check_both_wallets: Option<bool>,
+    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
+}
+
+impl SetupExtraMetasBuilder {
+    pub fn new() -> Self {
+        Self::default()
+    }
+    #[inline(always)]
+    pub fn authority(&mut self, authority: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn mint(&mut self, mint: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.mint = Some(mint);
+        self
+    }
+    #[inline(always)]
+    pub fn extra_metas(&mut self, extra_metas: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.extra_metas = Some(extra_metas);
+        self
+    }
+    /// `[optional account, default to '11111111111111111111111111111111']`
+    #[inline(always)]
+    pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.system_program = Some(system_program);
+        self
+    }
+    /// `[optional argument, defaults to 'false']`
+    #[inline(always)]
+    pub fn check_both_wallets(&mut self, check_both_wallets: bool) -> &mut Self {
+        self.check_both_wallets = Some(check_both_wallets);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: solana_program::instruction::AccountMeta,
+    ) -> &mut Self {
+        self.__remaining_accounts.push(account);
+        self
+    }
+    /// Add additional accounts to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[solana_program::instruction::AccountMeta],
+    ) -> &mut Self {
+        self.__remaining_accounts.extend_from_slice(accounts);
+        self
+    }
+    #[allow(clippy::clone_on_copy)]
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        let accounts = SetupExtraMetas {
+            authority: self.authority.expect("authority is not set"),
+            config: self.config.expect("config is not set"),
+            mint: self.mint.expect("mint is not set"),
+            extra_metas: self.extra_metas.expect("extra_metas is not set"),
+            system_program: self
+                .system_program
+                .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")),
+        };
+        let args = SetupExtraMetasInstructionArgs {
+            check_both_wallets: self.check_both_wallets.clone().unwrap_or(false),
+        };
+
+        accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts)
+    }
+}
+
+/// `setup_extra_metas` CPI accounts.
+pub struct SetupExtraMetasCpiAccounts<'a, 'b> {
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub mint: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub extra_metas: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+/// `setup_extra_metas` CPI instruction.
+pub struct SetupExtraMetasCpi<'a, 'b> {
+    /// The program to invoke.
+    pub __program: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub mint: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub extra_metas: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    /// The arguments for the instruction.
+    pub __args: SetupExtraMetasInstructionArgs,
+}
+
+impl<'a, 'b> SetupExtraMetasCpi<'a, 'b> {
+    pub fn new(
+        program: &'b solana_program::account_info::AccountInfo<'a>,
+        accounts: SetupExtraMetasCpiAccounts<'a, 'b>,
+        args: SetupExtraMetasInstructionArgs,
+    ) -> Self {
+        Self {
+            __program: program,
+            authority: accounts.authority,
+            config: accounts.config,
+            mint: accounts.mint,
+            extra_metas: accounts.extra_metas,
+            system_program: accounts.system_program,
+            __args: args,
+        }
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], &[])
+    }
+    #[inline(always)]
+    pub fn invoke_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
+    }
+    #[inline(always)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed_with_remaining_accounts(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.authority.key,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.config.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.mint.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.extra_metas.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.system_program.key,
+            false,
+        ));
+        remaining_accounts.iter().for_each(|remaining_account| {
+            accounts.push(solana_program::instruction::AccountMeta {
+                pubkey: *remaining_account.0.key,
+                is_signer: remaining_account.1,
+                is_writable: remaining_account.2,
+            })
+        });
+        let mut data = borsh::to_vec(&SetupExtraMetasInstructionData::new()).unwrap();
+        let mut args = borsh::to_vec(&self.__args).unwrap();
+        data.append(&mut args);
+
+        let instruction = solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        };
+        let mut account_infos = Vec::with_capacity(6 + remaining_accounts.len());
+        account_infos.push(self.__program.clone());
+        account_infos.push(self.authority.clone());
+        account_infos.push(self.config.clone());
+        account_infos.push(self.mint.clone());
+        account_infos.push(self.extra_metas.clone());
+        account_infos.push(self.system_program.clone());
+        remaining_accounts
+            .iter()
+            .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
+
+        if signers_seeds.is_empty() {
+            solana_program::program::invoke(&instruction, &account_infos)
+        } else {
+            solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
+        }
+    }
+}
+
+/// Instruction builder for `SetupExtraMetas` via CPI.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[]` config
+///   2. `[]` mint
+///   3. `[writable]` extra_metas
+///   4. `[]` system_program
+#[derive(Clone, Debug)]
+pub struct SetupExtraMetasCpiBuilder<'a, 'b> {
+    instruction: Box<SetupExtraMetasCpiBuilderInstruction<'a, 'b>>,
+}
+
+impl<'a, 'b> SetupExtraMetasCpiBuilder<'a, 'b> {
+    pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
+        let instruction = Box::new(SetupExtraMetasCpiBuilderInstruction {
+            __program: program,
+            authority: None,
+            config: None,
+            mint: None,
+            extra_metas: None,
+            system_program: None,
+            check_both_wallets: None,
+            __remaining_accounts: Vec::new(),
+        });
+        Self { instruction }
+    }
+    #[inline(always)]
+    pub fn authority(
+        &mut self,
+        authority: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(
+        &mut self,
+        config: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn mint(&mut self, mint: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self {
+        self.instruction.mint = Some(mint);
+        self
+    }
+    #[inline(always)]
+    pub fn extra_metas(
+        &mut self,
+        extra_metas: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.extra_metas = Some(extra_metas);
+        self
+    }
+    #[inline(always)]
+    pub fn system_program(
+        &mut self,
+        system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.system_program = Some(system_program);
+        self
+    }
+    /// `[optional argument, defaults to 'false']`
+    #[inline(always)]
+    pub fn check_both_wallets(&mut self, check_both_wallets: bool) -> &mut Self {
+        self.instruction.check_both_wallets = Some(check_both_wallets);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: &'b solana_program::account_info::AccountInfo<'a>,
+        is_writable: bool,
+        is_signer: bool,
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .push((account, is_writable, is_signer));
+        self
+    }
+    /// Add additional accounts to the instruction.
+    ///
+    /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not,
+    /// and a `bool` indicating whether the account is a signer or not.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .extend_from_slice(accounts);
+        self
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed(&[])
+    }
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let args = SetupExtraMetasInstructionArgs {
+            check_both_wallets: self.instruction.check_both_wallets.clone().unwrap_or(false),
+        };
+        let instruction = SetupExtraMetasCpi {
+            __program: self.instruction.__program,
+
+            authority: self.instruction.authority.expect("authority is not set"),
+
+            config: self.instruction.config.expect("config is not set"),
+
+            mint: self.instruction.mint.expect("mint is not set"),
+
+            extra_metas: self
+                .instruction
+                .extra_metas
+                .expect("extra_metas is not set"),
+
+            system_program: self
+                .instruction
+                .system_program
+                .expect("system_program is not set"),
+            __args: args,
+        };
+        instruction.invoke_signed_with_remaining_accounts(
+            signers_seeds,
+            &self.instruction.__remaining_accounts,
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
+struct SetupExtraMetasCpiBuilderInstruction<'a, 'b> {
+    __program: &'b solana_program::account_info::AccountInfo<'a>,
+    authority: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    config: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    mint: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    extra_metas: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    check_both_wallets: Option<bool>,
+    /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
+    __remaining_accounts: Vec<(
+        &'b solana_program::account_info::AccountInfo<'a>,
+        bool,
+        bool,
+    )>,
+}

+ 410 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/unblock_wallet.rs

@@ -0,0 +1,410 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+
+/// Accounts.
+#[derive(Debug)]
+pub struct UnblockWallet {
+    pub authority: solana_program::pubkey::Pubkey,
+
+    pub config: solana_program::pubkey::Pubkey,
+
+    pub wallet_block: solana_program::pubkey::Pubkey,
+
+    pub system_program: solana_program::pubkey::Pubkey,
+}
+
+impl UnblockWallet {
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        self.instruction_with_remaining_accounts(&[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn instruction_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[solana_program::instruction::AccountMeta],
+    ) -> solana_program::instruction::Instruction {
+        let mut accounts = Vec::with_capacity(4 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.authority,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.config,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.wallet_block,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.system_program,
+            false,
+        ));
+        accounts.extend_from_slice(remaining_accounts);
+        let data = borsh::to_vec(&UnblockWalletInstructionData::new()).unwrap();
+
+        solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        }
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct UnblockWalletInstructionData {
+    discriminator: u8,
+}
+
+impl UnblockWalletInstructionData {
+    pub fn new() -> Self {
+        Self { discriminator: 243 }
+    }
+}
+
+impl Default for UnblockWalletInstructionData {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// Instruction builder for `UnblockWallet`.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[writable]` wallet_block
+///   3. `[optional]` system_program (default to `11111111111111111111111111111111`)
+#[derive(Clone, Debug, Default)]
+pub struct UnblockWalletBuilder {
+    authority: Option<solana_program::pubkey::Pubkey>,
+    config: Option<solana_program::pubkey::Pubkey>,
+    wallet_block: Option<solana_program::pubkey::Pubkey>,
+    system_program: Option<solana_program::pubkey::Pubkey>,
+    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
+}
+
+impl UnblockWalletBuilder {
+    pub fn new() -> Self {
+        Self::default()
+    }
+    #[inline(always)]
+    pub fn authority(&mut self, authority: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet_block(&mut self, wallet_block: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.wallet_block = Some(wallet_block);
+        self
+    }
+    /// `[optional account, default to '11111111111111111111111111111111']`
+    #[inline(always)]
+    pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: solana_program::instruction::AccountMeta,
+    ) -> &mut Self {
+        self.__remaining_accounts.push(account);
+        self
+    }
+    /// Add additional accounts to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[solana_program::instruction::AccountMeta],
+    ) -> &mut Self {
+        self.__remaining_accounts.extend_from_slice(accounts);
+        self
+    }
+    #[allow(clippy::clone_on_copy)]
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        let accounts = UnblockWallet {
+            authority: self.authority.expect("authority is not set"),
+            config: self.config.expect("config is not set"),
+            wallet_block: self.wallet_block.expect("wallet_block is not set"),
+            system_program: self
+                .system_program
+                .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")),
+        };
+
+        accounts.instruction_with_remaining_accounts(&self.__remaining_accounts)
+    }
+}
+
+/// `unblock_wallet` CPI accounts.
+pub struct UnblockWalletCpiAccounts<'a, 'b> {
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+/// `unblock_wallet` CPI instruction.
+pub struct UnblockWalletCpi<'a, 'b> {
+    /// The program to invoke.
+    pub __program: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+impl<'a, 'b> UnblockWalletCpi<'a, 'b> {
+    pub fn new(
+        program: &'b solana_program::account_info::AccountInfo<'a>,
+        accounts: UnblockWalletCpiAccounts<'a, 'b>,
+    ) -> Self {
+        Self {
+            __program: program,
+            authority: accounts.authority,
+            config: accounts.config,
+            wallet_block: accounts.wallet_block,
+            system_program: accounts.system_program,
+        }
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], &[])
+    }
+    #[inline(always)]
+    pub fn invoke_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
+    }
+    #[inline(always)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed_with_remaining_accounts(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let mut accounts = Vec::with_capacity(4 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.authority.key,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.config.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.wallet_block.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.system_program.key,
+            false,
+        ));
+        remaining_accounts.iter().for_each(|remaining_account| {
+            accounts.push(solana_program::instruction::AccountMeta {
+                pubkey: *remaining_account.0.key,
+                is_signer: remaining_account.1,
+                is_writable: remaining_account.2,
+            })
+        });
+        let data = borsh::to_vec(&UnblockWalletInstructionData::new()).unwrap();
+
+        let instruction = solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        };
+        let mut account_infos = Vec::with_capacity(5 + remaining_accounts.len());
+        account_infos.push(self.__program.clone());
+        account_infos.push(self.authority.clone());
+        account_infos.push(self.config.clone());
+        account_infos.push(self.wallet_block.clone());
+        account_infos.push(self.system_program.clone());
+        remaining_accounts
+            .iter()
+            .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
+
+        if signers_seeds.is_empty() {
+            solana_program::program::invoke(&instruction, &account_infos)
+        } else {
+            solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
+        }
+    }
+}
+
+/// Instruction builder for `UnblockWallet` via CPI.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[writable]` wallet_block
+///   3. `[]` system_program
+#[derive(Clone, Debug)]
+pub struct UnblockWalletCpiBuilder<'a, 'b> {
+    instruction: Box<UnblockWalletCpiBuilderInstruction<'a, 'b>>,
+}
+
+impl<'a, 'b> UnblockWalletCpiBuilder<'a, 'b> {
+    pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
+        let instruction = Box::new(UnblockWalletCpiBuilderInstruction {
+            __program: program,
+            authority: None,
+            config: None,
+            wallet_block: None,
+            system_program: None,
+            __remaining_accounts: Vec::new(),
+        });
+        Self { instruction }
+    }
+    #[inline(always)]
+    pub fn authority(
+        &mut self,
+        authority: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(
+        &mut self,
+        config: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet_block(
+        &mut self,
+        wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.wallet_block = Some(wallet_block);
+        self
+    }
+    #[inline(always)]
+    pub fn system_program(
+        &mut self,
+        system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: &'b solana_program::account_info::AccountInfo<'a>,
+        is_writable: bool,
+        is_signer: bool,
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .push((account, is_writable, is_signer));
+        self
+    }
+    /// Add additional accounts to the instruction.
+    ///
+    /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not,
+    /// and a `bool` indicating whether the account is a signer or not.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .extend_from_slice(accounts);
+        self
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed(&[])
+    }
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let instruction = UnblockWalletCpi {
+            __program: self.instruction.__program,
+
+            authority: self.instruction.authority.expect("authority is not set"),
+
+            config: self.instruction.config.expect("config is not set"),
+
+            wallet_block: self
+                .instruction
+                .wallet_block
+                .expect("wallet_block is not set"),
+
+            system_program: self
+                .instruction
+                .system_program
+                .expect("system_program is not set"),
+        };
+        instruction.invoke_signed_with_remaining_accounts(
+            signers_seeds,
+            &self.instruction.__remaining_accounts,
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
+struct UnblockWalletCpiBuilderInstruction<'a, 'b> {
+    __program: &'b solana_program::account_info::AccountInfo<'a>,
+    authority: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    config: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    wallet_block: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
+    __remaining_accounts: Vec<(
+        &'b solana_program::account_info::AccountInfo<'a>,
+        bool,
+        bool,
+    )>,
+}

+ 14 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/mod.rs

@@ -0,0 +1,14 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+pub mod accounts;
+pub mod errors;
+pub mod instructions;
+pub mod programs;
+pub mod shared;
+
+pub(crate) use programs::*;

+ 11 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/programs.rs

@@ -0,0 +1,11 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use solana_program::{pubkey, pubkey::Pubkey};
+
+/// `block_list` program ID.
+pub const BLOCK_LIST_ID: Pubkey = pubkey!("BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf");

+ 21 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/shared.rs

@@ -0,0 +1,21 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+#[cfg(feature = "fetch")]
+#[derive(Debug, Clone)]
+pub struct DecodedAccount<T> {
+    pub address: solana_program::pubkey::Pubkey,
+    pub account: solana_sdk::account::Account,
+    pub data: T,
+}
+
+#[cfg(feature = "fetch")]
+#[derive(Debug, Clone)]
+pub enum MaybeAccount<T> {
+    Exists(DecodedAccount<T>),
+    NotFound(solana_program::pubkey::Pubkey),
+}

+ 2 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/lib.rs

@@ -0,0 +1,2 @@
+pub mod client;
+pub use client::*;

+ 148 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/config.ts

@@ -0,0 +1,148 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  assertAccountExists,
+  assertAccountsExist,
+  combineCodec,
+  decodeAccount,
+  fetchEncodedAccount,
+  fetchEncodedAccounts,
+  getAddressDecoder,
+  getAddressEncoder,
+  getStructDecoder,
+  getStructEncoder,
+  getU64Decoder,
+  getU64Encoder,
+  getU8Decoder,
+  getU8Encoder,
+  type Account,
+  type Address,
+  type Codec,
+  type Decoder,
+  type EncodedAccount,
+  type Encoder,
+  type FetchAccountConfig,
+  type FetchAccountsConfig,
+  type MaybeAccount,
+  type MaybeEncodedAccount,
+} from '@solana/kit';
+import { findConfigPda } from '../pdas';
+
+export const CONFIG_DISCRIMINATOR = 0;
+
+export function getConfigDiscriminatorBytes() {
+  return getU8Encoder().encode(CONFIG_DISCRIMINATOR);
+}
+
+export type Config = {
+  discriminator: number;
+  authority: Address;
+  blockedWalletsCount: bigint;
+};
+
+export type ConfigArgs = {
+  discriminator: number;
+  authority: Address;
+  blockedWalletsCount: number | bigint;
+};
+
+export function getConfigEncoder(): Encoder<ConfigArgs> {
+  return getStructEncoder([
+    ['discriminator', getU8Encoder()],
+    ['authority', getAddressEncoder()],
+    ['blockedWalletsCount', getU64Encoder()],
+  ]);
+}
+
+export function getConfigDecoder(): Decoder<Config> {
+  return getStructDecoder([
+    ['discriminator', getU8Decoder()],
+    ['authority', getAddressDecoder()],
+    ['blockedWalletsCount', getU64Decoder()],
+  ]);
+}
+
+export function getConfigCodec(): Codec<ConfigArgs, Config> {
+  return combineCodec(getConfigEncoder(), getConfigDecoder());
+}
+
+export function decodeConfig<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress>
+): Account<Config, TAddress>;
+export function decodeConfig<TAddress extends string = string>(
+  encodedAccount: MaybeEncodedAccount<TAddress>
+): MaybeAccount<Config, TAddress>;
+export function decodeConfig<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>
+): Account<Config, TAddress> | MaybeAccount<Config, TAddress> {
+  return decodeAccount(
+    encodedAccount as MaybeEncodedAccount<TAddress>,
+    getConfigDecoder()
+  );
+}
+
+export async function fetchConfig<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<Account<Config, TAddress>> {
+  const maybeAccount = await fetchMaybeConfig(rpc, address, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeConfig<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<MaybeAccount<Config, TAddress>> {
+  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
+  return decodeConfig(maybeAccount);
+}
+
+export async function fetchAllConfig(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<Account<Config>[]> {
+  const maybeAccounts = await fetchAllMaybeConfig(rpc, addresses, config);
+  assertAccountsExist(maybeAccounts);
+  return maybeAccounts;
+}
+
+export async function fetchAllMaybeConfig(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<MaybeAccount<Config>[]> {
+  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
+  return maybeAccounts.map((maybeAccount) => decodeConfig(maybeAccount));
+}
+
+export function getConfigSize(): number {
+  return 41;
+}
+
+export async function fetchConfigFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<Account<Config>> {
+  const maybeAccount = await fetchMaybeConfigFromSeeds(rpc, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeConfigFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<MaybeAccount<Config>> {
+  const { programAddress, ...fetchConfig } = config;
+  const [address] = await findConfigPda({ programAddress });
+  return await fetchMaybeConfig(rpc, address, fetchConfig);
+}

+ 118 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/extraMetas.ts

@@ -0,0 +1,118 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  assertAccountExists,
+  assertAccountsExist,
+  combineCodec,
+  decodeAccount,
+  fetchEncodedAccount,
+  fetchEncodedAccounts,
+  getStructDecoder,
+  getStructEncoder,
+  type Account,
+  type Address,
+  type Codec,
+  type Decoder,
+  type EncodedAccount,
+  type Encoder,
+  type FetchAccountConfig,
+  type FetchAccountsConfig,
+  type MaybeAccount,
+  type MaybeEncodedAccount,
+} from '@solana/kit';
+import { ExtraMetasSeeds, findExtraMetasPda } from '../pdas';
+
+export type ExtraMetas = {};
+
+export type ExtraMetasArgs = ExtraMetas;
+
+export function getExtraMetasEncoder(): Encoder<ExtraMetasArgs> {
+  return getStructEncoder([]);
+}
+
+export function getExtraMetasDecoder(): Decoder<ExtraMetas> {
+  return getStructDecoder([]);
+}
+
+export function getExtraMetasCodec(): Codec<ExtraMetasArgs, ExtraMetas> {
+  return combineCodec(getExtraMetasEncoder(), getExtraMetasDecoder());
+}
+
+export function decodeExtraMetas<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress>
+): Account<ExtraMetas, TAddress>;
+export function decodeExtraMetas<TAddress extends string = string>(
+  encodedAccount: MaybeEncodedAccount<TAddress>
+): MaybeAccount<ExtraMetas, TAddress>;
+export function decodeExtraMetas<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>
+): Account<ExtraMetas, TAddress> | MaybeAccount<ExtraMetas, TAddress> {
+  return decodeAccount(
+    encodedAccount as MaybeEncodedAccount<TAddress>,
+    getExtraMetasDecoder()
+  );
+}
+
+export async function fetchExtraMetas<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<Account<ExtraMetas, TAddress>> {
+  const maybeAccount = await fetchMaybeExtraMetas(rpc, address, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeExtraMetas<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<MaybeAccount<ExtraMetas, TAddress>> {
+  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
+  return decodeExtraMetas(maybeAccount);
+}
+
+export async function fetchAllExtraMetas(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<Account<ExtraMetas>[]> {
+  const maybeAccounts = await fetchAllMaybeExtraMetas(rpc, addresses, config);
+  assertAccountsExist(maybeAccounts);
+  return maybeAccounts;
+}
+
+export async function fetchAllMaybeExtraMetas(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<MaybeAccount<ExtraMetas>[]> {
+  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
+  return maybeAccounts.map((maybeAccount) => decodeExtraMetas(maybeAccount));
+}
+
+export async function fetchExtraMetasFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  seeds: ExtraMetasSeeds,
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<Account<ExtraMetas>> {
+  const maybeAccount = await fetchMaybeExtraMetasFromSeeds(rpc, seeds, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeExtraMetasFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  seeds: ExtraMetasSeeds,
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<MaybeAccount<ExtraMetas>> {
+  const { programAddress, ...fetchConfig } = config;
+  const [address] = await findExtraMetasPda(seeds, { programAddress });
+  return await fetchMaybeExtraMetas(rpc, address, fetchConfig);
+}

+ 11 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/index.ts

@@ -0,0 +1,11 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './config';
+export * from './extraMetas';
+export * from './walletBlock';

+ 131 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/walletBlock.ts

@@ -0,0 +1,131 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  assertAccountExists,
+  assertAccountsExist,
+  combineCodec,
+  decodeAccount,
+  fetchEncodedAccount,
+  fetchEncodedAccounts,
+  getAddressDecoder,
+  getAddressEncoder,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Encoder,
+  type Account,
+  type Address,
+  type Codec,
+  type Decoder,
+  type EncodedAccount,
+  type Encoder,
+  type FetchAccountConfig,
+  type FetchAccountsConfig,
+  type MaybeAccount,
+  type MaybeEncodedAccount,
+} from '@solana/kit';
+import { WalletBlockSeeds, findWalletBlockPda } from '../pdas';
+
+export const WALLET_BLOCK_DISCRIMINATOR = 1;
+
+export function getWalletBlockDiscriminatorBytes() {
+  return getU8Encoder().encode(WALLET_BLOCK_DISCRIMINATOR);
+}
+
+export type WalletBlock = { authority: Address };
+
+export type WalletBlockArgs = WalletBlock;
+
+export function getWalletBlockEncoder(): Encoder<WalletBlockArgs> {
+  return getStructEncoder([['authority', getAddressEncoder()]]);
+}
+
+export function getWalletBlockDecoder(): Decoder<WalletBlock> {
+  return getStructDecoder([['authority', getAddressDecoder()]]);
+}
+
+export function getWalletBlockCodec(): Codec<WalletBlockArgs, WalletBlock> {
+  return combineCodec(getWalletBlockEncoder(), getWalletBlockDecoder());
+}
+
+export function decodeWalletBlock<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress>
+): Account<WalletBlock, TAddress>;
+export function decodeWalletBlock<TAddress extends string = string>(
+  encodedAccount: MaybeEncodedAccount<TAddress>
+): MaybeAccount<WalletBlock, TAddress>;
+export function decodeWalletBlock<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>
+): Account<WalletBlock, TAddress> | MaybeAccount<WalletBlock, TAddress> {
+  return decodeAccount(
+    encodedAccount as MaybeEncodedAccount<TAddress>,
+    getWalletBlockDecoder()
+  );
+}
+
+export async function fetchWalletBlock<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<Account<WalletBlock, TAddress>> {
+  const maybeAccount = await fetchMaybeWalletBlock(rpc, address, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeWalletBlock<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<MaybeAccount<WalletBlock, TAddress>> {
+  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
+  return decodeWalletBlock(maybeAccount);
+}
+
+export async function fetchAllWalletBlock(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<Account<WalletBlock>[]> {
+  const maybeAccounts = await fetchAllMaybeWalletBlock(rpc, addresses, config);
+  assertAccountsExist(maybeAccounts);
+  return maybeAccounts;
+}
+
+export async function fetchAllMaybeWalletBlock(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<MaybeAccount<WalletBlock>[]> {
+  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
+  return maybeAccounts.map((maybeAccount) => decodeWalletBlock(maybeAccount));
+}
+
+export function getWalletBlockSize(): number {
+  return 33;
+}
+
+export async function fetchWalletBlockFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  seeds: WalletBlockSeeds,
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<Account<WalletBlock>> {
+  const maybeAccount = await fetchMaybeWalletBlockFromSeeds(rpc, seeds, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeWalletBlockFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  seeds: WalletBlockSeeds,
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<MaybeAccount<WalletBlock>> {
+  const { programAddress, ...fetchConfig } = config;
+  const [address] = await findWalletBlockPda(seeds, { programAddress });
+  return await fetchMaybeWalletBlock(rpc, address, fetchConfig);
+}

+ 12 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/index.ts

@@ -0,0 +1,12 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './accounts';
+export * from './instructions';
+export * from './pdas';
+export * from './programs';

+ 314 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/blockWallet.ts

@@ -0,0 +1,314 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Decoder,
+  getU8Encoder,
+  transformEncoder,
+  type Address,
+  type Codec,
+  type Decoder,
+  type Encoder,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type IInstruction,
+  type IInstructionWithAccounts,
+  type IInstructionWithData,
+  type ReadonlyAccount,
+  type TransactionSigner,
+  type WritableAccount,
+  type WritableSignerAccount,
+} from '@solana/kit';
+import { findConfigPda } from '../pdas';
+import { BLOCK_LIST_PROGRAM_ADDRESS } from '../programs';
+import { getAccountMetaFactory, type ResolvedAccount } from '../shared';
+
+export const BLOCK_WALLET_DISCRIMINATOR = 242;
+
+export function getBlockWalletDiscriminatorBytes() {
+  return getU8Encoder().encode(BLOCK_WALLET_DISCRIMINATOR);
+}
+
+export type BlockWalletInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountAuthority extends string | IAccountMeta<string> = string,
+  TAccountConfig extends string | IAccountMeta<string> = string,
+  TAccountWallet extends string | IAccountMeta<string> = string,
+  TAccountWalletBlock extends string | IAccountMeta<string> = string,
+  TAccountSystemProgram extends
+    | string
+    | IAccountMeta<string> = '11111111111111111111111111111111',
+  TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
+> = IInstruction<TProgram> &
+  IInstructionWithData<Uint8Array> &
+  IInstructionWithAccounts<
+    [
+      TAccountAuthority extends string
+        ? WritableSignerAccount<TAccountAuthority> &
+            IAccountSignerMeta<TAccountAuthority>
+        : TAccountAuthority,
+      TAccountConfig extends string
+        ? WritableAccount<TAccountConfig>
+        : TAccountConfig,
+      TAccountWallet extends string
+        ? ReadonlyAccount<TAccountWallet>
+        : TAccountWallet,
+      TAccountWalletBlock extends string
+        ? WritableAccount<TAccountWalletBlock>
+        : TAccountWalletBlock,
+      TAccountSystemProgram extends string
+        ? ReadonlyAccount<TAccountSystemProgram>
+        : TAccountSystemProgram,
+      ...TRemainingAccounts,
+    ]
+  >;
+
+export type BlockWalletInstructionData = { discriminator: number };
+
+export type BlockWalletInstructionDataArgs = {};
+
+export function getBlockWalletInstructionDataEncoder(): Encoder<BlockWalletInstructionDataArgs> {
+  return transformEncoder(
+    getStructEncoder([['discriminator', getU8Encoder()]]),
+    (value) => ({ ...value, discriminator: 242 })
+  );
+}
+
+export function getBlockWalletInstructionDataDecoder(): Decoder<BlockWalletInstructionData> {
+  return getStructDecoder([['discriminator', getU8Decoder()]]);
+}
+
+export function getBlockWalletInstructionDataCodec(): Codec<
+  BlockWalletInstructionDataArgs,
+  BlockWalletInstructionData
+> {
+  return combineCodec(
+    getBlockWalletInstructionDataEncoder(),
+    getBlockWalletInstructionDataDecoder()
+  );
+}
+
+export type BlockWalletAsyncInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountWallet extends string = string,
+  TAccountWalletBlock extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config?: Address<TAccountConfig>;
+  wallet: Address<TAccountWallet>;
+  walletBlock: Address<TAccountWalletBlock>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export async function getBlockWalletInstructionAsync<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountWallet extends string,
+  TAccountWalletBlock extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: BlockWalletAsyncInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): Promise<
+  BlockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    wallet: { value: input.wallet ?? null, isWritable: false },
+    walletBlock: { value: input.walletBlock ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.config.value) {
+    accounts.config.value = await findConfigPda();
+  }
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.wallet),
+      getAccountMeta(accounts.walletBlock),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getBlockWalletInstructionDataEncoder().encode({}),
+  } as BlockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type BlockWalletInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountWallet extends string = string,
+  TAccountWalletBlock extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config: Address<TAccountConfig>;
+  wallet: Address<TAccountWallet>;
+  walletBlock: Address<TAccountWalletBlock>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export function getBlockWalletInstruction<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountWallet extends string,
+  TAccountWalletBlock extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: BlockWalletInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): BlockWalletInstruction<
+  TProgramAddress,
+  TAccountAuthority,
+  TAccountConfig,
+  TAccountWallet,
+  TAccountWalletBlock,
+  TAccountSystemProgram
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    wallet: { value: input.wallet ?? null, isWritable: false },
+    walletBlock: { value: input.walletBlock ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.wallet),
+      getAccountMeta(accounts.walletBlock),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getBlockWalletInstructionDataEncoder().encode({}),
+  } as BlockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type ParsedBlockWalletInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
+> = {
+  programAddress: Address<TProgram>;
+  accounts: {
+    authority: TAccountMetas[0];
+    config: TAccountMetas[1];
+    wallet: TAccountMetas[2];
+    walletBlock: TAccountMetas[3];
+    systemProgram: TAccountMetas[4];
+  };
+  data: BlockWalletInstructionData;
+};
+
+export function parseBlockWalletInstruction<
+  TProgram extends string,
+  TAccountMetas extends readonly IAccountMeta[],
+>(
+  instruction: IInstruction<TProgram> &
+    IInstructionWithAccounts<TAccountMetas> &
+    IInstructionWithData<Uint8Array>
+): ParsedBlockWalletInstruction<TProgram, TAccountMetas> {
+  if (instruction.accounts.length < 5) {
+    // TODO: Coded error.
+    throw new Error('Not enough accounts');
+  }
+  let accountIndex = 0;
+  const getNextAccount = () => {
+    const accountMeta = instruction.accounts![accountIndex]!;
+    accountIndex += 1;
+    return accountMeta;
+  };
+  return {
+    programAddress: instruction.programAddress,
+    accounts: {
+      authority: getNextAccount(),
+      config: getNextAccount(),
+      wallet: getNextAccount(),
+      walletBlock: getNextAccount(),
+      systemProgram: getNextAccount(),
+    },
+    data: getBlockWalletInstructionDataDecoder().decode(instruction.data),
+  };
+}

+ 12 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/index.ts

@@ -0,0 +1,12 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './blockWallet';
+export * from './init';
+export * from './setupExtraMetas';
+export * from './unblockWallet';

+ 266 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/init.ts

@@ -0,0 +1,266 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Decoder,
+  getU8Encoder,
+  transformEncoder,
+  type Address,
+  type Codec,
+  type Decoder,
+  type Encoder,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type IInstruction,
+  type IInstructionWithAccounts,
+  type IInstructionWithData,
+  type ReadonlyAccount,
+  type TransactionSigner,
+  type WritableAccount,
+  type WritableSignerAccount,
+} from '@solana/kit';
+import { findConfigPda } from '../pdas';
+import { BLOCK_LIST_PROGRAM_ADDRESS } from '../programs';
+import { getAccountMetaFactory, type ResolvedAccount } from '../shared';
+
+export const INIT_DISCRIMINATOR = 241;
+
+export function getInitDiscriminatorBytes() {
+  return getU8Encoder().encode(INIT_DISCRIMINATOR);
+}
+
+export type InitInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountAuthority extends string | IAccountMeta<string> = string,
+  TAccountConfig extends string | IAccountMeta<string> = string,
+  TAccountSystemProgram extends
+    | string
+    | IAccountMeta<string> = '11111111111111111111111111111111',
+  TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
+> = IInstruction<TProgram> &
+  IInstructionWithData<Uint8Array> &
+  IInstructionWithAccounts<
+    [
+      TAccountAuthority extends string
+        ? WritableSignerAccount<TAccountAuthority> &
+            IAccountSignerMeta<TAccountAuthority>
+        : TAccountAuthority,
+      TAccountConfig extends string
+        ? WritableAccount<TAccountConfig>
+        : TAccountConfig,
+      TAccountSystemProgram extends string
+        ? ReadonlyAccount<TAccountSystemProgram>
+        : TAccountSystemProgram,
+      ...TRemainingAccounts,
+    ]
+  >;
+
+export type InitInstructionData = { discriminator: number };
+
+export type InitInstructionDataArgs = {};
+
+export function getInitInstructionDataEncoder(): Encoder<InitInstructionDataArgs> {
+  return transformEncoder(
+    getStructEncoder([['discriminator', getU8Encoder()]]),
+    (value) => ({ ...value, discriminator: 241 })
+  );
+}
+
+export function getInitInstructionDataDecoder(): Decoder<InitInstructionData> {
+  return getStructDecoder([['discriminator', getU8Decoder()]]);
+}
+
+export function getInitInstructionDataCodec(): Codec<
+  InitInstructionDataArgs,
+  InitInstructionData
+> {
+  return combineCodec(
+    getInitInstructionDataEncoder(),
+    getInitInstructionDataDecoder()
+  );
+}
+
+export type InitAsyncInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config?: Address<TAccountConfig>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export async function getInitInstructionAsync<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: InitAsyncInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): Promise<
+  InitInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountSystemProgram
+  >
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.config.value) {
+    accounts.config.value = await findConfigPda();
+  }
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getInitInstructionDataEncoder().encode({}),
+  } as InitInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type InitInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config: Address<TAccountConfig>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export function getInitInstruction<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: InitInput<TAccountAuthority, TAccountConfig, TAccountSystemProgram>,
+  config?: { programAddress?: TProgramAddress }
+): InitInstruction<
+  TProgramAddress,
+  TAccountAuthority,
+  TAccountConfig,
+  TAccountSystemProgram
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getInitInstructionDataEncoder().encode({}),
+  } as InitInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type ParsedInitInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
+> = {
+  programAddress: Address<TProgram>;
+  accounts: {
+    authority: TAccountMetas[0];
+    config: TAccountMetas[1];
+    systemProgram: TAccountMetas[2];
+  };
+  data: InitInstructionData;
+};
+
+export function parseInitInstruction<
+  TProgram extends string,
+  TAccountMetas extends readonly IAccountMeta[],
+>(
+  instruction: IInstruction<TProgram> &
+    IInstructionWithAccounts<TAccountMetas> &
+    IInstructionWithData<Uint8Array>
+): ParsedInitInstruction<TProgram, TAccountMetas> {
+  if (instruction.accounts.length < 3) {
+    // TODO: Coded error.
+    throw new Error('Not enough accounts');
+  }
+  let accountIndex = 0;
+  const getNextAccount = () => {
+    const accountMeta = instruction.accounts![accountIndex]!;
+    accountIndex += 1;
+    return accountMeta;
+  };
+  return {
+    programAddress: instruction.programAddress,
+    accounts: {
+      authority: getNextAccount(),
+      config: getNextAccount(),
+      systemProgram: getNextAccount(),
+    },
+    data: getInitInstructionDataDecoder().decode(instruction.data),
+  };
+}

+ 344 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/setupExtraMetas.ts

@@ -0,0 +1,344 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getBooleanDecoder,
+  getBooleanEncoder,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Decoder,
+  getU8Encoder,
+  transformEncoder,
+  type Address,
+  type Codec,
+  type Decoder,
+  type Encoder,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type IInstruction,
+  type IInstructionWithAccounts,
+  type IInstructionWithData,
+  type ReadonlyAccount,
+  type TransactionSigner,
+  type WritableAccount,
+  type WritableSignerAccount,
+} from '@solana/kit';
+import { findConfigPda, findExtraMetasPda } from '../pdas';
+import { BLOCK_LIST_PROGRAM_ADDRESS } from '../programs';
+import { getAccountMetaFactory, type ResolvedAccount } from '../shared';
+
+export const SETUP_EXTRA_METAS_DISCRIMINATOR = 106;
+
+export function getSetupExtraMetasDiscriminatorBytes() {
+  return getU8Encoder().encode(SETUP_EXTRA_METAS_DISCRIMINATOR);
+}
+
+export type SetupExtraMetasInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountAuthority extends string | IAccountMeta<string> = string,
+  TAccountConfig extends string | IAccountMeta<string> = string,
+  TAccountMint extends string | IAccountMeta<string> = string,
+  TAccountExtraMetas extends string | IAccountMeta<string> = string,
+  TAccountSystemProgram extends
+    | string
+    | IAccountMeta<string> = '11111111111111111111111111111111',
+  TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
+> = IInstruction<TProgram> &
+  IInstructionWithData<Uint8Array> &
+  IInstructionWithAccounts<
+    [
+      TAccountAuthority extends string
+        ? WritableSignerAccount<TAccountAuthority> &
+            IAccountSignerMeta<TAccountAuthority>
+        : TAccountAuthority,
+      TAccountConfig extends string
+        ? ReadonlyAccount<TAccountConfig>
+        : TAccountConfig,
+      TAccountMint extends string
+        ? ReadonlyAccount<TAccountMint>
+        : TAccountMint,
+      TAccountExtraMetas extends string
+        ? WritableAccount<TAccountExtraMetas>
+        : TAccountExtraMetas,
+      TAccountSystemProgram extends string
+        ? ReadonlyAccount<TAccountSystemProgram>
+        : TAccountSystemProgram,
+      ...TRemainingAccounts,
+    ]
+  >;
+
+export type SetupExtraMetasInstructionData = {
+  discriminator: number;
+  checkBothWallets: boolean;
+};
+
+export type SetupExtraMetasInstructionDataArgs = { checkBothWallets?: boolean };
+
+export function getSetupExtraMetasInstructionDataEncoder(): Encoder<SetupExtraMetasInstructionDataArgs> {
+  return transformEncoder(
+    getStructEncoder([
+      ['discriminator', getU8Encoder()],
+      ['checkBothWallets', getBooleanEncoder()],
+    ]),
+    (value) => ({
+      ...value,
+      discriminator: 106,
+      checkBothWallets: value.checkBothWallets ?? false,
+    })
+  );
+}
+
+export function getSetupExtraMetasInstructionDataDecoder(): Decoder<SetupExtraMetasInstructionData> {
+  return getStructDecoder([
+    ['discriminator', getU8Decoder()],
+    ['checkBothWallets', getBooleanDecoder()],
+  ]);
+}
+
+export function getSetupExtraMetasInstructionDataCodec(): Codec<
+  SetupExtraMetasInstructionDataArgs,
+  SetupExtraMetasInstructionData
+> {
+  return combineCodec(
+    getSetupExtraMetasInstructionDataEncoder(),
+    getSetupExtraMetasInstructionDataDecoder()
+  );
+}
+
+export type SetupExtraMetasAsyncInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountMint extends string = string,
+  TAccountExtraMetas extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config?: Address<TAccountConfig>;
+  mint: Address<TAccountMint>;
+  extraMetas?: Address<TAccountExtraMetas>;
+  systemProgram?: Address<TAccountSystemProgram>;
+  checkBothWallets?: SetupExtraMetasInstructionDataArgs['checkBothWallets'];
+};
+
+export async function getSetupExtraMetasInstructionAsync<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountMint extends string,
+  TAccountExtraMetas extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: SetupExtraMetasAsyncInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): Promise<
+  SetupExtraMetasInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: false },
+    mint: { value: input.mint ?? null, isWritable: false },
+    extraMetas: { value: input.extraMetas ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Original args.
+  const args = { ...input };
+
+  // Resolve default values.
+  if (!accounts.config.value) {
+    accounts.config.value = await findConfigPda();
+  }
+  if (!accounts.extraMetas.value) {
+    accounts.extraMetas.value = await findExtraMetasPda();
+  }
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.mint),
+      getAccountMeta(accounts.extraMetas),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getSetupExtraMetasInstructionDataEncoder().encode(
+      args as SetupExtraMetasInstructionDataArgs
+    ),
+  } as SetupExtraMetasInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type SetupExtraMetasInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountMint extends string = string,
+  TAccountExtraMetas extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config: Address<TAccountConfig>;
+  mint: Address<TAccountMint>;
+  extraMetas: Address<TAccountExtraMetas>;
+  systemProgram?: Address<TAccountSystemProgram>;
+  checkBothWallets?: SetupExtraMetasInstructionDataArgs['checkBothWallets'];
+};
+
+export function getSetupExtraMetasInstruction<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountMint extends string,
+  TAccountExtraMetas extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: SetupExtraMetasInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): SetupExtraMetasInstruction<
+  TProgramAddress,
+  TAccountAuthority,
+  TAccountConfig,
+  TAccountMint,
+  TAccountExtraMetas,
+  TAccountSystemProgram
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: false },
+    mint: { value: input.mint ?? null, isWritable: false },
+    extraMetas: { value: input.extraMetas ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Original args.
+  const args = { ...input };
+
+  // Resolve default values.
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.mint),
+      getAccountMeta(accounts.extraMetas),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getSetupExtraMetasInstructionDataEncoder().encode(
+      args as SetupExtraMetasInstructionDataArgs
+    ),
+  } as SetupExtraMetasInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type ParsedSetupExtraMetasInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
+> = {
+  programAddress: Address<TProgram>;
+  accounts: {
+    authority: TAccountMetas[0];
+    config: TAccountMetas[1];
+    mint: TAccountMetas[2];
+    extraMetas: TAccountMetas[3];
+    systemProgram: TAccountMetas[4];
+  };
+  data: SetupExtraMetasInstructionData;
+};
+
+export function parseSetupExtraMetasInstruction<
+  TProgram extends string,
+  TAccountMetas extends readonly IAccountMeta[],
+>(
+  instruction: IInstruction<TProgram> &
+    IInstructionWithAccounts<TAccountMetas> &
+    IInstructionWithData<Uint8Array>
+): ParsedSetupExtraMetasInstruction<TProgram, TAccountMetas> {
+  if (instruction.accounts.length < 5) {
+    // TODO: Coded error.
+    throw new Error('Not enough accounts');
+  }
+  let accountIndex = 0;
+  const getNextAccount = () => {
+    const accountMeta = instruction.accounts![accountIndex]!;
+    accountIndex += 1;
+    return accountMeta;
+  };
+  return {
+    programAddress: instruction.programAddress,
+    accounts: {
+      authority: getNextAccount(),
+      config: getNextAccount(),
+      mint: getNextAccount(),
+      extraMetas: getNextAccount(),
+      systemProgram: getNextAccount(),
+    },
+    data: getSetupExtraMetasInstructionDataDecoder().decode(instruction.data),
+  };
+}

+ 292 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/unblockWallet.ts

@@ -0,0 +1,292 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Decoder,
+  getU8Encoder,
+  transformEncoder,
+  type Address,
+  type Codec,
+  type Decoder,
+  type Encoder,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type IInstruction,
+  type IInstructionWithAccounts,
+  type IInstructionWithData,
+  type ReadonlyAccount,
+  type TransactionSigner,
+  type WritableAccount,
+  type WritableSignerAccount,
+} from '@solana/kit';
+import { findConfigPda } from '../pdas';
+import { BLOCK_LIST_PROGRAM_ADDRESS } from '../programs';
+import { getAccountMetaFactory, type ResolvedAccount } from '../shared';
+
+export const UNBLOCK_WALLET_DISCRIMINATOR = 243;
+
+export function getUnblockWalletDiscriminatorBytes() {
+  return getU8Encoder().encode(UNBLOCK_WALLET_DISCRIMINATOR);
+}
+
+export type UnblockWalletInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountAuthority extends string | IAccountMeta<string> = string,
+  TAccountConfig extends string | IAccountMeta<string> = string,
+  TAccountWalletBlock extends string | IAccountMeta<string> = string,
+  TAccountSystemProgram extends
+    | string
+    | IAccountMeta<string> = '11111111111111111111111111111111',
+  TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
+> = IInstruction<TProgram> &
+  IInstructionWithData<Uint8Array> &
+  IInstructionWithAccounts<
+    [
+      TAccountAuthority extends string
+        ? WritableSignerAccount<TAccountAuthority> &
+            IAccountSignerMeta<TAccountAuthority>
+        : TAccountAuthority,
+      TAccountConfig extends string
+        ? WritableAccount<TAccountConfig>
+        : TAccountConfig,
+      TAccountWalletBlock extends string
+        ? WritableAccount<TAccountWalletBlock>
+        : TAccountWalletBlock,
+      TAccountSystemProgram extends string
+        ? ReadonlyAccount<TAccountSystemProgram>
+        : TAccountSystemProgram,
+      ...TRemainingAccounts,
+    ]
+  >;
+
+export type UnblockWalletInstructionData = { discriminator: number };
+
+export type UnblockWalletInstructionDataArgs = {};
+
+export function getUnblockWalletInstructionDataEncoder(): Encoder<UnblockWalletInstructionDataArgs> {
+  return transformEncoder(
+    getStructEncoder([['discriminator', getU8Encoder()]]),
+    (value) => ({ ...value, discriminator: 243 })
+  );
+}
+
+export function getUnblockWalletInstructionDataDecoder(): Decoder<UnblockWalletInstructionData> {
+  return getStructDecoder([['discriminator', getU8Decoder()]]);
+}
+
+export function getUnblockWalletInstructionDataCodec(): Codec<
+  UnblockWalletInstructionDataArgs,
+  UnblockWalletInstructionData
+> {
+  return combineCodec(
+    getUnblockWalletInstructionDataEncoder(),
+    getUnblockWalletInstructionDataDecoder()
+  );
+}
+
+export type UnblockWalletAsyncInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountWalletBlock extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config?: Address<TAccountConfig>;
+  walletBlock: Address<TAccountWalletBlock>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export async function getUnblockWalletInstructionAsync<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountWalletBlock extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: UnblockWalletAsyncInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): Promise<
+  UnblockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    walletBlock: { value: input.walletBlock ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.config.value) {
+    accounts.config.value = await findConfigPda();
+  }
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.walletBlock),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getUnblockWalletInstructionDataEncoder().encode({}),
+  } as UnblockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type UnblockWalletInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountWalletBlock extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config: Address<TAccountConfig>;
+  walletBlock: Address<TAccountWalletBlock>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export function getUnblockWalletInstruction<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountWalletBlock extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: UnblockWalletInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): UnblockWalletInstruction<
+  TProgramAddress,
+  TAccountAuthority,
+  TAccountConfig,
+  TAccountWalletBlock,
+  TAccountSystemProgram
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    walletBlock: { value: input.walletBlock ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.walletBlock),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getUnblockWalletInstructionDataEncoder().encode({}),
+  } as UnblockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type ParsedUnblockWalletInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
+> = {
+  programAddress: Address<TProgram>;
+  accounts: {
+    authority: TAccountMetas[0];
+    config: TAccountMetas[1];
+    walletBlock: TAccountMetas[2];
+    systemProgram: TAccountMetas[3];
+  };
+  data: UnblockWalletInstructionData;
+};
+
+export function parseUnblockWalletInstruction<
+  TProgram extends string,
+  TAccountMetas extends readonly IAccountMeta[],
+>(
+  instruction: IInstruction<TProgram> &
+    IInstructionWithAccounts<TAccountMetas> &
+    IInstructionWithData<Uint8Array>
+): ParsedUnblockWalletInstruction<TProgram, TAccountMetas> {
+  if (instruction.accounts.length < 4) {
+    // TODO: Coded error.
+    throw new Error('Not enough accounts');
+  }
+  let accountIndex = 0;
+  const getNextAccount = () => {
+    const accountMeta = instruction.accounts![accountIndex]!;
+    accountIndex += 1;
+    return accountMeta;
+  };
+  return {
+    programAddress: instruction.programAddress,
+    accounts: {
+      authority: getNextAccount(),
+      config: getNextAccount(),
+      walletBlock: getNextAccount(),
+      systemProgram: getNextAccount(),
+    },
+    data: getUnblockWalletInstructionDataDecoder().decode(instruction.data),
+  };
+}

+ 26 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/config.ts

@@ -0,0 +1,26 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  getProgramDerivedAddress,
+  getUtf8Encoder,
+  type Address,
+  type ProgramDerivedAddress,
+} from '@solana/kit';
+
+export async function findConfigPda(
+  config: { programAddress?: Address | undefined } = {}
+): Promise<ProgramDerivedAddress> {
+  const {
+    programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>,
+  } = config;
+  return await getProgramDerivedAddress({
+    programAddress,
+    seeds: [getUtf8Encoder().encode('config')],
+  });
+}

+ 35 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/extraMetas.ts

@@ -0,0 +1,35 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  getAddressEncoder,
+  getProgramDerivedAddress,
+  getUtf8Encoder,
+  type Address,
+  type ProgramDerivedAddress,
+} from '@solana/kit';
+
+export type ExtraMetasSeeds = {
+  mint: Address;
+};
+
+export async function findExtraMetasPda(
+  seeds: ExtraMetasSeeds,
+  config: { programAddress?: Address | undefined } = {}
+): Promise<ProgramDerivedAddress> {
+  const {
+    programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>,
+  } = config;
+  return await getProgramDerivedAddress({
+    programAddress,
+    seeds: [
+      getUtf8Encoder().encode('extra-account-metas'),
+      getAddressEncoder().encode(seeds.mint),
+    ],
+  });
+}

+ 11 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/index.ts

@@ -0,0 +1,11 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './config';
+export * from './extraMetas';
+export * from './walletBlock';

+ 35 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/walletBlock.ts

@@ -0,0 +1,35 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  getAddressEncoder,
+  getProgramDerivedAddress,
+  getUtf8Encoder,
+  type Address,
+  type ProgramDerivedAddress,
+} from '@solana/kit';
+
+export type WalletBlockSeeds = {
+  wallet: Address;
+};
+
+export async function findWalletBlockPda(
+  seeds: WalletBlockSeeds,
+  config: { programAddress?: Address | undefined } = {}
+): Promise<ProgramDerivedAddress> {
+  const {
+    programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>,
+  } = config;
+  return await getProgramDerivedAddress({
+    programAddress,
+    seeds: [
+      getUtf8Encoder().encode('wallet_block'),
+      getAddressEncoder().encode(seeds.wallet),
+    ],
+  });
+}

+ 88 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/programs/blockList.ts

@@ -0,0 +1,88 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  containsBytes,
+  getU8Encoder,
+  type Address,
+  type ReadonlyUint8Array,
+} from '@solana/kit';
+import {
+  type ParsedBlockWalletInstruction,
+  type ParsedInitInstruction,
+  type ParsedSetupExtraMetasInstruction,
+  type ParsedUnblockWalletInstruction,
+} from '../instructions';
+
+export const BLOCK_LIST_PROGRAM_ADDRESS =
+  'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>;
+
+export enum BlockListAccount {
+  Config,
+  WalletBlock,
+  ExtraMetas,
+}
+
+export function identifyBlockListAccount(
+  account: { data: ReadonlyUint8Array } | ReadonlyUint8Array
+): BlockListAccount {
+  const data = 'data' in account ? account.data : account;
+  if (containsBytes(data, getU8Encoder().encode(0), 0)) {
+    return BlockListAccount.Config;
+  }
+  if (containsBytes(data, getU8Encoder().encode(1), 0)) {
+    return BlockListAccount.WalletBlock;
+  }
+  throw new Error(
+    'The provided account could not be identified as a blockList account.'
+  );
+}
+
+export enum BlockListInstruction {
+  Init,
+  BlockWallet,
+  UnblockWallet,
+  SetupExtraMetas,
+}
+
+export function identifyBlockListInstruction(
+  instruction: { data: ReadonlyUint8Array } | ReadonlyUint8Array
+): BlockListInstruction {
+  const data = 'data' in instruction ? instruction.data : instruction;
+  if (containsBytes(data, getU8Encoder().encode(241), 0)) {
+    return BlockListInstruction.Init;
+  }
+  if (containsBytes(data, getU8Encoder().encode(242), 0)) {
+    return BlockListInstruction.BlockWallet;
+  }
+  if (containsBytes(data, getU8Encoder().encode(243), 0)) {
+    return BlockListInstruction.UnblockWallet;
+  }
+  if (containsBytes(data, getU8Encoder().encode(106), 0)) {
+    return BlockListInstruction.SetupExtraMetas;
+  }
+  throw new Error(
+    'The provided instruction could not be identified as a blockList instruction.'
+  );
+}
+
+export type ParsedBlockListInstruction<
+  TProgram extends string = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf',
+> =
+  | ({
+      instructionType: BlockListInstruction.Init;
+    } & ParsedInitInstruction<TProgram>)
+  | ({
+      instructionType: BlockListInstruction.BlockWallet;
+    } & ParsedBlockWalletInstruction<TProgram>)
+  | ({
+      instructionType: BlockListInstruction.UnblockWallet;
+    } & ParsedUnblockWalletInstruction<TProgram>)
+  | ({
+      instructionType: BlockListInstruction.SetupExtraMetas;
+    } & ParsedSetupExtraMetasInstruction<TProgram>);

+ 9 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/programs/index.ts

@@ -0,0 +1,9 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './blockList';

+ 164 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/shared/index.ts

@@ -0,0 +1,164 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  AccountRole,
+  isProgramDerivedAddress,
+  isTransactionSigner as kitIsTransactionSigner,
+  type Address,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type ProgramDerivedAddress,
+  type TransactionSigner,
+  upgradeRoleToSigner,
+} from '@solana/kit';
+
+/**
+ * Asserts that the given value is not null or undefined.
+ * @internal
+ */
+export function expectSome<T>(value: T | null | undefined): T {
+  if (value == null) {
+    throw new Error('Expected a value but received null or undefined.');
+  }
+  return value;
+}
+
+/**
+ * Asserts that the given value is a PublicKey.
+ * @internal
+ */
+export function expectAddress<T extends string = string>(
+  value:
+    | Address<T>
+    | ProgramDerivedAddress<T>
+    | TransactionSigner<T>
+    | null
+    | undefined
+): Address<T> {
+  if (!value) {
+    throw new Error('Expected a Address.');
+  }
+  if (typeof value === 'object' && 'address' in value) {
+    return value.address;
+  }
+  if (Array.isArray(value)) {
+    return value[0];
+  }
+  return value as Address<T>;
+}
+
+/**
+ * Asserts that the given value is a PDA.
+ * @internal
+ */
+export function expectProgramDerivedAddress<T extends string = string>(
+  value:
+    | Address<T>
+    | ProgramDerivedAddress<T>
+    | TransactionSigner<T>
+    | null
+    | undefined
+): ProgramDerivedAddress<T> {
+  if (!value || !Array.isArray(value) || !isProgramDerivedAddress(value)) {
+    throw new Error('Expected a ProgramDerivedAddress.');
+  }
+  return value;
+}
+
+/**
+ * Asserts that the given value is a TransactionSigner.
+ * @internal
+ */
+export function expectTransactionSigner<T extends string = string>(
+  value:
+    | Address<T>
+    | ProgramDerivedAddress<T>
+    | TransactionSigner<T>
+    | null
+    | undefined
+): TransactionSigner<T> {
+  if (!value || !isTransactionSigner(value)) {
+    throw new Error('Expected a TransactionSigner.');
+  }
+  return value;
+}
+
+/**
+ * Defines an instruction account to resolve.
+ * @internal
+ */
+export type ResolvedAccount<
+  T extends string = string,
+  U extends
+    | Address<T>
+    | ProgramDerivedAddress<T>
+    | TransactionSigner<T>
+    | null =
+    | Address<T>
+    | ProgramDerivedAddress<T>
+    | TransactionSigner<T>
+    | null,
+> = {
+  isWritable: boolean;
+  value: U;
+};
+
+/**
+ * Defines an instruction that stores additional bytes on-chain.
+ * @internal
+ */
+export type IInstructionWithByteDelta = {
+  byteDelta: number;
+};
+
+/**
+ * Get account metas and signers from resolved accounts.
+ * @internal
+ */
+export function getAccountMetaFactory(
+  programAddress: Address,
+  optionalAccountStrategy: 'omitted' | 'programId'
+) {
+  return (
+    account: ResolvedAccount
+  ): IAccountMeta | IAccountSignerMeta | undefined => {
+    if (!account.value) {
+      if (optionalAccountStrategy === 'omitted') return;
+      return Object.freeze({
+        address: programAddress,
+        role: AccountRole.READONLY,
+      });
+    }
+
+    const writableRole = account.isWritable
+      ? AccountRole.WRITABLE
+      : AccountRole.READONLY;
+    return Object.freeze({
+      address: expectAddress(account.value),
+      role: isTransactionSigner(account.value)
+        ? upgradeRoleToSigner(writableRole)
+        : writableRole,
+      ...(isTransactionSigner(account.value) ? { signer: account.value } : {}),
+    });
+  };
+}
+
+export function isTransactionSigner<TAddress extends string = string>(
+  value:
+    | Address<TAddress>
+    | ProgramDerivedAddress<TAddress>
+    | TransactionSigner<TAddress>
+): value is TransactionSigner<TAddress> {
+  return (
+    !!value &&
+    typeof value === 'object' &&
+    'address' in value &&
+    kitIsTransactionSigner(value)
+  );
+}

+ 13 - 0
tokens/token-2022/transfer-hook/pblock-list/pinocchio/tsconfig.json

@@ -0,0 +1,13 @@
+{
+    "compilerOptions": {
+      "target": "ESNext",
+      "module": "CommonJS",
+      "moduleResolution": "node",
+      "esModuleInterop": true,
+      "types": ["mocha", "node"],
+      "strict": true,
+      "skipLibCheck": true
+    },
+    "include": ["**/*.ts"],
+    "exclude": ["node_modules"]
+}

+ 15 - 0
tokens/token-2022/transfer-hook/pblock-list/program/Cargo.toml

@@ -0,0 +1,15 @@
+[package]
+name = "block-list"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["lib", "cdylib"]
+name = "block_list"
+
+[dependencies]
+bytemuck = "1.23.0"
+pinocchio = "0.8.4"
+pinocchio-pubkey = "0.2.4"
+pinocchio-system = "0.2.3"
+pinocchio-log = "0.4.0"

+ 25 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/error.rs

@@ -0,0 +1,25 @@
+use pinocchio::program_error::ProgramError;
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub enum BlockListError {
+    InvalidInstruction,
+
+    InvalidAuthority,
+    AccountBlocked,
+    NotEnoughAccounts,
+    InvalidAccountData,
+    UninitializedAccount,
+    InvalidSystemProgram,
+    InvalidConfigAccount,
+    AccountNotWritable,
+    InvalidMint,
+    InvalidExtraMetasAccount,
+    ImmutableOwnerExtensionMissing,
+}
+
+
+impl From<BlockListError> for ProgramError {
+    fn from(e: BlockListError) -> Self {
+        ProgramError::Custom(e as u32)
+    }
+}

+ 87 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/block_wallet.rs

@@ -0,0 +1,87 @@
+use pinocchio::{account_info::AccountInfo, instruction::Signer, program_error::ProgramError, pubkey::find_program_address, seeds, sysvars::{rent::Rent, Sysvar}, ProgramResult};
+
+use crate::{load, load_mut_unchecked, BlockListError, Config, Discriminator, Transmutable, WalletBlock};
+
+
+pub struct BlockWallet<'a> {
+    pub authority: &'a AccountInfo,
+    pub config: &'a AccountInfo,
+    pub wallet: &'a AccountInfo,
+    pub wallet_block: &'a AccountInfo,
+    pub system_program: &'a AccountInfo,
+    pub wallet_block_bump: u8,
+}
+
+impl<'a> BlockWallet<'a> {
+    pub fn process(&self) -> ProgramResult {
+        let lamports = Rent::get()?.minimum_balance(WalletBlock::LEN);
+
+        let bump_seed = [self.wallet_block_bump];
+        let seeds = seeds!(WalletBlock::SEED_PREFIX, self.wallet.key(), &bump_seed);
+        let signer = Signer::from(&seeds);
+            
+        pinocchio_system::instructions::CreateAccount {
+            from: self.authority,
+            to: self.wallet_block,
+            lamports,
+            space: WalletBlock::LEN as u64,
+            owner: &crate::ID,
+        }.invoke_signed(&[signer])?;
+
+        let mut data = self.wallet_block.try_borrow_mut_data()?;
+        let wallet_block = unsafe { 
+            load_mut_unchecked::<WalletBlock>(&mut data)? 
+        };
+        wallet_block.discriminator = WalletBlock::DISCRIMINATOR;
+        wallet_block.address = *self.wallet.key();
+
+        let config = unsafe { load_mut_unchecked::<Config>(self.config.borrow_mut_data_unchecked())? };
+        config.blocked_wallets_count = config.blocked_wallets_count.checked_add(1).ok_or(ProgramError::ArithmeticOverflow)?;
+        
+        Ok(())
+    }
+}
+
+impl<'a> Discriminator for BlockWallet<'a> {
+    const DISCRIMINATOR: u8 = 0xF2;
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for BlockWallet<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+        let [authority, config, wallet, wallet_block, system_program] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        let cfg = unsafe { load::<Config>(config.borrow_data_unchecked())? };
+        
+        if !config.is_owned_by(&crate::ID) {
+            return Err(BlockListError::InvalidConfigAccount);
+        }
+
+        if !authority.is_signer() || cfg.authority.ne(authority.key()) {
+            return Err(BlockListError::InvalidAuthority);
+        }
+
+        if !config.is_writable() && !wallet_block.is_writable() {
+            return Err(BlockListError::AccountNotWritable);
+        }
+
+        let (_, wallet_block_bump) = find_program_address(&[WalletBlock::SEED_PREFIX, wallet.key()], &crate::ID);
+
+        // check if system program is valid
+        if system_program.key().ne(&pinocchio_system::ID) {
+            return Err(BlockListError::InvalidSystemProgram);
+        }
+
+        Ok(Self {
+            authority,
+            config,
+            wallet,
+            wallet_block,
+            system_program,
+            wallet_block_bump,
+        })
+    }
+}

+ 85 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/init.rs

@@ -0,0 +1,85 @@
+use pinocchio::{account_info::AccountInfo, instruction::Signer, pubkey::find_program_address, seeds, sysvars::{rent::Rent, Sysvar}, ProgramResult};
+
+use crate::{load_mut_unchecked, BlockListError, Config, Discriminator, Transmutable};
+
+
+
+pub struct Init<'a> {
+    pub authority: &'a AccountInfo,
+    pub config: &'a AccountInfo,
+    pub system_program: &'a AccountInfo,
+    pub config_bump: u8,
+}
+
+impl<'a> Discriminator for Init<'a> {
+    const DISCRIMINATOR: u8 = 0xF1;
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for Init<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+        let [authority, config, system_program] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        if !authority.is_signer() {
+            return Err(BlockListError::InvalidAuthority);
+        }
+
+        /* do we really need to check this? its going to fail silently if not writable
+        if !config.is_writable {
+            return Err(BlockListError::InvalidInstruction);
+        }*/
+
+
+        // derive config account
+        let (_, config_bump) = find_program_address(&[Config::SEED_PREFIX], &crate::ID);
+        // no need to check if address is valid
+        // cpi call with config as signer, runtime will check if the right account has been signer escalated
+
+        //if config_account.ne(config.key()) {
+        //    return Err(BlockListError::InvalidConfigAccount);
+        //}
+
+        // check if system program is valid
+        if system_program.key().ne(&pinocchio_system::ID) {
+            return Err(BlockListError::InvalidSystemProgram);
+        }
+
+
+        Ok(Self {
+            authority,
+            config,
+            system_program,
+            config_bump,
+        })
+    }
+}
+
+impl<'a> Init<'a> {
+    pub fn process(&self) -> ProgramResult {
+        let lamports = Rent::get()?.minimum_balance(Config::LEN);
+
+        let bump_seed = [self.config_bump];
+        let seeds = seeds!(Config::SEED_PREFIX, &bump_seed);
+        let signer = Signer::from(&seeds);
+            
+        pinocchio_system::instructions::CreateAccount {
+            from: self.authority,
+            to: self.config,
+            lamports,
+            space: Config::LEN as u64,
+            owner: &crate::ID,
+        }.invoke_signed(&[signer])?;
+
+        let mut data = self.config.try_borrow_mut_data()?;
+        let config = unsafe { 
+            load_mut_unchecked::<Config>(&mut data)? 
+        };
+        config.discriminator = Config::DISCRIMINATOR;
+        config.authority = *self.authority.key();
+
+        Ok(())
+    }
+}

+ 11 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/mod.rs

@@ -0,0 +1,11 @@
+pub mod tx_hook;
+pub mod init;
+pub mod block_wallet;
+pub mod unblock_wallet;
+pub mod setup_extra_metas;
+
+pub use tx_hook::*;
+pub use init::*;
+pub use block_wallet::*;
+pub use unblock_wallet::*;
+pub use setup_extra_metas::*;

+ 133 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/setup_extra_metas.rs

@@ -0,0 +1,133 @@
+use pinocchio::{account_info::AccountInfo, instruction::Signer, memory::sol_memcpy, pubkey::find_program_address, seeds, sysvars::{rent::Rent, Sysvar}, ProgramResult};
+
+use crate::{load, token2022_utils::{get_transfer_hook_authority, EXTRA_METAS_SEED, is_token_2022_mint}, BlockListError, Config, Discriminator};
+
+
+pub struct SetupExtraMetas<'a> {
+    pub authority: &'a AccountInfo,
+    pub config: &'a AccountInfo,
+    pub mint: &'a AccountInfo,
+    pub extra_metas: &'a AccountInfo,
+    pub system_program: &'a AccountInfo,
+    pub extra_metas_bump: u8,
+}
+
+impl<'a> Discriminator for SetupExtraMetas<'a> {
+    const DISCRIMINATOR: u8 = 0x6A;
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for SetupExtraMetas<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+        let [authority, config, mint, extra_metas, system_program] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        if !authority.is_signer() {
+            return Err(BlockListError::InvalidAuthority);
+        }
+
+        if !is_token_2022_mint(mint) {
+            return Err(BlockListError::InvalidMint);
+        }
+
+        let transfer_hook_authority = get_transfer_hook_authority(unsafe { mint.borrow_data_unchecked() });
+        if transfer_hook_authority.is_none() || !transfer_hook_authority.unwrap().eq(authority.key()) {
+            return Err(BlockListError::InvalidAuthority);
+        }
+
+        // derive extra_metas account
+        let (extra_metas_address, extra_metas_bump) = find_program_address(&[EXTRA_METAS_SEED, mint.key()], &crate::ID);
+
+        if extra_metas_address.ne(extra_metas.key()) {
+            return Err(BlockListError::InvalidExtraMetasAccount);
+        }
+
+        // check if system program is valid
+        if system_program.key().ne(&pinocchio_system::ID) {
+            return Err(BlockListError::InvalidSystemProgram);
+        }
+
+        Ok(Self {
+            authority,
+            config,
+            mint,
+            extra_metas,
+            system_program,
+            extra_metas_bump,
+        })
+    }
+}
+
+impl<'a> SetupExtraMetas<'a> {
+    pub fn process(&self, remaining_data: &[u8]) -> ProgramResult {
+        let config = unsafe { load::<Config>(&self.config.borrow_data_unchecked())? };
+        
+        let data = if config.blocked_wallets_count == 0 {
+            EXTRA_METAS_EMPTY_DEPENDENCIES
+        } else if remaining_data.len() == 1 && remaining_data[0] == 1 {
+            EXTRA_METAS_BOTH_DEPENDENCIES
+        } else {
+            EXTRA_METAS_SOURCE_DEPENDENCY
+        };
+
+        let min_lamports = Rent::get()?.minimum_balance(data.len());
+
+        if self.extra_metas.is_owned_by(&crate::ID) {
+            let current_lamports = self.extra_metas.lamports();
+            let auth_lamports = self.authority.lamports();
+
+            // just resize
+            self.extra_metas.realloc(data.len(), false)?;
+
+            if current_lamports < min_lamports {
+                // transfer to extra
+                let diff = min_lamports - current_lamports;
+                pinocchio_system::instructions::Transfer {
+                    from: self.authority,
+                    to: self.extra_metas,
+                    lamports: diff,
+                }.invoke()?;
+            } else if current_lamports > min_lamports {
+                // transfer from extra
+                let diff = current_lamports - min_lamports;
+                unsafe {
+                    *self.extra_metas.borrow_mut_lamports_unchecked() = min_lamports;
+                    *self.authority.borrow_mut_lamports_unchecked() = auth_lamports.checked_add(diff).unwrap();
+                }
+            }
+        } else {
+            // create new account
+
+            let bump_seed = [self.extra_metas_bump];
+            let seeds = seeds!(EXTRA_METAS_SEED, self.mint.key(), &bump_seed);
+            let signer = Signer::from(&seeds);
+                
+            pinocchio_system::instructions::CreateAccount {
+                from: self.authority,
+                to: self.extra_metas,
+                lamports: min_lamports,
+                space: data.len() as u64,
+                owner: &crate::ID,
+            }.invoke_signed(&[signer])?;
+        }
+
+        // overwrite state depending on config
+
+        let extra_metas_data = unsafe { self.extra_metas.borrow_mut_data_unchecked() };
+
+        unsafe { sol_memcpy(extra_metas_data, data, data.len()); }
+
+        Ok(())
+    }
+}
+
+
+/// HOW TO GET THESE MAGIC VALUES
+/// run the CLI using `block-list-cli get-extra-metas-account-data`
+/// it will generate the 3 arrays without needing to add more dependencies (bloat) to the program
+pub const EXTRA_METAS_EMPTY_DEPENDENCIES: &[u8] = &[105, 37, 101, 197, 75, 251, 102, 26, 4, 0, 0, 0, 0, 0, 0, 0];
+pub const EXTRA_METAS_SOURCE_DEPENDENCY: &[u8] = &[105, 37, 101, 197, 75, 251, 102, 26, 39, 0, 0, 0, 1, 0, 0, 0, 1, 1, 12, 119, 97, 108, 108, 101, 116, 95, 98, 108, 111, 99, 107, 4, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+pub const EXTRA_METAS_BOTH_DEPENDENCIES: &[u8] = &[105, 37, 101, 197, 75, 251, 102, 26, 74, 0, 0, 0, 2, 0, 0, 0, 1, 1, 12, 119, 97, 108, 108, 101, 116, 95, 98, 108, 111, 99, 107, 4, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 12, 119, 97, 108, 108, 101, 116, 95, 98, 108, 111, 99, 107, 4, 2, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
+

+ 135 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/tx_hook.rs

@@ -0,0 +1,135 @@
+use pinocchio::{account_info::AccountInfo, pubkey::Pubkey, ProgramResult};
+use pinocchio_log::logger::Logger;
+
+use crate::{load, token2022_utils::has_immutable_owner_extension, BlockListError, WalletBlock};
+
+///
+/// SECURITY ASSUMPTIONS OVER TX-HOOK
+/// 
+/// 1- its called by the token-2022 program
+/// 2- if some other program is calling it, we don't care as we don't write state here
+/// 2- its inputs are already sanitized by the token-2022 program
+/// 3- if some other program is calling it with invalid inputs, we don't care as we only read state and return ok/nok
+/// 4- there may be 3 different extra metas setup
+/// 4.1- no extra accounts
+/// 4.2- only source wallet block
+/// 4.3- both source and destination wallet blocks
+/// 5- given all the above we can skip a lot of type and owner checks
+
+pub struct TxHook<'a> {
+    pub source: &'a AccountInfo,
+    pub mint: &'a AccountInfo,
+    pub destination: &'a AccountInfo,
+    pub authority: &'a AccountInfo,
+    pub source_wallet_block: Option<&'a AccountInfo>,
+    pub destination_wallet_block: Option<&'a AccountInfo>,
+    //pub remaining_accounts: &'a [AccountInfo],
+}
+
+impl<'a> TxHook<'a> {
+    pub const DISCRIMINATOR: u8 = 0x69;
+
+    pub fn process(&self) -> ProgramResult {
+        // check if there is a wallet block for the source account
+        if let Some(source_wallet_block) = self.source_wallet_block {
+            let source_data = unsafe {self.source.borrow_data_unchecked()};
+            // without the immutable owner extension, TA owners could bypass wallet blocks
+            // by changing the owner to a different wallet controlled by the same entity
+            if !has_immutable_owner_extension(source_data) {
+                let mut logger = Logger::<64>::default();
+                logger.append("Transfer Blocked: Source TA - ImmutableOwnerExtensionMissing");
+                logger.log();
+                return Err(BlockListError::ImmutableOwnerExtensionMissing.into());
+            }
+
+            if !source_wallet_block.data_is_empty() {
+
+                let _ = unsafe { load::<WalletBlock>(source_wallet_block.borrow_data_unchecked())? };
+
+                // its a potential blocked wallet
+                // lets check if authority is not the owner nor the delegate
+                // this implies its the permanent delegate
+                // alternatively we can decode the mint and get the permanent delegate
+
+                let owner = unsafe { &*(source_data[32..64].as_ptr() as *const Pubkey) };
+                let delegate = unsafe { &*(source_data[76..108].as_ptr() as *const Pubkey) };
+
+                if owner.eq(self.authority.key()) || delegate.eq(self.authority.key()) {
+                    let mut logger = Logger::<64>::default();
+                    logger.append("Transfer Blocked:  Source TA - AccountBlocked");
+                    logger.log();
+                    return Err(BlockListError::AccountBlocked.into());
+                }
+                
+            }
+
+        }
+
+        // check if there is a wallet block for the destination account
+        if let Some(destination_wallet_block) = self.destination_wallet_block {
+
+            if !has_immutable_owner_extension(unsafe {self.destination.borrow_data_unchecked()}) {
+                let mut logger = Logger::<64>::default();
+                logger.append("Transfer Blocked: Destination TA - ImmutableOwnerExtensionMissing");
+                logger.log();
+                return Err(BlockListError::ImmutableOwnerExtensionMissing.into());
+            }
+
+            if !destination_wallet_block.data_is_empty() {
+
+                let _ = unsafe { load::<WalletBlock>(destination_wallet_block.borrow_data_unchecked())? };
+                
+                let mut logger = Logger::<64>::default();
+                logger.append("Transfer Blocked:  Destination TA - AccountBlocked");
+                logger.log();
+
+                return Err(BlockListError::AccountBlocked.into());
+            }
+
+        }
+
+        Ok(())
+    }
+
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for TxHook<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+
+        /*
+        TX HOOK GETS CALLED WITH:
+         1- source TA
+         2- mint
+         3- destination TA
+         4- authority (either src owner or src delegate)
+         5- extra account metas
+         6- (optional) source wallet block 
+         7- (optional) destination wallet block 
+         */
+
+        let [source, mint, destination, authority, remaining_accounts @ ..] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        let (source_wallet_block, destination_wallet_block) = if remaining_accounts.len() == 2 {
+            (Some(&remaining_accounts[1]), None)
+        } else if remaining_accounts.len() == 3 {
+            (Some(&remaining_accounts[1]), Some(&remaining_accounts[2]))
+        } else {
+            (None, None)
+        };
+
+
+
+        Ok(Self {
+            source,
+            destination,
+            mint,
+            authority,
+            source_wallet_block,
+            destination_wallet_block,
+        })
+    }
+}

+ 69 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/unblock_wallet.rs

@@ -0,0 +1,69 @@
+use pinocchio::{account_info::AccountInfo, program_error::ProgramError, ProgramResult};
+
+use crate::{load, load_mut_unchecked, BlockListError, Config, Discriminator, WalletBlock};
+
+
+pub struct UnblockWallet<'a> {
+    pub authority: &'a AccountInfo,
+    pub config: &'a AccountInfo,
+    pub wallet_block: &'a AccountInfo,
+    pub system_program: &'a AccountInfo,
+}
+
+impl<'a> UnblockWallet<'a> {
+    pub fn process(&self) -> ProgramResult {
+        
+        let destination_lamports = self.authority.lamports();
+
+        unsafe {
+            *self.authority.borrow_mut_lamports_unchecked() = destination_lamports
+                .checked_add(self.wallet_block.lamports())
+                .ok_or(ProgramError::ArithmeticOverflow)?;
+            self.wallet_block.close_unchecked();
+        }
+        
+        let config = unsafe { load_mut_unchecked::<Config>(self.config.borrow_mut_data_unchecked())? };
+        config.blocked_wallets_count = config.blocked_wallets_count.checked_sub(1).ok_or(ProgramError::ArithmeticOverflow)?;
+
+        Ok(())
+    }
+}
+
+impl<'a> Discriminator for UnblockWallet<'a> {
+    const DISCRIMINATOR: u8 = 0xF3;
+}
+
+impl<'a> TryFrom<&'a [AccountInfo]> for UnblockWallet<'a> {
+    type Error = BlockListError;
+
+    fn try_from(accounts: &'a [AccountInfo]) -> Result<Self, Self::Error> {
+        let [authority, config, wallet_block, system_program] = accounts else {
+            return Err(BlockListError::NotEnoughAccounts);
+        };
+
+        let cfg = unsafe { load::<Config>(config.borrow_data_unchecked())? };
+        
+        if !config.is_owned_by(&crate::ID) {
+            return Err(BlockListError::InvalidConfigAccount);
+        }
+
+        if !authority.is_signer() || cfg.authority.ne(authority.key()) {
+            return Err(BlockListError::InvalidAuthority);
+        }
+        
+        if !config.is_writable() && !wallet_block.is_writable() {
+            return Err(BlockListError::AccountNotWritable);
+        }
+
+        if unsafe { load::<WalletBlock>(wallet_block.borrow_data_unchecked()).is_err() }{
+            return Err(BlockListError::InvalidAccountData);
+        }
+
+        Ok(Self {
+            authority,
+            config,
+            wallet_block,
+            system_program,
+        })
+    }
+}

+ 42 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/lib.rs

@@ -0,0 +1,42 @@
+#![no_std]
+ 
+use pinocchio::{account_info::AccountInfo, no_allocator, nostd_panic_handler, program_entrypoint, program_error::ProgramError, pubkey::Pubkey, ProgramResult};
+use pinocchio_pubkey::declare_id;
+ 
+program_entrypoint!(process_instruction);
+// Do not allocate memory.
+no_allocator!();
+// Use the no_std panic handler.
+nostd_panic_handler!();
+ 
+pub mod instructions;
+pub use instructions::*;
+pub mod error;
+pub use error::*;
+pub mod state;
+pub use state::*;
+mod token2022_utils;
+
+declare_id!("BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf");
+
+ 
+#[inline(always)]
+fn process_instruction(
+    _program_id: &Pubkey,
+    accounts: &[AccountInfo],
+    instruction_data: &[u8],
+) -> ProgramResult {
+    let [disc, remaining_data @ ..] = instruction_data else {
+        return Err(BlockListError::InvalidInstruction.into());
+    };
+    
+    
+    match *disc {
+        TxHook::DISCRIMINATOR => TxHook::try_from(accounts)?.process(),
+        Init::DISCRIMINATOR => Init::try_from(accounts)?.process(),
+        BlockWallet::DISCRIMINATOR => BlockWallet::try_from(accounts)?.process(),
+        UnblockWallet::DISCRIMINATOR => UnblockWallet::try_from(accounts)?.process(),
+        SetupExtraMetas::DISCRIMINATOR => SetupExtraMetas::try_from(accounts)?.process(remaining_data),
+        _ => Err(ProgramError::InvalidInstructionData),
+    }
+}

+ 24 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/state/config.rs

@@ -0,0 +1,24 @@
+use pinocchio::pubkey::Pubkey;
+
+use super::{Discriminator, Transmutable};
+
+
+#[repr(C)]
+pub struct Config {
+    pub discriminator: u8,
+    pub authority: Pubkey,
+    pub blocked_wallets_count: u64,
+}
+
+impl Config {
+    pub const SEED_PREFIX: &'static [u8] = b"config";
+}
+
+impl Transmutable for Config {
+    const LEN: usize = 1 + 32 + 8;
+}
+
+impl Discriminator for Config {
+    const DISCRIMINATOR: u8 = 0x01;
+}
+

+ 64 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/state/mod.rs

@@ -0,0 +1,64 @@
+pub mod config;
+pub mod wallet_block;
+pub use config::*;
+pub use wallet_block::*;
+
+use crate::BlockListError;
+
+pub trait Transmutable {
+    const LEN: usize;
+}
+
+pub trait Discriminator {
+    const DISCRIMINATOR: u8;
+}
+
+/// Return a reference for an initialized `T` from the given bytes.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load<T: Discriminator + Transmutable>(bytes: &[u8]) -> Result<&T, BlockListError> {
+    load_unchecked(bytes).and_then(|t: &T| {
+        // checks if the data is initialized
+        if bytes[0] == T::DISCRIMINATOR {
+            Ok(t)
+        } else {
+            Err(BlockListError::InvalidAccountData)
+        }
+    })
+}
+
+/// Return a `T` reference from the given bytes.
+///
+/// This function does not check if the data is initialized.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load_unchecked<T: Transmutable>(bytes: &[u8]) -> Result<&T, BlockListError> {
+    if bytes.len() != T::LEN {
+        return Err(BlockListError::InvalidAccountData);
+    }
+    Ok(&*(bytes.as_ptr() as *const T))
+}
+
+/// Return a mutable `T` reference from the given bytes.
+///
+/// This function does not check if the data is initialized.
+///
+/// # Safety
+///
+/// The caller must ensure that `bytes` contains a valid representation of `T`.
+#[inline(always)]
+pub unsafe fn load_mut_unchecked<T: Transmutable>(
+    bytes: &mut [u8],
+) -> Result<&mut T, BlockListError> {
+    if bytes.len() != T::LEN {
+        return Err(BlockListError::InvalidAccountData);
+    }
+    Ok(&mut *(bytes.as_mut_ptr() as *mut T))
+}
+

+ 21 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/state/wallet_block.rs

@@ -0,0 +1,21 @@
+use pinocchio::pubkey::Pubkey;
+
+use super::{Discriminator, Transmutable};
+
+#[repr(C)]
+pub struct WalletBlock {
+    pub discriminator: u8,
+    pub address: Pubkey,
+}
+
+impl WalletBlock {
+    pub const SEED_PREFIX: &'static [u8] = b"wallet_block";
+}
+
+impl Transmutable for WalletBlock {
+    const LEN: usize = 1 + 32;
+}
+
+impl Discriminator for WalletBlock {
+    const DISCRIMINATOR: u8 = 0x02;
+}

+ 57 - 0
tokens/token-2022/transfer-hook/pblock-list/program/src/token2022_utils.rs

@@ -0,0 +1,57 @@
+use pinocchio::{account_info::AccountInfo, pubkey::Pubkey};
+use pinocchio_pubkey::from_str;
+
+pub const TOKEN_2022_PROGRAM_ID: Pubkey = from_str("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb");
+
+pub const EXTRA_METAS_SEED: &[u8] = b"extra-account-metas";
+
+const MINT_LEN: usize = 82;
+const EXTENSIONS_PADDING: usize = 83;
+const EXTENSION_START_OFFSET: usize = 1;
+const EXTENSION_LENGTH_LEN: usize = 2;
+const EXTENSION_TYPE_LEN: usize = 2;
+const TRANSFER_HOOK_EXTENSION_TYPE: u16 = 14;
+const IMMUTABLE_OWNER_EXTENSION_TYPE: u16 = 7;
+const TYPE_BYTE_OFFSET: usize = MINT_LEN + EXTENSIONS_PADDING;
+const EXTENSION_DATA_OFFSET: usize = TYPE_BYTE_OFFSET + EXTENSION_START_OFFSET;
+const MINT_TYPE_BYTE: u8 = 1;
+
+pub fn get_transfer_hook_authority(acc_data_bytes: &[u8]) -> Option<&Pubkey> {
+    let extension_data = get_extension_data_(acc_data_bytes, TRANSFER_HOOK_EXTENSION_TYPE);
+    if let Some(data) = extension_data {
+        return Some( unsafe { &*(data.as_ptr() as *const Pubkey) });
+    }
+    None
+}
+
+fn get_extension_data_(acc_data_bytes: &[u8], extension_type: u16) -> Option<&[u8]> {
+    let ext_bytes = &acc_data_bytes[EXTENSION_DATA_OFFSET..];
+    let mut start = 0;
+    let end = ext_bytes.len();
+    while start < end {
+        let ext_type_idx = start;
+        let ext_len_idx = ext_type_idx + 2;
+        let ext_data_idx = ext_len_idx + EXTENSION_LENGTH_LEN;
+
+        let ext_type = unsafe { &*(ext_bytes[ext_type_idx..].as_ptr() as *const u16) };
+        let ext_len = unsafe { &*(ext_bytes[ext_len_idx..].as_ptr() as *const u16) };
+
+        if *ext_type == extension_type {
+            return Some(&ext_bytes[ext_data_idx..ext_data_idx + *ext_len as usize]);
+        }
+
+        start = start + EXTENSION_TYPE_LEN + EXTENSION_LENGTH_LEN + *ext_len as usize;
+    }
+    None
+}
+
+pub fn has_immutable_owner_extension(acc_data_bytes: &[u8]) -> bool {
+    let extension_data = get_extension_data_(acc_data_bytes, IMMUTABLE_OWNER_EXTENSION_TYPE);
+    extension_data.is_some()
+}
+
+pub fn is_token_2022_mint(mint: &AccountInfo) -> bool {
+    let data = unsafe { mint.borrow_data_unchecked() };
+    let mint_type_byte = data[TYPE_BYTE_OFFSET];
+    data.len() > TYPE_BYTE_OFFSET && mint_type_byte == MINT_TYPE_BYTE && mint.is_owned_by(&TOKEN_2022_PROGRAM_ID)
+}

+ 143 - 0
tokens/token-2022/transfer-hook/pblock-list/readme.md

@@ -0,0 +1,143 @@
+# Block List 
+
+This is a Block List program that implements the Token2022 Transfer-hook execute instruction.
+It allows a centralized authority to defined a block list - a collection of wallets that are blocked.
+Token issuers (transfer-hook extension authorities), can then setup this program as the hook to be used and choose an operation mode (either filter source wallet, or both source and destination).
+
+## Operation Mode
+
+The Block list has different operation modes depending whether the block list is empty or not and the issuer choice. These modes are achieved by building a different `extra-account-metas` account for the token mint (see `setup_extra_metas` bellow). When the list gets the first blocked wallet, the issuer needs to re-set the `extra-account-metas`.
+The modes are the following: 
+- Empty extra metas - default behaviour when config account counter is 0
+- Check Source - default behaviour when config account counter is above 0
+- Check both source and destination - optional behaviour when config account counter is above 0
+
+## Accounts
+
+### Config
+- Defines the block list authority. 
+- Tracks the number of blocked wallets.
+
+### WalletBlock
+- Defines a wallet as blocked
+
+## Instructions
+
+### init
+
+Initializes the global `Config` account with a given authority to control the block list.
+
+### block_wallet
+
+Adds a given wallet address to the blocked wallets. This creates a `WalletBlock` reccord account.
+
+### unblock_wallet
+
+Removes a given wallet address from the blocked wallets. This removes a `WalletBlock` reccord account.
+
+### setup_extra_metas
+
+Sets up the `extra-account-metas` account dependency for the Transfer-Hook extension. Receives an optional bool value to switch operation modes when the blocked wallet counter is non zero.
+Note: once wallets are added to the block list, the issuer needs to call this method again to setup one of the blocking modes.
+
+### tx_hook
+
+The hook that is executed during token transfers.
+
+## Repo contents
+
+### Smart Contract
+
+A pinocchio based Block List smart contract under the [program](program/) folder.
+
+### SDKs
+
+Codama generated rust and ts [SDKs](sdk/). 
+
+### CLI
+
+A rust CLI to interact with the contract.
+
+## Building
+
+First install dependencies:
+```
+pnpm install
+```
+
+To build the smart contract:
+```
+cd program
+cargo build-sbf
+```
+
+To deploy the smart contract:
+```
+solana program deploy --program-id <your_program_keypair.json> target/deploy/block_list.so
+```
+
+To generate the SDKs:
+```
+pnpm run generate-sdks
+```
+
+To build the CLI:
+```
+cd cli
+cargo build
+```
+
+## Setup
+
+### Block List
+
+Initialize the block list and defined the authority:
+```
+target/debug/block-list-cli init
+```
+
+Add a wallet to the block list:
+```
+target/debug/block-list-cli block-wallet <wallet_address>
+```
+
+Remove a wallet from the block list:
+```
+target/debug/block-list-cli unblock-wallet <wallet_address>
+```
+
+
+### Token Mint
+
+Initialize a new token mint:
+```
+spl-token create-token --program-2022 --transfer-hook BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf
+```
+
+Initialize the extra account metas:
+```
+target/debug/block-list-cli setup-extra-metas <wallet_address>
+```
+
+Change the extra account metas to filter both source and destination token account wallets:
+```
+target/debug/block-list-cli setup-extra-metas --check-both-wallets <wallet_address>
+```
+
+## Devnet deployment
+
+Smart contract was deployed to devnet at address `BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf`.
+
+Test transfer with empty block list [here](https://explorer.solana.com/tx/2EnQD5mFZvrR3EAyFamCfxJDS3yAtZQxNVhFtK46PanCgbX6rpvgcQ961ZAs8H3auawJZPaVZMpAxoj3qZK55mHT?cluster=devnet&customUrl=http%3A%2F%2Flocalhost%3A8899).
+
+Test transfer with non empty block list only checking source TA [here](https://explorer.solana.com/tx/4pmx31Lx5mXS7FWUtRjAxdRiwKZKCwJv3Du2qGhbLpQUenBuRxRUbrCaGGVjLjeDtpt4AXHzoNex1ppBsmKWSS7r?cluster=devnet&customUrl=http%3A%2F%2Flocalhost%3A8899).
+
+Test transfer with non empty block list checking both source and destination TAs [here](https://explorer.solana.com/tx/Q5Bk6GjGQ9TJtwS5zjDKp7GiFZK6efmGNCcxjqcmzf1YoZZJVE3rQkkSgSBNo7tst4hjUX6SJMsmEGXQ2NAdBjF?cluster=devnet&customUrl=http%3A%2F%2Flocalhost%3A8899).
+
+Simulated transaction that fails due to destination TA owner being blocked [here](https://explorer.solana.com/tx/inspector?cluster=devnet&signatures=%255B%25221111111111111111111111111111111111111111111111111111111111111111%2522%255D&message=AQAHCgqDBmqk%252FDMT5D9rK85EOwBVSTyxwkSJNDGhjodJl5A8fkyFjtMOw8TOzjiallL3mM8ylDy3Dmf4kPO6zjRCB5meTp%252FmYh4SPAIwzTHZRyKqrqiz%252FskDcCP4xKa5KaJaNQKmMSi6syOX%252BagX8jS6oj8o9glIci7jjFsFtVKThVTSAwZGb%252BUhFzL%252F7K26csOb57yM5bvF9xJrLEObOkAAAAC1QoHXoRYodtouw5cKbwI1AuPk%252BVWEpzwvoAzgkyTWD7vvmloKSuwS0IrUHLk7n0Yfp3DOKmgbjiyFpaYfufnS5xfqCyGJ%252BEpC8iKMH9T%252FdgnUADYw6SCHmevlcTztM6TwOn%252FMbMOP4VGXJKhkykzArfWQd9JuJlU%252B0GDnERJVAQbd9uHudY%252FeGEJdvORszdq2GvxNg7kNJ%252F69%252BSjYoYv8sm6yFK1CM9Gp2RvGj6wbHdQmQ4vCDR59WzHPZ5aOHbIDBAAJA9i4BQAAAAAABAAFAkANAwAJCQEIAgAABQcDBgoMAMqaOwAAAAAJ) (press simulate to see logs).
+
+Simulated transaction that fails due to source TA owner being blocked [here](https://explorer.solana.com/tx/inspector?cluster=devnet&signatures=%255B%25221111111111111111111111111111111111111111111111111111111111111111%2522%255D&message=AQAHCrod5ZzEG06%252BJzr8OnDqiGNK2oQt0Rghykcx3Sw51mE4cZQ%252BDFc%252BtWThZi0XGFuhfdEKDoUp3bkLE8gIYc3DR2N%252BTIWO0w7DxM7OOJqWUveYzzKUPLcOZ%252FiQ87rONEIHmQKmMSi6syOX%252BagX8jS6oj8o9glIci7jjFsFtVKThVTSAwZGb%252BUhFzL%252F7K26csOb57yM5bvF9xJrLEObOkAAAAC1QoHXoRYodtouw5cKbwI1AuPk%252BVWEpzwvoAzgkyTWD7vvmloKSuwS0IrUHLk7n0Yfp3DOKmgbjiyFpaYfufnS8Dp%252FzGzDj%252BFRlySoZMpMwK31kHfSbiZVPtBg5xESVQH3LKeXpXVZHuJ4gl0YZu2j5%252FXT6SUfgp2Znq1tIs7tSwbd9uHudY%252FeGEJdvORszdq2GvxNg7kNJ%252F69%252BSjYoYv8tp02GkX6M1fpsk76QI9ZgGPx%252BxaMNWlOk82JXeuOngcDBAAJA9i4BQAAAAAABAAFAkANAwAJCQEHAgAACAUDBgoMAMqaOwAAAAAJ) (press simulate to see logs).
+
+## DISCLAIMER
+
+THIS CODE IS NOT AUDITED NOR REVIEWED. USE AT YOUR OWN DISCRETION.

+ 12 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/Cargo.toml

@@ -0,0 +1,12 @@
+[package]
+name = "block-list-client"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+name = "block_list_client"
+
+[dependencies]
+solana-program = "2.2.1"
+kaigan = ">=0.2.6"
+borsh = "^0.10"

+ 163 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/config.rs

@@ -0,0 +1,163 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+use solana_program::pubkey::Pubkey;
+
+/// The config PDA account
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct Config {
+    pub discriminator: u8,
+    #[cfg_attr(
+        feature = "serde",
+        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
+    )]
+    pub authority: Pubkey,
+    pub blocked_wallets_count: u64,
+}
+
+impl Config {
+    pub const LEN: usize = 41;
+
+    /// Prefix values used to generate a PDA for this account.
+    ///
+    /// Values are positional and appear in the following order:
+    ///
+    ///   0. `Config::PREFIX`
+    pub const PREFIX: &'static [u8] = "config".as_bytes();
+
+    pub fn create_pda(
+        bump: u8,
+    ) -> Result<solana_program::pubkey::Pubkey, solana_program::pubkey::PubkeyError> {
+        solana_program::pubkey::Pubkey::create_program_address(
+            &["config".as_bytes(), &[bump]],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    pub fn find_pda() -> (solana_program::pubkey::Pubkey, u8) {
+        solana_program::pubkey::Pubkey::find_program_address(
+            &["config".as_bytes()],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    #[inline(always)]
+    pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
+        let mut data = data;
+        Self::deserialize(&mut data)
+    }
+}
+
+impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for Config {
+    type Error = std::io::Error;
+
+    fn try_from(
+        account_info: &solana_program::account_info::AccountInfo<'a>,
+    ) -> Result<Self, Self::Error> {
+        let mut data: &[u8] = &(*account_info.data).borrow();
+        Self::deserialize(&mut data)
+    }
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_config(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::DecodedAccount<Config>, std::io::Error> {
+    let accounts = fetch_all_config(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_config(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::DecodedAccount<Config>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::DecodedAccount<Config>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        let account = accounts[i].as_ref().ok_or(std::io::Error::new(
+            std::io::ErrorKind::Other,
+            format!("Account not found: {}", address),
+        ))?;
+        let data = Config::from_bytes(&account.data)?;
+        decoded_accounts.push(crate::shared::DecodedAccount {
+            address,
+            account: account.clone(),
+            data,
+        });
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_maybe_config(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::MaybeAccount<Config>, std::io::Error> {
+    let accounts = fetch_all_maybe_config(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_maybe_config(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::MaybeAccount<Config>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::MaybeAccount<Config>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        if let Some(account) = accounts[i].as_ref() {
+            let data = Config::from_bytes(&account.data)?;
+            decoded_accounts.push(crate::shared::MaybeAccount::Exists(
+                crate::shared::DecodedAccount {
+                    address,
+                    account: account.clone(),
+                    data,
+                },
+            ));
+        } else {
+            decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address));
+        }
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountDeserialize for Config {
+    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
+        Ok(Self::deserialize(buf)?)
+    }
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountSerialize for Config {}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::Owner for Config {
+    fn owner() -> Pubkey {
+        crate::BLOCK_LIST_ID
+    }
+}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::IdlBuild for Config {}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::Discriminator for Config {
+    const DISCRIMINATOR: [u8; 8] = [0; 8];
+}

+ 155 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/extra_metas.rs

@@ -0,0 +1,155 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+use solana_program::pubkey::Pubkey;
+
+/// The extra metas PDA account
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct ExtraMetas {}
+
+impl ExtraMetas {
+    /// Prefix values used to generate a PDA for this account.
+    ///
+    /// Values are positional and appear in the following order:
+    ///
+    ///   0. `ExtraMetas::PREFIX`
+    ///   1. mint (`Pubkey`)
+    pub const PREFIX: &'static [u8] = "extra-account-metas".as_bytes();
+
+    pub fn create_pda(
+        mint: Pubkey,
+        bump: u8,
+    ) -> Result<solana_program::pubkey::Pubkey, solana_program::pubkey::PubkeyError> {
+        solana_program::pubkey::Pubkey::create_program_address(
+            &["extra-account-metas".as_bytes(), mint.as_ref(), &[bump]],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    pub fn find_pda(mint: &Pubkey) -> (solana_program::pubkey::Pubkey, u8) {
+        solana_program::pubkey::Pubkey::find_program_address(
+            &["extra-account-metas".as_bytes(), mint.as_ref()],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    #[inline(always)]
+    pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
+        let mut data = data;
+        Self::deserialize(&mut data)
+    }
+}
+
+impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for ExtraMetas {
+    type Error = std::io::Error;
+
+    fn try_from(
+        account_info: &solana_program::account_info::AccountInfo<'a>,
+    ) -> Result<Self, Self::Error> {
+        let mut data: &[u8] = &(*account_info.data).borrow();
+        Self::deserialize(&mut data)
+    }
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_extra_metas(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::DecodedAccount<ExtraMetas>, std::io::Error> {
+    let accounts = fetch_all_extra_metas(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_extra_metas(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::DecodedAccount<ExtraMetas>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::DecodedAccount<ExtraMetas>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        let account = accounts[i].as_ref().ok_or(std::io::Error::new(
+            std::io::ErrorKind::Other,
+            format!("Account not found: {}", address),
+        ))?;
+        let data = ExtraMetas::from_bytes(&account.data)?;
+        decoded_accounts.push(crate::shared::DecodedAccount {
+            address,
+            account: account.clone(),
+            data,
+        });
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_maybe_extra_metas(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::MaybeAccount<ExtraMetas>, std::io::Error> {
+    let accounts = fetch_all_maybe_extra_metas(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_maybe_extra_metas(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::MaybeAccount<ExtraMetas>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::MaybeAccount<ExtraMetas>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        if let Some(account) = accounts[i].as_ref() {
+            let data = ExtraMetas::from_bytes(&account.data)?;
+            decoded_accounts.push(crate::shared::MaybeAccount::Exists(
+                crate::shared::DecodedAccount {
+                    address,
+                    account: account.clone(),
+                    data,
+                },
+            ));
+        } else {
+            decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address));
+        }
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountDeserialize for ExtraMetas {
+    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
+        Ok(Self::deserialize(buf)?)
+    }
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountSerialize for ExtraMetas {}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::Owner for ExtraMetas {
+    fn owner() -> Pubkey {
+        crate::BLOCK_LIST_ID
+    }
+}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::IdlBuild for ExtraMetas {}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::Discriminator for ExtraMetas {
+    const DISCRIMINATOR: [u8; 8] = [0; 8];
+}

+ 14 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/mod.rs

@@ -0,0 +1,14 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+pub(crate) mod r#config;
+pub(crate) mod r#extra_metas;
+pub(crate) mod r#wallet_block;
+
+pub use self::r#config::*;
+pub use self::r#extra_metas::*;
+pub use self::r#wallet_block::*;

+ 163 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/wallet_block.rs

@@ -0,0 +1,163 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+use solana_program::pubkey::Pubkey;
+
+/// The config PDA account
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct WalletBlock {
+    #[cfg_attr(
+        feature = "serde",
+        serde(with = "serde_with::As::<serde_with::DisplayFromStr>")
+    )]
+    pub authority: Pubkey,
+}
+
+impl WalletBlock {
+    pub const LEN: usize = 33;
+
+    /// Prefix values used to generate a PDA for this account.
+    ///
+    /// Values are positional and appear in the following order:
+    ///
+    ///   0. `WalletBlock::PREFIX`
+    ///   1. wallet (`Pubkey`)
+    pub const PREFIX: &'static [u8] = "wallet_block".as_bytes();
+
+    pub fn create_pda(
+        wallet: Pubkey,
+        bump: u8,
+    ) -> Result<solana_program::pubkey::Pubkey, solana_program::pubkey::PubkeyError> {
+        solana_program::pubkey::Pubkey::create_program_address(
+            &["wallet_block".as_bytes(), wallet.as_ref(), &[bump]],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    pub fn find_pda(wallet: &Pubkey) -> (solana_program::pubkey::Pubkey, u8) {
+        solana_program::pubkey::Pubkey::find_program_address(
+            &["wallet_block".as_bytes(), wallet.as_ref()],
+            &crate::BLOCK_LIST_ID,
+        )
+    }
+
+    #[inline(always)]
+    pub fn from_bytes(data: &[u8]) -> Result<Self, std::io::Error> {
+        let mut data = data;
+        Self::deserialize(&mut data)
+    }
+}
+
+impl<'a> TryFrom<&solana_program::account_info::AccountInfo<'a>> for WalletBlock {
+    type Error = std::io::Error;
+
+    fn try_from(
+        account_info: &solana_program::account_info::AccountInfo<'a>,
+    ) -> Result<Self, Self::Error> {
+        let mut data: &[u8] = &(*account_info.data).borrow();
+        Self::deserialize(&mut data)
+    }
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_wallet_block(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::DecodedAccount<WalletBlock>, std::io::Error> {
+    let accounts = fetch_all_wallet_block(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_wallet_block(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::DecodedAccount<WalletBlock>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::DecodedAccount<WalletBlock>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        let account = accounts[i].as_ref().ok_or(std::io::Error::new(
+            std::io::ErrorKind::Other,
+            format!("Account not found: {}", address),
+        ))?;
+        let data = WalletBlock::from_bytes(&account.data)?;
+        decoded_accounts.push(crate::shared::DecodedAccount {
+            address,
+            account: account.clone(),
+            data,
+        });
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_maybe_wallet_block(
+    rpc: &solana_client::rpc_client::RpcClient,
+    address: &solana_program::pubkey::Pubkey,
+) -> Result<crate::shared::MaybeAccount<WalletBlock>, std::io::Error> {
+    let accounts = fetch_all_maybe_wallet_block(rpc, &[*address])?;
+    Ok(accounts[0].clone())
+}
+
+#[cfg(feature = "fetch")]
+pub fn fetch_all_maybe_wallet_block(
+    rpc: &solana_client::rpc_client::RpcClient,
+    addresses: &[solana_program::pubkey::Pubkey],
+) -> Result<Vec<crate::shared::MaybeAccount<WalletBlock>>, std::io::Error> {
+    let accounts = rpc
+        .get_multiple_accounts(addresses)
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
+    let mut decoded_accounts: Vec<crate::shared::MaybeAccount<WalletBlock>> = Vec::new();
+    for i in 0..addresses.len() {
+        let address = addresses[i];
+        if let Some(account) = accounts[i].as_ref() {
+            let data = WalletBlock::from_bytes(&account.data)?;
+            decoded_accounts.push(crate::shared::MaybeAccount::Exists(
+                crate::shared::DecodedAccount {
+                    address,
+                    account: account.clone(),
+                    data,
+                },
+            ));
+        } else {
+            decoded_accounts.push(crate::shared::MaybeAccount::NotFound(address));
+        }
+    }
+    Ok(decoded_accounts)
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountDeserialize for WalletBlock {
+    fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result<Self> {
+        Ok(Self::deserialize(buf)?)
+    }
+}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::AccountSerialize for WalletBlock {}
+
+#[cfg(feature = "anchor")]
+impl anchor_lang::Owner for WalletBlock {
+    fn owner() -> Pubkey {
+        crate::BLOCK_LIST_ID
+    }
+}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::IdlBuild for WalletBlock {}
+
+#[cfg(feature = "anchor-idl-build")]
+impl anchor_lang::Discriminator for WalletBlock {
+    const DISCRIMINATOR: [u8; 8] = [0; 8];
+}

+ 6 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/errors/mod.rs

@@ -0,0 +1,6 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!

+ 447 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/block_wallet.rs

@@ -0,0 +1,447 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+
+/// Accounts.
+#[derive(Debug)]
+pub struct BlockWallet {
+    pub authority: solana_program::pubkey::Pubkey,
+
+    pub config: solana_program::pubkey::Pubkey,
+
+    pub wallet: solana_program::pubkey::Pubkey,
+
+    pub wallet_block: solana_program::pubkey::Pubkey,
+
+    pub system_program: solana_program::pubkey::Pubkey,
+}
+
+impl BlockWallet {
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        self.instruction_with_remaining_accounts(&[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn instruction_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[solana_program::instruction::AccountMeta],
+    ) -> solana_program::instruction::Instruction {
+        let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.authority,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.config,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.wallet,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.wallet_block,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.system_program,
+            false,
+        ));
+        accounts.extend_from_slice(remaining_accounts);
+        let data = borsh::to_vec(&BlockWalletInstructionData::new()).unwrap();
+
+        solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        }
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct BlockWalletInstructionData {
+    discriminator: u8,
+}
+
+impl BlockWalletInstructionData {
+    pub fn new() -> Self {
+        Self { discriminator: 242 }
+    }
+}
+
+impl Default for BlockWalletInstructionData {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// Instruction builder for `BlockWallet`.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[]` wallet
+///   3. `[writable]` wallet_block
+///   4. `[optional]` system_program (default to `11111111111111111111111111111111`)
+#[derive(Clone, Debug, Default)]
+pub struct BlockWalletBuilder {
+    authority: Option<solana_program::pubkey::Pubkey>,
+    config: Option<solana_program::pubkey::Pubkey>,
+    wallet: Option<solana_program::pubkey::Pubkey>,
+    wallet_block: Option<solana_program::pubkey::Pubkey>,
+    system_program: Option<solana_program::pubkey::Pubkey>,
+    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
+}
+
+impl BlockWalletBuilder {
+    pub fn new() -> Self {
+        Self::default()
+    }
+    #[inline(always)]
+    pub fn authority(&mut self, authority: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet(&mut self, wallet: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.wallet = Some(wallet);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet_block(&mut self, wallet_block: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.wallet_block = Some(wallet_block);
+        self
+    }
+    /// `[optional account, default to '11111111111111111111111111111111']`
+    #[inline(always)]
+    pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: solana_program::instruction::AccountMeta,
+    ) -> &mut Self {
+        self.__remaining_accounts.push(account);
+        self
+    }
+    /// Add additional accounts to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[solana_program::instruction::AccountMeta],
+    ) -> &mut Self {
+        self.__remaining_accounts.extend_from_slice(accounts);
+        self
+    }
+    #[allow(clippy::clone_on_copy)]
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        let accounts = BlockWallet {
+            authority: self.authority.expect("authority is not set"),
+            config: self.config.expect("config is not set"),
+            wallet: self.wallet.expect("wallet is not set"),
+            wallet_block: self.wallet_block.expect("wallet_block is not set"),
+            system_program: self
+                .system_program
+                .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")),
+        };
+
+        accounts.instruction_with_remaining_accounts(&self.__remaining_accounts)
+    }
+}
+
+/// `block_wallet` CPI accounts.
+pub struct BlockWalletCpiAccounts<'a, 'b> {
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+/// `block_wallet` CPI instruction.
+pub struct BlockWalletCpi<'a, 'b> {
+    /// The program to invoke.
+    pub __program: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+impl<'a, 'b> BlockWalletCpi<'a, 'b> {
+    pub fn new(
+        program: &'b solana_program::account_info::AccountInfo<'a>,
+        accounts: BlockWalletCpiAccounts<'a, 'b>,
+    ) -> Self {
+        Self {
+            __program: program,
+            authority: accounts.authority,
+            config: accounts.config,
+            wallet: accounts.wallet,
+            wallet_block: accounts.wallet_block,
+            system_program: accounts.system_program,
+        }
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], &[])
+    }
+    #[inline(always)]
+    pub fn invoke_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
+    }
+    #[inline(always)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed_with_remaining_accounts(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.authority.key,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.config.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.wallet.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.wallet_block.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.system_program.key,
+            false,
+        ));
+        remaining_accounts.iter().for_each(|remaining_account| {
+            accounts.push(solana_program::instruction::AccountMeta {
+                pubkey: *remaining_account.0.key,
+                is_signer: remaining_account.1,
+                is_writable: remaining_account.2,
+            })
+        });
+        let data = borsh::to_vec(&BlockWalletInstructionData::new()).unwrap();
+
+        let instruction = solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        };
+        let mut account_infos = Vec::with_capacity(6 + remaining_accounts.len());
+        account_infos.push(self.__program.clone());
+        account_infos.push(self.authority.clone());
+        account_infos.push(self.config.clone());
+        account_infos.push(self.wallet.clone());
+        account_infos.push(self.wallet_block.clone());
+        account_infos.push(self.system_program.clone());
+        remaining_accounts
+            .iter()
+            .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
+
+        if signers_seeds.is_empty() {
+            solana_program::program::invoke(&instruction, &account_infos)
+        } else {
+            solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
+        }
+    }
+}
+
+/// Instruction builder for `BlockWallet` via CPI.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[]` wallet
+///   3. `[writable]` wallet_block
+///   4. `[]` system_program
+#[derive(Clone, Debug)]
+pub struct BlockWalletCpiBuilder<'a, 'b> {
+    instruction: Box<BlockWalletCpiBuilderInstruction<'a, 'b>>,
+}
+
+impl<'a, 'b> BlockWalletCpiBuilder<'a, 'b> {
+    pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
+        let instruction = Box::new(BlockWalletCpiBuilderInstruction {
+            __program: program,
+            authority: None,
+            config: None,
+            wallet: None,
+            wallet_block: None,
+            system_program: None,
+            __remaining_accounts: Vec::new(),
+        });
+        Self { instruction }
+    }
+    #[inline(always)]
+    pub fn authority(
+        &mut self,
+        authority: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(
+        &mut self,
+        config: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet(
+        &mut self,
+        wallet: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.wallet = Some(wallet);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet_block(
+        &mut self,
+        wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.wallet_block = Some(wallet_block);
+        self
+    }
+    #[inline(always)]
+    pub fn system_program(
+        &mut self,
+        system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: &'b solana_program::account_info::AccountInfo<'a>,
+        is_writable: bool,
+        is_signer: bool,
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .push((account, is_writable, is_signer));
+        self
+    }
+    /// Add additional accounts to the instruction.
+    ///
+    /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not,
+    /// and a `bool` indicating whether the account is a signer or not.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .extend_from_slice(accounts);
+        self
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed(&[])
+    }
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let instruction = BlockWalletCpi {
+            __program: self.instruction.__program,
+
+            authority: self.instruction.authority.expect("authority is not set"),
+
+            config: self.instruction.config.expect("config is not set"),
+
+            wallet: self.instruction.wallet.expect("wallet is not set"),
+
+            wallet_block: self
+                .instruction
+                .wallet_block
+                .expect("wallet_block is not set"),
+
+            system_program: self
+                .instruction
+                .system_program
+                .expect("system_program is not set"),
+        };
+        instruction.invoke_signed_with_remaining_accounts(
+            signers_seeds,
+            &self.instruction.__remaining_accounts,
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
+struct BlockWalletCpiBuilderInstruction<'a, 'b> {
+    __program: &'b solana_program::account_info::AccountInfo<'a>,
+    authority: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    config: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    wallet: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    wallet_block: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
+    __remaining_accounts: Vec<(
+        &'b solana_program::account_info::AccountInfo<'a>,
+        bool,
+        bool,
+    )>,
+}

+ 370 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/init.rs

@@ -0,0 +1,370 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+
+/// Accounts.
+#[derive(Debug)]
+pub struct Init {
+    pub authority: solana_program::pubkey::Pubkey,
+
+    pub config: solana_program::pubkey::Pubkey,
+
+    pub system_program: solana_program::pubkey::Pubkey,
+}
+
+impl Init {
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        self.instruction_with_remaining_accounts(&[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn instruction_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[solana_program::instruction::AccountMeta],
+    ) -> solana_program::instruction::Instruction {
+        let mut accounts = Vec::with_capacity(3 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.authority,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.config,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.system_program,
+            false,
+        ));
+        accounts.extend_from_slice(remaining_accounts);
+        let data = borsh::to_vec(&InitInstructionData::new()).unwrap();
+
+        solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        }
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct InitInstructionData {
+    discriminator: u8,
+}
+
+impl InitInstructionData {
+    pub fn new() -> Self {
+        Self { discriminator: 241 }
+    }
+}
+
+impl Default for InitInstructionData {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// Instruction builder for `Init`.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[optional]` system_program (default to `11111111111111111111111111111111`)
+#[derive(Clone, Debug, Default)]
+pub struct InitBuilder {
+    authority: Option<solana_program::pubkey::Pubkey>,
+    config: Option<solana_program::pubkey::Pubkey>,
+    system_program: Option<solana_program::pubkey::Pubkey>,
+    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
+}
+
+impl InitBuilder {
+    pub fn new() -> Self {
+        Self::default()
+    }
+    #[inline(always)]
+    pub fn authority(&mut self, authority: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.config = Some(config);
+        self
+    }
+    /// `[optional account, default to '11111111111111111111111111111111']`
+    #[inline(always)]
+    pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: solana_program::instruction::AccountMeta,
+    ) -> &mut Self {
+        self.__remaining_accounts.push(account);
+        self
+    }
+    /// Add additional accounts to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[solana_program::instruction::AccountMeta],
+    ) -> &mut Self {
+        self.__remaining_accounts.extend_from_slice(accounts);
+        self
+    }
+    #[allow(clippy::clone_on_copy)]
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        let accounts = Init {
+            authority: self.authority.expect("authority is not set"),
+            config: self.config.expect("config is not set"),
+            system_program: self
+                .system_program
+                .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")),
+        };
+
+        accounts.instruction_with_remaining_accounts(&self.__remaining_accounts)
+    }
+}
+
+/// `init` CPI accounts.
+pub struct InitCpiAccounts<'a, 'b> {
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+/// `init` CPI instruction.
+pub struct InitCpi<'a, 'b> {
+    /// The program to invoke.
+    pub __program: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+impl<'a, 'b> InitCpi<'a, 'b> {
+    pub fn new(
+        program: &'b solana_program::account_info::AccountInfo<'a>,
+        accounts: InitCpiAccounts<'a, 'b>,
+    ) -> Self {
+        Self {
+            __program: program,
+            authority: accounts.authority,
+            config: accounts.config,
+            system_program: accounts.system_program,
+        }
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], &[])
+    }
+    #[inline(always)]
+    pub fn invoke_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
+    }
+    #[inline(always)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed_with_remaining_accounts(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let mut accounts = Vec::with_capacity(3 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.authority.key,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.config.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.system_program.key,
+            false,
+        ));
+        remaining_accounts.iter().for_each(|remaining_account| {
+            accounts.push(solana_program::instruction::AccountMeta {
+                pubkey: *remaining_account.0.key,
+                is_signer: remaining_account.1,
+                is_writable: remaining_account.2,
+            })
+        });
+        let data = borsh::to_vec(&InitInstructionData::new()).unwrap();
+
+        let instruction = solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        };
+        let mut account_infos = Vec::with_capacity(4 + remaining_accounts.len());
+        account_infos.push(self.__program.clone());
+        account_infos.push(self.authority.clone());
+        account_infos.push(self.config.clone());
+        account_infos.push(self.system_program.clone());
+        remaining_accounts
+            .iter()
+            .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
+
+        if signers_seeds.is_empty() {
+            solana_program::program::invoke(&instruction, &account_infos)
+        } else {
+            solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
+        }
+    }
+}
+
+/// Instruction builder for `Init` via CPI.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[]` system_program
+#[derive(Clone, Debug)]
+pub struct InitCpiBuilder<'a, 'b> {
+    instruction: Box<InitCpiBuilderInstruction<'a, 'b>>,
+}
+
+impl<'a, 'b> InitCpiBuilder<'a, 'b> {
+    pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
+        let instruction = Box::new(InitCpiBuilderInstruction {
+            __program: program,
+            authority: None,
+            config: None,
+            system_program: None,
+            __remaining_accounts: Vec::new(),
+        });
+        Self { instruction }
+    }
+    #[inline(always)]
+    pub fn authority(
+        &mut self,
+        authority: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(
+        &mut self,
+        config: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn system_program(
+        &mut self,
+        system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: &'b solana_program::account_info::AccountInfo<'a>,
+        is_writable: bool,
+        is_signer: bool,
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .push((account, is_writable, is_signer));
+        self
+    }
+    /// Add additional accounts to the instruction.
+    ///
+    /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not,
+    /// and a `bool` indicating whether the account is a signer or not.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .extend_from_slice(accounts);
+        self
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed(&[])
+    }
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let instruction = InitCpi {
+            __program: self.instruction.__program,
+
+            authority: self.instruction.authority.expect("authority is not set"),
+
+            config: self.instruction.config.expect("config is not set"),
+
+            system_program: self
+                .instruction
+                .system_program
+                .expect("system_program is not set"),
+        };
+        instruction.invoke_signed_with_remaining_accounts(
+            signers_seeds,
+            &self.instruction.__remaining_accounts,
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
+struct InitCpiBuilderInstruction<'a, 'b> {
+    __program: &'b solana_program::account_info::AccountInfo<'a>,
+    authority: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    config: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
+    __remaining_accounts: Vec<(
+        &'b solana_program::account_info::AccountInfo<'a>,
+        bool,
+        bool,
+    )>,
+}

+ 16 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/mod.rs

@@ -0,0 +1,16 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+pub(crate) mod r#block_wallet;
+pub(crate) mod r#init;
+pub(crate) mod r#setup_extra_metas;
+pub(crate) mod r#unblock_wallet;
+
+pub use self::r#block_wallet::*;
+pub use self::r#init::*;
+pub use self::r#setup_extra_metas::*;
+pub use self::r#unblock_wallet::*;

+ 483 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/setup_extra_metas.rs

@@ -0,0 +1,483 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+
+/// Accounts.
+#[derive(Debug)]
+pub struct SetupExtraMetas {
+    pub authority: solana_program::pubkey::Pubkey,
+
+    pub config: solana_program::pubkey::Pubkey,
+
+    pub mint: solana_program::pubkey::Pubkey,
+
+    pub extra_metas: solana_program::pubkey::Pubkey,
+
+    pub system_program: solana_program::pubkey::Pubkey,
+}
+
+impl SetupExtraMetas {
+    pub fn instruction(
+        &self,
+        args: SetupExtraMetasInstructionArgs,
+    ) -> solana_program::instruction::Instruction {
+        self.instruction_with_remaining_accounts(args, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn instruction_with_remaining_accounts(
+        &self,
+        args: SetupExtraMetasInstructionArgs,
+        remaining_accounts: &[solana_program::instruction::AccountMeta],
+    ) -> solana_program::instruction::Instruction {
+        let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.authority,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.config,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.mint, false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.extra_metas,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.system_program,
+            false,
+        ));
+        accounts.extend_from_slice(remaining_accounts);
+        let mut data = borsh::to_vec(&SetupExtraMetasInstructionData::new()).unwrap();
+        let mut args = borsh::to_vec(&args).unwrap();
+        data.append(&mut args);
+
+        solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        }
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct SetupExtraMetasInstructionData {
+    discriminator: u8,
+}
+
+impl SetupExtraMetasInstructionData {
+    pub fn new() -> Self {
+        Self { discriminator: 106 }
+    }
+}
+
+impl Default for SetupExtraMetasInstructionData {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct SetupExtraMetasInstructionArgs {
+    pub check_both_wallets: bool,
+}
+
+/// Instruction builder for `SetupExtraMetas`.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[]` config
+///   2. `[]` mint
+///   3. `[writable]` extra_metas
+///   4. `[optional]` system_program (default to `11111111111111111111111111111111`)
+#[derive(Clone, Debug, Default)]
+pub struct SetupExtraMetasBuilder {
+    authority: Option<solana_program::pubkey::Pubkey>,
+    config: Option<solana_program::pubkey::Pubkey>,
+    mint: Option<solana_program::pubkey::Pubkey>,
+    extra_metas: Option<solana_program::pubkey::Pubkey>,
+    system_program: Option<solana_program::pubkey::Pubkey>,
+    check_both_wallets: Option<bool>,
+    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
+}
+
+impl SetupExtraMetasBuilder {
+    pub fn new() -> Self {
+        Self::default()
+    }
+    #[inline(always)]
+    pub fn authority(&mut self, authority: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn mint(&mut self, mint: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.mint = Some(mint);
+        self
+    }
+    #[inline(always)]
+    pub fn extra_metas(&mut self, extra_metas: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.extra_metas = Some(extra_metas);
+        self
+    }
+    /// `[optional account, default to '11111111111111111111111111111111']`
+    #[inline(always)]
+    pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.system_program = Some(system_program);
+        self
+    }
+    /// `[optional argument, defaults to 'false']`
+    #[inline(always)]
+    pub fn check_both_wallets(&mut self, check_both_wallets: bool) -> &mut Self {
+        self.check_both_wallets = Some(check_both_wallets);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: solana_program::instruction::AccountMeta,
+    ) -> &mut Self {
+        self.__remaining_accounts.push(account);
+        self
+    }
+    /// Add additional accounts to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[solana_program::instruction::AccountMeta],
+    ) -> &mut Self {
+        self.__remaining_accounts.extend_from_slice(accounts);
+        self
+    }
+    #[allow(clippy::clone_on_copy)]
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        let accounts = SetupExtraMetas {
+            authority: self.authority.expect("authority is not set"),
+            config: self.config.expect("config is not set"),
+            mint: self.mint.expect("mint is not set"),
+            extra_metas: self.extra_metas.expect("extra_metas is not set"),
+            system_program: self
+                .system_program
+                .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")),
+        };
+        let args = SetupExtraMetasInstructionArgs {
+            check_both_wallets: self.check_both_wallets.clone().unwrap_or(false),
+        };
+
+        accounts.instruction_with_remaining_accounts(args, &self.__remaining_accounts)
+    }
+}
+
+/// `setup_extra_metas` CPI accounts.
+pub struct SetupExtraMetasCpiAccounts<'a, 'b> {
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub mint: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub extra_metas: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+/// `setup_extra_metas` CPI instruction.
+pub struct SetupExtraMetasCpi<'a, 'b> {
+    /// The program to invoke.
+    pub __program: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub mint: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub extra_metas: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    /// The arguments for the instruction.
+    pub __args: SetupExtraMetasInstructionArgs,
+}
+
+impl<'a, 'b> SetupExtraMetasCpi<'a, 'b> {
+    pub fn new(
+        program: &'b solana_program::account_info::AccountInfo<'a>,
+        accounts: SetupExtraMetasCpiAccounts<'a, 'b>,
+        args: SetupExtraMetasInstructionArgs,
+    ) -> Self {
+        Self {
+            __program: program,
+            authority: accounts.authority,
+            config: accounts.config,
+            mint: accounts.mint,
+            extra_metas: accounts.extra_metas,
+            system_program: accounts.system_program,
+            __args: args,
+        }
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], &[])
+    }
+    #[inline(always)]
+    pub fn invoke_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
+    }
+    #[inline(always)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed_with_remaining_accounts(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let mut accounts = Vec::with_capacity(5 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.authority.key,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.config.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.mint.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.extra_metas.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.system_program.key,
+            false,
+        ));
+        remaining_accounts.iter().for_each(|remaining_account| {
+            accounts.push(solana_program::instruction::AccountMeta {
+                pubkey: *remaining_account.0.key,
+                is_signer: remaining_account.1,
+                is_writable: remaining_account.2,
+            })
+        });
+        let mut data = borsh::to_vec(&SetupExtraMetasInstructionData::new()).unwrap();
+        let mut args = borsh::to_vec(&self.__args).unwrap();
+        data.append(&mut args);
+
+        let instruction = solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        };
+        let mut account_infos = Vec::with_capacity(6 + remaining_accounts.len());
+        account_infos.push(self.__program.clone());
+        account_infos.push(self.authority.clone());
+        account_infos.push(self.config.clone());
+        account_infos.push(self.mint.clone());
+        account_infos.push(self.extra_metas.clone());
+        account_infos.push(self.system_program.clone());
+        remaining_accounts
+            .iter()
+            .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
+
+        if signers_seeds.is_empty() {
+            solana_program::program::invoke(&instruction, &account_infos)
+        } else {
+            solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
+        }
+    }
+}
+
+/// Instruction builder for `SetupExtraMetas` via CPI.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[]` config
+///   2. `[]` mint
+///   3. `[writable]` extra_metas
+///   4. `[]` system_program
+#[derive(Clone, Debug)]
+pub struct SetupExtraMetasCpiBuilder<'a, 'b> {
+    instruction: Box<SetupExtraMetasCpiBuilderInstruction<'a, 'b>>,
+}
+
+impl<'a, 'b> SetupExtraMetasCpiBuilder<'a, 'b> {
+    pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
+        let instruction = Box::new(SetupExtraMetasCpiBuilderInstruction {
+            __program: program,
+            authority: None,
+            config: None,
+            mint: None,
+            extra_metas: None,
+            system_program: None,
+            check_both_wallets: None,
+            __remaining_accounts: Vec::new(),
+        });
+        Self { instruction }
+    }
+    #[inline(always)]
+    pub fn authority(
+        &mut self,
+        authority: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(
+        &mut self,
+        config: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn mint(&mut self, mint: &'b solana_program::account_info::AccountInfo<'a>) -> &mut Self {
+        self.instruction.mint = Some(mint);
+        self
+    }
+    #[inline(always)]
+    pub fn extra_metas(
+        &mut self,
+        extra_metas: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.extra_metas = Some(extra_metas);
+        self
+    }
+    #[inline(always)]
+    pub fn system_program(
+        &mut self,
+        system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.system_program = Some(system_program);
+        self
+    }
+    /// `[optional argument, defaults to 'false']`
+    #[inline(always)]
+    pub fn check_both_wallets(&mut self, check_both_wallets: bool) -> &mut Self {
+        self.instruction.check_both_wallets = Some(check_both_wallets);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: &'b solana_program::account_info::AccountInfo<'a>,
+        is_writable: bool,
+        is_signer: bool,
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .push((account, is_writable, is_signer));
+        self
+    }
+    /// Add additional accounts to the instruction.
+    ///
+    /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not,
+    /// and a `bool` indicating whether the account is a signer or not.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .extend_from_slice(accounts);
+        self
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed(&[])
+    }
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let args = SetupExtraMetasInstructionArgs {
+            check_both_wallets: self.instruction.check_both_wallets.clone().unwrap_or(false),
+        };
+        let instruction = SetupExtraMetasCpi {
+            __program: self.instruction.__program,
+
+            authority: self.instruction.authority.expect("authority is not set"),
+
+            config: self.instruction.config.expect("config is not set"),
+
+            mint: self.instruction.mint.expect("mint is not set"),
+
+            extra_metas: self
+                .instruction
+                .extra_metas
+                .expect("extra_metas is not set"),
+
+            system_program: self
+                .instruction
+                .system_program
+                .expect("system_program is not set"),
+            __args: args,
+        };
+        instruction.invoke_signed_with_remaining_accounts(
+            signers_seeds,
+            &self.instruction.__remaining_accounts,
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
+struct SetupExtraMetasCpiBuilderInstruction<'a, 'b> {
+    __program: &'b solana_program::account_info::AccountInfo<'a>,
+    authority: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    config: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    mint: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    extra_metas: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    check_both_wallets: Option<bool>,
+    /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
+    __remaining_accounts: Vec<(
+        &'b solana_program::account_info::AccountInfo<'a>,
+        bool,
+        bool,
+    )>,
+}

+ 410 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/unblock_wallet.rs

@@ -0,0 +1,410 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use borsh::BorshDeserialize;
+use borsh::BorshSerialize;
+
+/// Accounts.
+#[derive(Debug)]
+pub struct UnblockWallet {
+    pub authority: solana_program::pubkey::Pubkey,
+
+    pub config: solana_program::pubkey::Pubkey,
+
+    pub wallet_block: solana_program::pubkey::Pubkey,
+
+    pub system_program: solana_program::pubkey::Pubkey,
+}
+
+impl UnblockWallet {
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        self.instruction_with_remaining_accounts(&[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn instruction_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[solana_program::instruction::AccountMeta],
+    ) -> solana_program::instruction::Instruction {
+        let mut accounts = Vec::with_capacity(4 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.authority,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.config,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            self.wallet_block,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            self.system_program,
+            false,
+        ));
+        accounts.extend_from_slice(remaining_accounts);
+        let data = borsh::to_vec(&UnblockWalletInstructionData::new()).unwrap();
+
+        solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        }
+    }
+}
+
+#[derive(BorshSerialize, BorshDeserialize, Clone, Debug, Eq, PartialEq)]
+#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
+pub struct UnblockWalletInstructionData {
+    discriminator: u8,
+}
+
+impl UnblockWalletInstructionData {
+    pub fn new() -> Self {
+        Self { discriminator: 243 }
+    }
+}
+
+impl Default for UnblockWalletInstructionData {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+/// Instruction builder for `UnblockWallet`.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[writable]` wallet_block
+///   3. `[optional]` system_program (default to `11111111111111111111111111111111`)
+#[derive(Clone, Debug, Default)]
+pub struct UnblockWalletBuilder {
+    authority: Option<solana_program::pubkey::Pubkey>,
+    config: Option<solana_program::pubkey::Pubkey>,
+    wallet_block: Option<solana_program::pubkey::Pubkey>,
+    system_program: Option<solana_program::pubkey::Pubkey>,
+    __remaining_accounts: Vec<solana_program::instruction::AccountMeta>,
+}
+
+impl UnblockWalletBuilder {
+    pub fn new() -> Self {
+        Self::default()
+    }
+    #[inline(always)]
+    pub fn authority(&mut self, authority: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(&mut self, config: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet_block(&mut self, wallet_block: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.wallet_block = Some(wallet_block);
+        self
+    }
+    /// `[optional account, default to '11111111111111111111111111111111']`
+    #[inline(always)]
+    pub fn system_program(&mut self, system_program: solana_program::pubkey::Pubkey) -> &mut Self {
+        self.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: solana_program::instruction::AccountMeta,
+    ) -> &mut Self {
+        self.__remaining_accounts.push(account);
+        self
+    }
+    /// Add additional accounts to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[solana_program::instruction::AccountMeta],
+    ) -> &mut Self {
+        self.__remaining_accounts.extend_from_slice(accounts);
+        self
+    }
+    #[allow(clippy::clone_on_copy)]
+    pub fn instruction(&self) -> solana_program::instruction::Instruction {
+        let accounts = UnblockWallet {
+            authority: self.authority.expect("authority is not set"),
+            config: self.config.expect("config is not set"),
+            wallet_block: self.wallet_block.expect("wallet_block is not set"),
+            system_program: self
+                .system_program
+                .unwrap_or(solana_program::pubkey!("11111111111111111111111111111111")),
+        };
+
+        accounts.instruction_with_remaining_accounts(&self.__remaining_accounts)
+    }
+}
+
+/// `unblock_wallet` CPI accounts.
+pub struct UnblockWalletCpiAccounts<'a, 'b> {
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+/// `unblock_wallet` CPI instruction.
+pub struct UnblockWalletCpi<'a, 'b> {
+    /// The program to invoke.
+    pub __program: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub authority: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub config: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+
+    pub system_program: &'b solana_program::account_info::AccountInfo<'a>,
+}
+
+impl<'a, 'b> UnblockWalletCpi<'a, 'b> {
+    pub fn new(
+        program: &'b solana_program::account_info::AccountInfo<'a>,
+        accounts: UnblockWalletCpiAccounts<'a, 'b>,
+    ) -> Self {
+        Self {
+            __program: program,
+            authority: accounts.authority,
+            config: accounts.config,
+            wallet_block: accounts.wallet_block,
+            system_program: accounts.system_program,
+        }
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], &[])
+    }
+    #[inline(always)]
+    pub fn invoke_with_remaining_accounts(
+        &self,
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(&[], remaining_accounts)
+    }
+    #[inline(always)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed_with_remaining_accounts(signers_seeds, &[])
+    }
+    #[allow(clippy::arithmetic_side_effects)]
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed_with_remaining_accounts(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+        remaining_accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let mut accounts = Vec::with_capacity(4 + remaining_accounts.len());
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.authority.key,
+            true,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.config.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new(
+            *self.wallet_block.key,
+            false,
+        ));
+        accounts.push(solana_program::instruction::AccountMeta::new_readonly(
+            *self.system_program.key,
+            false,
+        ));
+        remaining_accounts.iter().for_each(|remaining_account| {
+            accounts.push(solana_program::instruction::AccountMeta {
+                pubkey: *remaining_account.0.key,
+                is_signer: remaining_account.1,
+                is_writable: remaining_account.2,
+            })
+        });
+        let data = borsh::to_vec(&UnblockWalletInstructionData::new()).unwrap();
+
+        let instruction = solana_program::instruction::Instruction {
+            program_id: crate::BLOCK_LIST_ID,
+            accounts,
+            data,
+        };
+        let mut account_infos = Vec::with_capacity(5 + remaining_accounts.len());
+        account_infos.push(self.__program.clone());
+        account_infos.push(self.authority.clone());
+        account_infos.push(self.config.clone());
+        account_infos.push(self.wallet_block.clone());
+        account_infos.push(self.system_program.clone());
+        remaining_accounts
+            .iter()
+            .for_each(|remaining_account| account_infos.push(remaining_account.0.clone()));
+
+        if signers_seeds.is_empty() {
+            solana_program::program::invoke(&instruction, &account_infos)
+        } else {
+            solana_program::program::invoke_signed(&instruction, &account_infos, signers_seeds)
+        }
+    }
+}
+
+/// Instruction builder for `UnblockWallet` via CPI.
+///
+/// ### Accounts:
+///
+///   0. `[writable, signer]` authority
+///   1. `[writable]` config
+///   2. `[writable]` wallet_block
+///   3. `[]` system_program
+#[derive(Clone, Debug)]
+pub struct UnblockWalletCpiBuilder<'a, 'b> {
+    instruction: Box<UnblockWalletCpiBuilderInstruction<'a, 'b>>,
+}
+
+impl<'a, 'b> UnblockWalletCpiBuilder<'a, 'b> {
+    pub fn new(program: &'b solana_program::account_info::AccountInfo<'a>) -> Self {
+        let instruction = Box::new(UnblockWalletCpiBuilderInstruction {
+            __program: program,
+            authority: None,
+            config: None,
+            wallet_block: None,
+            system_program: None,
+            __remaining_accounts: Vec::new(),
+        });
+        Self { instruction }
+    }
+    #[inline(always)]
+    pub fn authority(
+        &mut self,
+        authority: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.authority = Some(authority);
+        self
+    }
+    #[inline(always)]
+    pub fn config(
+        &mut self,
+        config: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.config = Some(config);
+        self
+    }
+    #[inline(always)]
+    pub fn wallet_block(
+        &mut self,
+        wallet_block: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.wallet_block = Some(wallet_block);
+        self
+    }
+    #[inline(always)]
+    pub fn system_program(
+        &mut self,
+        system_program: &'b solana_program::account_info::AccountInfo<'a>,
+    ) -> &mut Self {
+        self.instruction.system_program = Some(system_program);
+        self
+    }
+    /// Add an additional account to the instruction.
+    #[inline(always)]
+    pub fn add_remaining_account(
+        &mut self,
+        account: &'b solana_program::account_info::AccountInfo<'a>,
+        is_writable: bool,
+        is_signer: bool,
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .push((account, is_writable, is_signer));
+        self
+    }
+    /// Add additional accounts to the instruction.
+    ///
+    /// Each account is represented by a tuple of the `AccountInfo`, a `bool` indicating whether the account is writable or not,
+    /// and a `bool` indicating whether the account is a signer or not.
+    #[inline(always)]
+    pub fn add_remaining_accounts(
+        &mut self,
+        accounts: &[(
+            &'b solana_program::account_info::AccountInfo<'a>,
+            bool,
+            bool,
+        )],
+    ) -> &mut Self {
+        self.instruction
+            .__remaining_accounts
+            .extend_from_slice(accounts);
+        self
+    }
+    #[inline(always)]
+    pub fn invoke(&self) -> solana_program::entrypoint::ProgramResult {
+        self.invoke_signed(&[])
+    }
+    #[allow(clippy::clone_on_copy)]
+    #[allow(clippy::vec_init_then_push)]
+    pub fn invoke_signed(
+        &self,
+        signers_seeds: &[&[&[u8]]],
+    ) -> solana_program::entrypoint::ProgramResult {
+        let instruction = UnblockWalletCpi {
+            __program: self.instruction.__program,
+
+            authority: self.instruction.authority.expect("authority is not set"),
+
+            config: self.instruction.config.expect("config is not set"),
+
+            wallet_block: self
+                .instruction
+                .wallet_block
+                .expect("wallet_block is not set"),
+
+            system_program: self
+                .instruction
+                .system_program
+                .expect("system_program is not set"),
+        };
+        instruction.invoke_signed_with_remaining_accounts(
+            signers_seeds,
+            &self.instruction.__remaining_accounts,
+        )
+    }
+}
+
+#[derive(Clone, Debug)]
+struct UnblockWalletCpiBuilderInstruction<'a, 'b> {
+    __program: &'b solana_program::account_info::AccountInfo<'a>,
+    authority: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    config: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    wallet_block: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    system_program: Option<&'b solana_program::account_info::AccountInfo<'a>>,
+    /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`.
+    __remaining_accounts: Vec<(
+        &'b solana_program::account_info::AccountInfo<'a>,
+        bool,
+        bool,
+    )>,
+}

+ 14 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/mod.rs

@@ -0,0 +1,14 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+pub mod accounts;
+pub mod errors;
+pub mod instructions;
+pub mod programs;
+pub mod shared;
+
+pub(crate) use programs::*;

+ 11 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/programs.rs

@@ -0,0 +1,11 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+use solana_program::{pubkey, pubkey::Pubkey};
+
+/// `block_list` program ID.
+pub const BLOCK_LIST_ID: Pubkey = pubkey!("BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf");

+ 21 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/shared.rs

@@ -0,0 +1,21 @@
+//! This code was AUTOGENERATED using the codama library.
+//! Please DO NOT EDIT THIS FILE, instead use visitors
+//! to add features, then rerun codama to update it.
+//!
+//! <https://github.com/codama-idl/codama>
+//!
+
+#[cfg(feature = "fetch")]
+#[derive(Debug, Clone)]
+pub struct DecodedAccount<T> {
+    pub address: solana_program::pubkey::Pubkey,
+    pub account: solana_sdk::account::Account,
+    pub data: T,
+}
+
+#[cfg(feature = "fetch")]
+#[derive(Debug, Clone)]
+pub enum MaybeAccount<T> {
+    Exists(DecodedAccount<T>),
+    NotFound(solana_program::pubkey::Pubkey),
+}

+ 2 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/lib.rs

@@ -0,0 +1,2 @@
+pub mod client;
+pub use client::*;

+ 148 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/config.ts

@@ -0,0 +1,148 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  assertAccountExists,
+  assertAccountsExist,
+  combineCodec,
+  decodeAccount,
+  fetchEncodedAccount,
+  fetchEncodedAccounts,
+  getAddressDecoder,
+  getAddressEncoder,
+  getStructDecoder,
+  getStructEncoder,
+  getU64Decoder,
+  getU64Encoder,
+  getU8Decoder,
+  getU8Encoder,
+  type Account,
+  type Address,
+  type Codec,
+  type Decoder,
+  type EncodedAccount,
+  type Encoder,
+  type FetchAccountConfig,
+  type FetchAccountsConfig,
+  type MaybeAccount,
+  type MaybeEncodedAccount,
+} from '@solana/kit';
+import { findConfigPda } from '../pdas';
+
+export const CONFIG_DISCRIMINATOR = 0;
+
+export function getConfigDiscriminatorBytes() {
+  return getU8Encoder().encode(CONFIG_DISCRIMINATOR);
+}
+
+export type Config = {
+  discriminator: number;
+  authority: Address;
+  blockedWalletsCount: bigint;
+};
+
+export type ConfigArgs = {
+  discriminator: number;
+  authority: Address;
+  blockedWalletsCount: number | bigint;
+};
+
+export function getConfigEncoder(): Encoder<ConfigArgs> {
+  return getStructEncoder([
+    ['discriminator', getU8Encoder()],
+    ['authority', getAddressEncoder()],
+    ['blockedWalletsCount', getU64Encoder()],
+  ]);
+}
+
+export function getConfigDecoder(): Decoder<Config> {
+  return getStructDecoder([
+    ['discriminator', getU8Decoder()],
+    ['authority', getAddressDecoder()],
+    ['blockedWalletsCount', getU64Decoder()],
+  ]);
+}
+
+export function getConfigCodec(): Codec<ConfigArgs, Config> {
+  return combineCodec(getConfigEncoder(), getConfigDecoder());
+}
+
+export function decodeConfig<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress>
+): Account<Config, TAddress>;
+export function decodeConfig<TAddress extends string = string>(
+  encodedAccount: MaybeEncodedAccount<TAddress>
+): MaybeAccount<Config, TAddress>;
+export function decodeConfig<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>
+): Account<Config, TAddress> | MaybeAccount<Config, TAddress> {
+  return decodeAccount(
+    encodedAccount as MaybeEncodedAccount<TAddress>,
+    getConfigDecoder()
+  );
+}
+
+export async function fetchConfig<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<Account<Config, TAddress>> {
+  const maybeAccount = await fetchMaybeConfig(rpc, address, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeConfig<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<MaybeAccount<Config, TAddress>> {
+  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
+  return decodeConfig(maybeAccount);
+}
+
+export async function fetchAllConfig(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<Account<Config>[]> {
+  const maybeAccounts = await fetchAllMaybeConfig(rpc, addresses, config);
+  assertAccountsExist(maybeAccounts);
+  return maybeAccounts;
+}
+
+export async function fetchAllMaybeConfig(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<MaybeAccount<Config>[]> {
+  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
+  return maybeAccounts.map((maybeAccount) => decodeConfig(maybeAccount));
+}
+
+export function getConfigSize(): number {
+  return 41;
+}
+
+export async function fetchConfigFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<Account<Config>> {
+  const maybeAccount = await fetchMaybeConfigFromSeeds(rpc, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeConfigFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<MaybeAccount<Config>> {
+  const { programAddress, ...fetchConfig } = config;
+  const [address] = await findConfigPda({ programAddress });
+  return await fetchMaybeConfig(rpc, address, fetchConfig);
+}

+ 118 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/extraMetas.ts

@@ -0,0 +1,118 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  assertAccountExists,
+  assertAccountsExist,
+  combineCodec,
+  decodeAccount,
+  fetchEncodedAccount,
+  fetchEncodedAccounts,
+  getStructDecoder,
+  getStructEncoder,
+  type Account,
+  type Address,
+  type Codec,
+  type Decoder,
+  type EncodedAccount,
+  type Encoder,
+  type FetchAccountConfig,
+  type FetchAccountsConfig,
+  type MaybeAccount,
+  type MaybeEncodedAccount,
+} from '@solana/kit';
+import { ExtraMetasSeeds, findExtraMetasPda } from '../pdas';
+
+export type ExtraMetas = {};
+
+export type ExtraMetasArgs = ExtraMetas;
+
+export function getExtraMetasEncoder(): Encoder<ExtraMetasArgs> {
+  return getStructEncoder([]);
+}
+
+export function getExtraMetasDecoder(): Decoder<ExtraMetas> {
+  return getStructDecoder([]);
+}
+
+export function getExtraMetasCodec(): Codec<ExtraMetasArgs, ExtraMetas> {
+  return combineCodec(getExtraMetasEncoder(), getExtraMetasDecoder());
+}
+
+export function decodeExtraMetas<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress>
+): Account<ExtraMetas, TAddress>;
+export function decodeExtraMetas<TAddress extends string = string>(
+  encodedAccount: MaybeEncodedAccount<TAddress>
+): MaybeAccount<ExtraMetas, TAddress>;
+export function decodeExtraMetas<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>
+): Account<ExtraMetas, TAddress> | MaybeAccount<ExtraMetas, TAddress> {
+  return decodeAccount(
+    encodedAccount as MaybeEncodedAccount<TAddress>,
+    getExtraMetasDecoder()
+  );
+}
+
+export async function fetchExtraMetas<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<Account<ExtraMetas, TAddress>> {
+  const maybeAccount = await fetchMaybeExtraMetas(rpc, address, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeExtraMetas<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<MaybeAccount<ExtraMetas, TAddress>> {
+  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
+  return decodeExtraMetas(maybeAccount);
+}
+
+export async function fetchAllExtraMetas(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<Account<ExtraMetas>[]> {
+  const maybeAccounts = await fetchAllMaybeExtraMetas(rpc, addresses, config);
+  assertAccountsExist(maybeAccounts);
+  return maybeAccounts;
+}
+
+export async function fetchAllMaybeExtraMetas(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<MaybeAccount<ExtraMetas>[]> {
+  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
+  return maybeAccounts.map((maybeAccount) => decodeExtraMetas(maybeAccount));
+}
+
+export async function fetchExtraMetasFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  seeds: ExtraMetasSeeds,
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<Account<ExtraMetas>> {
+  const maybeAccount = await fetchMaybeExtraMetasFromSeeds(rpc, seeds, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeExtraMetasFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  seeds: ExtraMetasSeeds,
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<MaybeAccount<ExtraMetas>> {
+  const { programAddress, ...fetchConfig } = config;
+  const [address] = await findExtraMetasPda(seeds, { programAddress });
+  return await fetchMaybeExtraMetas(rpc, address, fetchConfig);
+}

+ 11 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/index.ts

@@ -0,0 +1,11 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './config';
+export * from './extraMetas';
+export * from './walletBlock';

+ 131 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/walletBlock.ts

@@ -0,0 +1,131 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  assertAccountExists,
+  assertAccountsExist,
+  combineCodec,
+  decodeAccount,
+  fetchEncodedAccount,
+  fetchEncodedAccounts,
+  getAddressDecoder,
+  getAddressEncoder,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Encoder,
+  type Account,
+  type Address,
+  type Codec,
+  type Decoder,
+  type EncodedAccount,
+  type Encoder,
+  type FetchAccountConfig,
+  type FetchAccountsConfig,
+  type MaybeAccount,
+  type MaybeEncodedAccount,
+} from '@solana/kit';
+import { WalletBlockSeeds, findWalletBlockPda } from '../pdas';
+
+export const WALLET_BLOCK_DISCRIMINATOR = 1;
+
+export function getWalletBlockDiscriminatorBytes() {
+  return getU8Encoder().encode(WALLET_BLOCK_DISCRIMINATOR);
+}
+
+export type WalletBlock = { authority: Address };
+
+export type WalletBlockArgs = WalletBlock;
+
+export function getWalletBlockEncoder(): Encoder<WalletBlockArgs> {
+  return getStructEncoder([['authority', getAddressEncoder()]]);
+}
+
+export function getWalletBlockDecoder(): Decoder<WalletBlock> {
+  return getStructDecoder([['authority', getAddressDecoder()]]);
+}
+
+export function getWalletBlockCodec(): Codec<WalletBlockArgs, WalletBlock> {
+  return combineCodec(getWalletBlockEncoder(), getWalletBlockDecoder());
+}
+
+export function decodeWalletBlock<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress>
+): Account<WalletBlock, TAddress>;
+export function decodeWalletBlock<TAddress extends string = string>(
+  encodedAccount: MaybeEncodedAccount<TAddress>
+): MaybeAccount<WalletBlock, TAddress>;
+export function decodeWalletBlock<TAddress extends string = string>(
+  encodedAccount: EncodedAccount<TAddress> | MaybeEncodedAccount<TAddress>
+): Account<WalletBlock, TAddress> | MaybeAccount<WalletBlock, TAddress> {
+  return decodeAccount(
+    encodedAccount as MaybeEncodedAccount<TAddress>,
+    getWalletBlockDecoder()
+  );
+}
+
+export async function fetchWalletBlock<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<Account<WalletBlock, TAddress>> {
+  const maybeAccount = await fetchMaybeWalletBlock(rpc, address, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeWalletBlock<TAddress extends string = string>(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  address: Address<TAddress>,
+  config?: FetchAccountConfig
+): Promise<MaybeAccount<WalletBlock, TAddress>> {
+  const maybeAccount = await fetchEncodedAccount(rpc, address, config);
+  return decodeWalletBlock(maybeAccount);
+}
+
+export async function fetchAllWalletBlock(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<Account<WalletBlock>[]> {
+  const maybeAccounts = await fetchAllMaybeWalletBlock(rpc, addresses, config);
+  assertAccountsExist(maybeAccounts);
+  return maybeAccounts;
+}
+
+export async function fetchAllMaybeWalletBlock(
+  rpc: Parameters<typeof fetchEncodedAccounts>[0],
+  addresses: Array<Address>,
+  config?: FetchAccountsConfig
+): Promise<MaybeAccount<WalletBlock>[]> {
+  const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config);
+  return maybeAccounts.map((maybeAccount) => decodeWalletBlock(maybeAccount));
+}
+
+export function getWalletBlockSize(): number {
+  return 33;
+}
+
+export async function fetchWalletBlockFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  seeds: WalletBlockSeeds,
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<Account<WalletBlock>> {
+  const maybeAccount = await fetchMaybeWalletBlockFromSeeds(rpc, seeds, config);
+  assertAccountExists(maybeAccount);
+  return maybeAccount;
+}
+
+export async function fetchMaybeWalletBlockFromSeeds(
+  rpc: Parameters<typeof fetchEncodedAccount>[0],
+  seeds: WalletBlockSeeds,
+  config: FetchAccountConfig & { programAddress?: Address } = {}
+): Promise<MaybeAccount<WalletBlock>> {
+  const { programAddress, ...fetchConfig } = config;
+  const [address] = await findWalletBlockPda(seeds, { programAddress });
+  return await fetchMaybeWalletBlock(rpc, address, fetchConfig);
+}

+ 12 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/index.ts

@@ -0,0 +1,12 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './accounts';
+export * from './instructions';
+export * from './pdas';
+export * from './programs';

+ 314 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/blockWallet.ts

@@ -0,0 +1,314 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Decoder,
+  getU8Encoder,
+  transformEncoder,
+  type Address,
+  type Codec,
+  type Decoder,
+  type Encoder,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type IInstruction,
+  type IInstructionWithAccounts,
+  type IInstructionWithData,
+  type ReadonlyAccount,
+  type TransactionSigner,
+  type WritableAccount,
+  type WritableSignerAccount,
+} from '@solana/kit';
+import { findConfigPda } from '../pdas';
+import { BLOCK_LIST_PROGRAM_ADDRESS } from '../programs';
+import { getAccountMetaFactory, type ResolvedAccount } from '../shared';
+
+export const BLOCK_WALLET_DISCRIMINATOR = 242;
+
+export function getBlockWalletDiscriminatorBytes() {
+  return getU8Encoder().encode(BLOCK_WALLET_DISCRIMINATOR);
+}
+
+export type BlockWalletInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountAuthority extends string | IAccountMeta<string> = string,
+  TAccountConfig extends string | IAccountMeta<string> = string,
+  TAccountWallet extends string | IAccountMeta<string> = string,
+  TAccountWalletBlock extends string | IAccountMeta<string> = string,
+  TAccountSystemProgram extends
+    | string
+    | IAccountMeta<string> = '11111111111111111111111111111111',
+  TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
+> = IInstruction<TProgram> &
+  IInstructionWithData<Uint8Array> &
+  IInstructionWithAccounts<
+    [
+      TAccountAuthority extends string
+        ? WritableSignerAccount<TAccountAuthority> &
+            IAccountSignerMeta<TAccountAuthority>
+        : TAccountAuthority,
+      TAccountConfig extends string
+        ? WritableAccount<TAccountConfig>
+        : TAccountConfig,
+      TAccountWallet extends string
+        ? ReadonlyAccount<TAccountWallet>
+        : TAccountWallet,
+      TAccountWalletBlock extends string
+        ? WritableAccount<TAccountWalletBlock>
+        : TAccountWalletBlock,
+      TAccountSystemProgram extends string
+        ? ReadonlyAccount<TAccountSystemProgram>
+        : TAccountSystemProgram,
+      ...TRemainingAccounts,
+    ]
+  >;
+
+export type BlockWalletInstructionData = { discriminator: number };
+
+export type BlockWalletInstructionDataArgs = {};
+
+export function getBlockWalletInstructionDataEncoder(): Encoder<BlockWalletInstructionDataArgs> {
+  return transformEncoder(
+    getStructEncoder([['discriminator', getU8Encoder()]]),
+    (value) => ({ ...value, discriminator: 242 })
+  );
+}
+
+export function getBlockWalletInstructionDataDecoder(): Decoder<BlockWalletInstructionData> {
+  return getStructDecoder([['discriminator', getU8Decoder()]]);
+}
+
+export function getBlockWalletInstructionDataCodec(): Codec<
+  BlockWalletInstructionDataArgs,
+  BlockWalletInstructionData
+> {
+  return combineCodec(
+    getBlockWalletInstructionDataEncoder(),
+    getBlockWalletInstructionDataDecoder()
+  );
+}
+
+export type BlockWalletAsyncInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountWallet extends string = string,
+  TAccountWalletBlock extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config?: Address<TAccountConfig>;
+  wallet: Address<TAccountWallet>;
+  walletBlock: Address<TAccountWalletBlock>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export async function getBlockWalletInstructionAsync<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountWallet extends string,
+  TAccountWalletBlock extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: BlockWalletAsyncInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): Promise<
+  BlockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    wallet: { value: input.wallet ?? null, isWritable: false },
+    walletBlock: { value: input.walletBlock ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.config.value) {
+    accounts.config.value = await findConfigPda();
+  }
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.wallet),
+      getAccountMeta(accounts.walletBlock),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getBlockWalletInstructionDataEncoder().encode({}),
+  } as BlockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type BlockWalletInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountWallet extends string = string,
+  TAccountWalletBlock extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config: Address<TAccountConfig>;
+  wallet: Address<TAccountWallet>;
+  walletBlock: Address<TAccountWalletBlock>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export function getBlockWalletInstruction<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountWallet extends string,
+  TAccountWalletBlock extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: BlockWalletInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): BlockWalletInstruction<
+  TProgramAddress,
+  TAccountAuthority,
+  TAccountConfig,
+  TAccountWallet,
+  TAccountWalletBlock,
+  TAccountSystemProgram
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    wallet: { value: input.wallet ?? null, isWritable: false },
+    walletBlock: { value: input.walletBlock ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.wallet),
+      getAccountMeta(accounts.walletBlock),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getBlockWalletInstructionDataEncoder().encode({}),
+  } as BlockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWallet,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type ParsedBlockWalletInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
+> = {
+  programAddress: Address<TProgram>;
+  accounts: {
+    authority: TAccountMetas[0];
+    config: TAccountMetas[1];
+    wallet: TAccountMetas[2];
+    walletBlock: TAccountMetas[3];
+    systemProgram: TAccountMetas[4];
+  };
+  data: BlockWalletInstructionData;
+};
+
+export function parseBlockWalletInstruction<
+  TProgram extends string,
+  TAccountMetas extends readonly IAccountMeta[],
+>(
+  instruction: IInstruction<TProgram> &
+    IInstructionWithAccounts<TAccountMetas> &
+    IInstructionWithData<Uint8Array>
+): ParsedBlockWalletInstruction<TProgram, TAccountMetas> {
+  if (instruction.accounts.length < 5) {
+    // TODO: Coded error.
+    throw new Error('Not enough accounts');
+  }
+  let accountIndex = 0;
+  const getNextAccount = () => {
+    const accountMeta = instruction.accounts![accountIndex]!;
+    accountIndex += 1;
+    return accountMeta;
+  };
+  return {
+    programAddress: instruction.programAddress,
+    accounts: {
+      authority: getNextAccount(),
+      config: getNextAccount(),
+      wallet: getNextAccount(),
+      walletBlock: getNextAccount(),
+      systemProgram: getNextAccount(),
+    },
+    data: getBlockWalletInstructionDataDecoder().decode(instruction.data),
+  };
+}

+ 12 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/index.ts

@@ -0,0 +1,12 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './blockWallet';
+export * from './init';
+export * from './setupExtraMetas';
+export * from './unblockWallet';

+ 266 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/init.ts

@@ -0,0 +1,266 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Decoder,
+  getU8Encoder,
+  transformEncoder,
+  type Address,
+  type Codec,
+  type Decoder,
+  type Encoder,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type IInstruction,
+  type IInstructionWithAccounts,
+  type IInstructionWithData,
+  type ReadonlyAccount,
+  type TransactionSigner,
+  type WritableAccount,
+  type WritableSignerAccount,
+} from '@solana/kit';
+import { findConfigPda } from '../pdas';
+import { BLOCK_LIST_PROGRAM_ADDRESS } from '../programs';
+import { getAccountMetaFactory, type ResolvedAccount } from '../shared';
+
+export const INIT_DISCRIMINATOR = 241;
+
+export function getInitDiscriminatorBytes() {
+  return getU8Encoder().encode(INIT_DISCRIMINATOR);
+}
+
+export type InitInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountAuthority extends string | IAccountMeta<string> = string,
+  TAccountConfig extends string | IAccountMeta<string> = string,
+  TAccountSystemProgram extends
+    | string
+    | IAccountMeta<string> = '11111111111111111111111111111111',
+  TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
+> = IInstruction<TProgram> &
+  IInstructionWithData<Uint8Array> &
+  IInstructionWithAccounts<
+    [
+      TAccountAuthority extends string
+        ? WritableSignerAccount<TAccountAuthority> &
+            IAccountSignerMeta<TAccountAuthority>
+        : TAccountAuthority,
+      TAccountConfig extends string
+        ? WritableAccount<TAccountConfig>
+        : TAccountConfig,
+      TAccountSystemProgram extends string
+        ? ReadonlyAccount<TAccountSystemProgram>
+        : TAccountSystemProgram,
+      ...TRemainingAccounts,
+    ]
+  >;
+
+export type InitInstructionData = { discriminator: number };
+
+export type InitInstructionDataArgs = {};
+
+export function getInitInstructionDataEncoder(): Encoder<InitInstructionDataArgs> {
+  return transformEncoder(
+    getStructEncoder([['discriminator', getU8Encoder()]]),
+    (value) => ({ ...value, discriminator: 241 })
+  );
+}
+
+export function getInitInstructionDataDecoder(): Decoder<InitInstructionData> {
+  return getStructDecoder([['discriminator', getU8Decoder()]]);
+}
+
+export function getInitInstructionDataCodec(): Codec<
+  InitInstructionDataArgs,
+  InitInstructionData
+> {
+  return combineCodec(
+    getInitInstructionDataEncoder(),
+    getInitInstructionDataDecoder()
+  );
+}
+
+export type InitAsyncInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config?: Address<TAccountConfig>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export async function getInitInstructionAsync<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: InitAsyncInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): Promise<
+  InitInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountSystemProgram
+  >
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.config.value) {
+    accounts.config.value = await findConfigPda();
+  }
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getInitInstructionDataEncoder().encode({}),
+  } as InitInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type InitInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config: Address<TAccountConfig>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export function getInitInstruction<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: InitInput<TAccountAuthority, TAccountConfig, TAccountSystemProgram>,
+  config?: { programAddress?: TProgramAddress }
+): InitInstruction<
+  TProgramAddress,
+  TAccountAuthority,
+  TAccountConfig,
+  TAccountSystemProgram
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getInitInstructionDataEncoder().encode({}),
+  } as InitInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type ParsedInitInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
+> = {
+  programAddress: Address<TProgram>;
+  accounts: {
+    authority: TAccountMetas[0];
+    config: TAccountMetas[1];
+    systemProgram: TAccountMetas[2];
+  };
+  data: InitInstructionData;
+};
+
+export function parseInitInstruction<
+  TProgram extends string,
+  TAccountMetas extends readonly IAccountMeta[],
+>(
+  instruction: IInstruction<TProgram> &
+    IInstructionWithAccounts<TAccountMetas> &
+    IInstructionWithData<Uint8Array>
+): ParsedInitInstruction<TProgram, TAccountMetas> {
+  if (instruction.accounts.length < 3) {
+    // TODO: Coded error.
+    throw new Error('Not enough accounts');
+  }
+  let accountIndex = 0;
+  const getNextAccount = () => {
+    const accountMeta = instruction.accounts![accountIndex]!;
+    accountIndex += 1;
+    return accountMeta;
+  };
+  return {
+    programAddress: instruction.programAddress,
+    accounts: {
+      authority: getNextAccount(),
+      config: getNextAccount(),
+      systemProgram: getNextAccount(),
+    },
+    data: getInitInstructionDataDecoder().decode(instruction.data),
+  };
+}

+ 344 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/setupExtraMetas.ts

@@ -0,0 +1,344 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getBooleanDecoder,
+  getBooleanEncoder,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Decoder,
+  getU8Encoder,
+  transformEncoder,
+  type Address,
+  type Codec,
+  type Decoder,
+  type Encoder,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type IInstruction,
+  type IInstructionWithAccounts,
+  type IInstructionWithData,
+  type ReadonlyAccount,
+  type TransactionSigner,
+  type WritableAccount,
+  type WritableSignerAccount,
+} from '@solana/kit';
+import { findConfigPda, findExtraMetasPda } from '../pdas';
+import { BLOCK_LIST_PROGRAM_ADDRESS } from '../programs';
+import { getAccountMetaFactory, type ResolvedAccount } from '../shared';
+
+export const SETUP_EXTRA_METAS_DISCRIMINATOR = 106;
+
+export function getSetupExtraMetasDiscriminatorBytes() {
+  return getU8Encoder().encode(SETUP_EXTRA_METAS_DISCRIMINATOR);
+}
+
+export type SetupExtraMetasInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountAuthority extends string | IAccountMeta<string> = string,
+  TAccountConfig extends string | IAccountMeta<string> = string,
+  TAccountMint extends string | IAccountMeta<string> = string,
+  TAccountExtraMetas extends string | IAccountMeta<string> = string,
+  TAccountSystemProgram extends
+    | string
+    | IAccountMeta<string> = '11111111111111111111111111111111',
+  TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
+> = IInstruction<TProgram> &
+  IInstructionWithData<Uint8Array> &
+  IInstructionWithAccounts<
+    [
+      TAccountAuthority extends string
+        ? WritableSignerAccount<TAccountAuthority> &
+            IAccountSignerMeta<TAccountAuthority>
+        : TAccountAuthority,
+      TAccountConfig extends string
+        ? ReadonlyAccount<TAccountConfig>
+        : TAccountConfig,
+      TAccountMint extends string
+        ? ReadonlyAccount<TAccountMint>
+        : TAccountMint,
+      TAccountExtraMetas extends string
+        ? WritableAccount<TAccountExtraMetas>
+        : TAccountExtraMetas,
+      TAccountSystemProgram extends string
+        ? ReadonlyAccount<TAccountSystemProgram>
+        : TAccountSystemProgram,
+      ...TRemainingAccounts,
+    ]
+  >;
+
+export type SetupExtraMetasInstructionData = {
+  discriminator: number;
+  checkBothWallets: boolean;
+};
+
+export type SetupExtraMetasInstructionDataArgs = { checkBothWallets?: boolean };
+
+export function getSetupExtraMetasInstructionDataEncoder(): Encoder<SetupExtraMetasInstructionDataArgs> {
+  return transformEncoder(
+    getStructEncoder([
+      ['discriminator', getU8Encoder()],
+      ['checkBothWallets', getBooleanEncoder()],
+    ]),
+    (value) => ({
+      ...value,
+      discriminator: 106,
+      checkBothWallets: value.checkBothWallets ?? false,
+    })
+  );
+}
+
+export function getSetupExtraMetasInstructionDataDecoder(): Decoder<SetupExtraMetasInstructionData> {
+  return getStructDecoder([
+    ['discriminator', getU8Decoder()],
+    ['checkBothWallets', getBooleanDecoder()],
+  ]);
+}
+
+export function getSetupExtraMetasInstructionDataCodec(): Codec<
+  SetupExtraMetasInstructionDataArgs,
+  SetupExtraMetasInstructionData
+> {
+  return combineCodec(
+    getSetupExtraMetasInstructionDataEncoder(),
+    getSetupExtraMetasInstructionDataDecoder()
+  );
+}
+
+export type SetupExtraMetasAsyncInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountMint extends string = string,
+  TAccountExtraMetas extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config?: Address<TAccountConfig>;
+  mint: Address<TAccountMint>;
+  extraMetas?: Address<TAccountExtraMetas>;
+  systemProgram?: Address<TAccountSystemProgram>;
+  checkBothWallets?: SetupExtraMetasInstructionDataArgs['checkBothWallets'];
+};
+
+export async function getSetupExtraMetasInstructionAsync<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountMint extends string,
+  TAccountExtraMetas extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: SetupExtraMetasAsyncInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): Promise<
+  SetupExtraMetasInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: false },
+    mint: { value: input.mint ?? null, isWritable: false },
+    extraMetas: { value: input.extraMetas ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Original args.
+  const args = { ...input };
+
+  // Resolve default values.
+  if (!accounts.config.value) {
+    accounts.config.value = await findConfigPda();
+  }
+  if (!accounts.extraMetas.value) {
+    accounts.extraMetas.value = await findExtraMetasPda();
+  }
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.mint),
+      getAccountMeta(accounts.extraMetas),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getSetupExtraMetasInstructionDataEncoder().encode(
+      args as SetupExtraMetasInstructionDataArgs
+    ),
+  } as SetupExtraMetasInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type SetupExtraMetasInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountMint extends string = string,
+  TAccountExtraMetas extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config: Address<TAccountConfig>;
+  mint: Address<TAccountMint>;
+  extraMetas: Address<TAccountExtraMetas>;
+  systemProgram?: Address<TAccountSystemProgram>;
+  checkBothWallets?: SetupExtraMetasInstructionDataArgs['checkBothWallets'];
+};
+
+export function getSetupExtraMetasInstruction<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountMint extends string,
+  TAccountExtraMetas extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: SetupExtraMetasInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): SetupExtraMetasInstruction<
+  TProgramAddress,
+  TAccountAuthority,
+  TAccountConfig,
+  TAccountMint,
+  TAccountExtraMetas,
+  TAccountSystemProgram
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: false },
+    mint: { value: input.mint ?? null, isWritable: false },
+    extraMetas: { value: input.extraMetas ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Original args.
+  const args = { ...input };
+
+  // Resolve default values.
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.mint),
+      getAccountMeta(accounts.extraMetas),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getSetupExtraMetasInstructionDataEncoder().encode(
+      args as SetupExtraMetasInstructionDataArgs
+    ),
+  } as SetupExtraMetasInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountMint,
+    TAccountExtraMetas,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type ParsedSetupExtraMetasInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
+> = {
+  programAddress: Address<TProgram>;
+  accounts: {
+    authority: TAccountMetas[0];
+    config: TAccountMetas[1];
+    mint: TAccountMetas[2];
+    extraMetas: TAccountMetas[3];
+    systemProgram: TAccountMetas[4];
+  };
+  data: SetupExtraMetasInstructionData;
+};
+
+export function parseSetupExtraMetasInstruction<
+  TProgram extends string,
+  TAccountMetas extends readonly IAccountMeta[],
+>(
+  instruction: IInstruction<TProgram> &
+    IInstructionWithAccounts<TAccountMetas> &
+    IInstructionWithData<Uint8Array>
+): ParsedSetupExtraMetasInstruction<TProgram, TAccountMetas> {
+  if (instruction.accounts.length < 5) {
+    // TODO: Coded error.
+    throw new Error('Not enough accounts');
+  }
+  let accountIndex = 0;
+  const getNextAccount = () => {
+    const accountMeta = instruction.accounts![accountIndex]!;
+    accountIndex += 1;
+    return accountMeta;
+  };
+  return {
+    programAddress: instruction.programAddress,
+    accounts: {
+      authority: getNextAccount(),
+      config: getNextAccount(),
+      mint: getNextAccount(),
+      extraMetas: getNextAccount(),
+      systemProgram: getNextAccount(),
+    },
+    data: getSetupExtraMetasInstructionDataDecoder().decode(instruction.data),
+  };
+}

+ 292 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/unblockWallet.ts

@@ -0,0 +1,292 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  combineCodec,
+  getStructDecoder,
+  getStructEncoder,
+  getU8Decoder,
+  getU8Encoder,
+  transformEncoder,
+  type Address,
+  type Codec,
+  type Decoder,
+  type Encoder,
+  type IAccountMeta,
+  type IAccountSignerMeta,
+  type IInstruction,
+  type IInstructionWithAccounts,
+  type IInstructionWithData,
+  type ReadonlyAccount,
+  type TransactionSigner,
+  type WritableAccount,
+  type WritableSignerAccount,
+} from '@solana/kit';
+import { findConfigPda } from '../pdas';
+import { BLOCK_LIST_PROGRAM_ADDRESS } from '../programs';
+import { getAccountMetaFactory, type ResolvedAccount } from '../shared';
+
+export const UNBLOCK_WALLET_DISCRIMINATOR = 243;
+
+export function getUnblockWalletDiscriminatorBytes() {
+  return getU8Encoder().encode(UNBLOCK_WALLET_DISCRIMINATOR);
+}
+
+export type UnblockWalletInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountAuthority extends string | IAccountMeta<string> = string,
+  TAccountConfig extends string | IAccountMeta<string> = string,
+  TAccountWalletBlock extends string | IAccountMeta<string> = string,
+  TAccountSystemProgram extends
+    | string
+    | IAccountMeta<string> = '11111111111111111111111111111111',
+  TRemainingAccounts extends readonly IAccountMeta<string>[] = [],
+> = IInstruction<TProgram> &
+  IInstructionWithData<Uint8Array> &
+  IInstructionWithAccounts<
+    [
+      TAccountAuthority extends string
+        ? WritableSignerAccount<TAccountAuthority> &
+            IAccountSignerMeta<TAccountAuthority>
+        : TAccountAuthority,
+      TAccountConfig extends string
+        ? WritableAccount<TAccountConfig>
+        : TAccountConfig,
+      TAccountWalletBlock extends string
+        ? WritableAccount<TAccountWalletBlock>
+        : TAccountWalletBlock,
+      TAccountSystemProgram extends string
+        ? ReadonlyAccount<TAccountSystemProgram>
+        : TAccountSystemProgram,
+      ...TRemainingAccounts,
+    ]
+  >;
+
+export type UnblockWalletInstructionData = { discriminator: number };
+
+export type UnblockWalletInstructionDataArgs = {};
+
+export function getUnblockWalletInstructionDataEncoder(): Encoder<UnblockWalletInstructionDataArgs> {
+  return transformEncoder(
+    getStructEncoder([['discriminator', getU8Encoder()]]),
+    (value) => ({ ...value, discriminator: 243 })
+  );
+}
+
+export function getUnblockWalletInstructionDataDecoder(): Decoder<UnblockWalletInstructionData> {
+  return getStructDecoder([['discriminator', getU8Decoder()]]);
+}
+
+export function getUnblockWalletInstructionDataCodec(): Codec<
+  UnblockWalletInstructionDataArgs,
+  UnblockWalletInstructionData
+> {
+  return combineCodec(
+    getUnblockWalletInstructionDataEncoder(),
+    getUnblockWalletInstructionDataDecoder()
+  );
+}
+
+export type UnblockWalletAsyncInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountWalletBlock extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config?: Address<TAccountConfig>;
+  walletBlock: Address<TAccountWalletBlock>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export async function getUnblockWalletInstructionAsync<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountWalletBlock extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: UnblockWalletAsyncInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): Promise<
+  UnblockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    walletBlock: { value: input.walletBlock ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.config.value) {
+    accounts.config.value = await findConfigPda();
+  }
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.walletBlock),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getUnblockWalletInstructionDataEncoder().encode({}),
+  } as UnblockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type UnblockWalletInput<
+  TAccountAuthority extends string = string,
+  TAccountConfig extends string = string,
+  TAccountWalletBlock extends string = string,
+  TAccountSystemProgram extends string = string,
+> = {
+  authority: TransactionSigner<TAccountAuthority>;
+  config: Address<TAccountConfig>;
+  walletBlock: Address<TAccountWalletBlock>;
+  systemProgram?: Address<TAccountSystemProgram>;
+};
+
+export function getUnblockWalletInstruction<
+  TAccountAuthority extends string,
+  TAccountConfig extends string,
+  TAccountWalletBlock extends string,
+  TAccountSystemProgram extends string,
+  TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+>(
+  input: UnblockWalletInput<
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >,
+  config?: { programAddress?: TProgramAddress }
+): UnblockWalletInstruction<
+  TProgramAddress,
+  TAccountAuthority,
+  TAccountConfig,
+  TAccountWalletBlock,
+  TAccountSystemProgram
+> {
+  // Program address.
+  const programAddress = config?.programAddress ?? BLOCK_LIST_PROGRAM_ADDRESS;
+
+  // Original accounts.
+  const originalAccounts = {
+    authority: { value: input.authority ?? null, isWritable: true },
+    config: { value: input.config ?? null, isWritable: true },
+    walletBlock: { value: input.walletBlock ?? null, isWritable: true },
+    systemProgram: { value: input.systemProgram ?? null, isWritable: false },
+  };
+  const accounts = originalAccounts as Record<
+    keyof typeof originalAccounts,
+    ResolvedAccount
+  >;
+
+  // Resolve default values.
+  if (!accounts.systemProgram.value) {
+    accounts.systemProgram.value =
+      '11111111111111111111111111111111' as Address<'11111111111111111111111111111111'>;
+  }
+
+  const getAccountMeta = getAccountMetaFactory(programAddress, 'programId');
+  const instruction = {
+    accounts: [
+      getAccountMeta(accounts.authority),
+      getAccountMeta(accounts.config),
+      getAccountMeta(accounts.walletBlock),
+      getAccountMeta(accounts.systemProgram),
+    ],
+    programAddress,
+    data: getUnblockWalletInstructionDataEncoder().encode({}),
+  } as UnblockWalletInstruction<
+    TProgramAddress,
+    TAccountAuthority,
+    TAccountConfig,
+    TAccountWalletBlock,
+    TAccountSystemProgram
+  >;
+
+  return instruction;
+}
+
+export type ParsedUnblockWalletInstruction<
+  TProgram extends string = typeof BLOCK_LIST_PROGRAM_ADDRESS,
+  TAccountMetas extends readonly IAccountMeta[] = readonly IAccountMeta[],
+> = {
+  programAddress: Address<TProgram>;
+  accounts: {
+    authority: TAccountMetas[0];
+    config: TAccountMetas[1];
+    walletBlock: TAccountMetas[2];
+    systemProgram: TAccountMetas[3];
+  };
+  data: UnblockWalletInstructionData;
+};
+
+export function parseUnblockWalletInstruction<
+  TProgram extends string,
+  TAccountMetas extends readonly IAccountMeta[],
+>(
+  instruction: IInstruction<TProgram> &
+    IInstructionWithAccounts<TAccountMetas> &
+    IInstructionWithData<Uint8Array>
+): ParsedUnblockWalletInstruction<TProgram, TAccountMetas> {
+  if (instruction.accounts.length < 4) {
+    // TODO: Coded error.
+    throw new Error('Not enough accounts');
+  }
+  let accountIndex = 0;
+  const getNextAccount = () => {
+    const accountMeta = instruction.accounts![accountIndex]!;
+    accountIndex += 1;
+    return accountMeta;
+  };
+  return {
+    programAddress: instruction.programAddress,
+    accounts: {
+      authority: getNextAccount(),
+      config: getNextAccount(),
+      walletBlock: getNextAccount(),
+      systemProgram: getNextAccount(),
+    },
+    data: getUnblockWalletInstructionDataDecoder().decode(instruction.data),
+  };
+}

+ 26 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/config.ts

@@ -0,0 +1,26 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  getProgramDerivedAddress,
+  getUtf8Encoder,
+  type Address,
+  type ProgramDerivedAddress,
+} from '@solana/kit';
+
+export async function findConfigPda(
+  config: { programAddress?: Address | undefined } = {}
+): Promise<ProgramDerivedAddress> {
+  const {
+    programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>,
+  } = config;
+  return await getProgramDerivedAddress({
+    programAddress,
+    seeds: [getUtf8Encoder().encode('config')],
+  });
+}

+ 35 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/extraMetas.ts

@@ -0,0 +1,35 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+import {
+  getAddressEncoder,
+  getProgramDerivedAddress,
+  getUtf8Encoder,
+  type Address,
+  type ProgramDerivedAddress,
+} from '@solana/kit';
+
+export type ExtraMetasSeeds = {
+  mint: Address;
+};
+
+export async function findExtraMetasPda(
+  seeds: ExtraMetasSeeds,
+  config: { programAddress?: Address | undefined } = {}
+): Promise<ProgramDerivedAddress> {
+  const {
+    programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>,
+  } = config;
+  return await getProgramDerivedAddress({
+    programAddress,
+    seeds: [
+      getUtf8Encoder().encode('extra-account-metas'),
+      getAddressEncoder().encode(seeds.mint),
+    ],
+  });
+}

+ 11 - 0
tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/index.ts

@@ -0,0 +1,11 @@
+/**
+ * This code was AUTOGENERATED using the codama library.
+ * Please DO NOT EDIT THIS FILE, instead use visitors
+ * to add features, then rerun codama to update it.
+ *
+ * @see https://github.com/codama-idl/codama
+ */
+
+export * from './config';
+export * from './extraMetas';
+export * from './walletBlock';

部分文件因为文件数量过多而无法显示