diff --git a/tokens/token-2022/transfer-hook/pblock-list/.gitignore b/tokens/token-2022/transfer-hook/pblock-list/.gitignore new file mode 100644 index 00000000..4bad4678 --- /dev/null +++ b/tokens/token-2022/transfer-hook/pblock-list/.gitignore @@ -0,0 +1,3 @@ +target/ +test-ledger/ +node_modules/ diff --git a/tokens/token-2022/transfer-hook/pblock-list/Cargo.toml b/tokens/token-2022/transfer-hook/pblock-list/Cargo.toml new file mode 100644 index 00000000..11ccfb99 --- /dev/null +++ b/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"} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/cli/Cargo.toml b/tokens/token-2022/transfer-hook/pblock-list/cli/Cargo.toml new file mode 100644 index 00000000..e29193f6 --- /dev/null +++ b/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" \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/cli/src/main.rs b/tokens/token-2022/transfer-hook/pblock-list/cli/src/main.rs new file mode 100644 index 00000000..7c5d7cc5 --- /dev/null +++ b/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, + json_rpc_url: String, + verbose: bool, +} + +pub fn get_extra_account_metas_with_source_wallet_block() -> Vec { + 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 { + 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 { + let size = ExtraAccountMetaList::size_of(0).unwrap(); + let metas: Vec = vec![]; + let mut data = vec![0; size]; + ExtraAccountMetaList::init::(&mut data, &metas).unwrap(); + data +} + +fn create_extra_metas_with_source_wallet_block() -> Vec { + let metas: Vec = get_extra_account_metas_with_source_wallet_block(); + let size = ExtraAccountMetaList::size_of(metas.len()).unwrap(); + let mut data = vec![0; size]; + ExtraAccountMetaList::init::(&mut data, &metas).unwrap(); + data +} +fn create_extra_metas_with_both_wallet_blocks() -> Vec { + let metas: Vec = get_extra_account_metas_with_both_wallet_blocks(); + let size = ExtraAccountMetaList::size_of(metas.len()).unwrap(); + let mut data = vec![0; size]; + ExtraAccountMetaList::init::(&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) { + 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, 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, + payer: &Arc, + mint_address: &Pubkey, + check_both_wallets: bool, +) -> Result> { + 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, + payer: &Arc, +) -> Result> { + + 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, + payer: &Arc, + wallet_address: &Pubkey, +) -> Result> { + + 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, + payer: &Arc, + wallet_address: &Pubkey, +) -> Result> { + 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> { + 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> = None; + + let config = { + let cli_config = if let Some(config_file) = matches.try_get_one::("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::("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(()) +} + \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/codama.ts b/tokens/token-2022/transfer-hook/pblock-list/codama.ts new file mode 100644 index 00000000..f532b847 --- /dev/null +++ b/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 })); diff --git a/tokens/token-2022/transfer-hook/pblock-list/package.json b/tokens/token-2022/transfer-hook/pblock-list/package.json new file mode 100644 index 00000000..6db4ee3f --- /dev/null +++ b/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" + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/Cargo.toml b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/Cargo.toml new file mode 100644 index 00000000..11ccfb99 --- /dev/null +++ b/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"} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/cli/Cargo.toml b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/cli/Cargo.toml new file mode 100644 index 00000000..e29193f6 --- /dev/null +++ b/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" \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/cli/src/main.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/cli/src/main.rs new file mode 100644 index 00000000..7c5d7cc5 --- /dev/null +++ b/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, + json_rpc_url: String, + verbose: bool, +} + +pub fn get_extra_account_metas_with_source_wallet_block() -> Vec { + 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 { + 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 { + let size = ExtraAccountMetaList::size_of(0).unwrap(); + let metas: Vec = vec![]; + let mut data = vec![0; size]; + ExtraAccountMetaList::init::(&mut data, &metas).unwrap(); + data +} + +fn create_extra_metas_with_source_wallet_block() -> Vec { + let metas: Vec = get_extra_account_metas_with_source_wallet_block(); + let size = ExtraAccountMetaList::size_of(metas.len()).unwrap(); + let mut data = vec![0; size]; + ExtraAccountMetaList::init::(&mut data, &metas).unwrap(); + data +} +fn create_extra_metas_with_both_wallet_blocks() -> Vec { + let metas: Vec = get_extra_account_metas_with_both_wallet_blocks(); + let size = ExtraAccountMetaList::size_of(metas.len()).unwrap(); + let mut data = vec![0; size]; + ExtraAccountMetaList::init::(&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) { + 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, 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, + payer: &Arc, + mint_address: &Pubkey, + check_both_wallets: bool, +) -> Result> { + 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, + payer: &Arc, +) -> Result> { + + 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, + payer: &Arc, + wallet_address: &Pubkey, +) -> Result> { + + 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, + payer: &Arc, + wallet_address: &Pubkey, +) -> Result> { + 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> { + 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> = None; + + let config = { + let cli_config = if let Some(config_file) = matches.try_get_one::("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::("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(()) +} + \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/codama.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/codama.ts new file mode 100644 index 00000000..f532b847 --- /dev/null +++ b/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 })); diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/package.json b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/package.json new file mode 100644 index 00000000..6db4ee3f --- /dev/null +++ b/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" + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/Cargo.toml b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/Cargo.toml new file mode 100644 index 00000000..6b84fe4b --- /dev/null +++ b/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" diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/error.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/error.rs new file mode 100644 index 00000000..1d8a7ab7 --- /dev/null +++ b/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 for ProgramError { + fn from(e: BlockListError) -> Self { + ProgramError::Custom(e as u32) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/block_wallet.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/block_wallet.rs new file mode 100644 index 00000000..0cdea2d0 --- /dev/null +++ b/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::(&mut data)? + }; + wallet_block.discriminator = WalletBlock::DISCRIMINATOR; + wallet_block.address = *self.wallet.key(); + + let config = unsafe { load_mut_unchecked::(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 { + let [authority, config, wallet, wallet_block, system_program] = accounts else { + return Err(BlockListError::NotEnoughAccounts); + }; + + let cfg = unsafe { load::(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, + }) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/init.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/init.rs new file mode 100644 index 00000000..f186d8d4 --- /dev/null +++ b/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 { + 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::(&mut data)? + }; + config.discriminator = Config::DISCRIMINATOR; + config.authority = *self.authority.key(); + + Ok(()) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/mod.rs new file mode 100644 index 00000000..1f2ea6b9 --- /dev/null +++ b/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::*; \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/setup_extra_metas.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/setup_extra_metas.rs new file mode 100644 index 00000000..6e9a017c --- /dev/null +++ b/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 { + 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::(&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]; + diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/tx_hook.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/tx_hook.rs new file mode 100644 index 00000000..ad960dfa --- /dev/null +++ b/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::(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::(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 { + + /* + 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, + }) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/unblock_wallet.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/instructions/unblock_wallet.rs new file mode 100644 index 00000000..c482dd5c --- /dev/null +++ b/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::(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 { + let [authority, config, wallet_block, system_program] = accounts else { + return Err(BlockListError::NotEnoughAccounts); + }; + + let cfg = unsafe { load::(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::(wallet_block.borrow_data_unchecked()).is_err() }{ + return Err(BlockListError::InvalidAccountData); + } + + Ok(Self { + authority, + config, + wallet_block, + system_program, + }) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/lib.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/lib.rs new file mode 100644 index 00000000..4efd3f60 --- /dev/null +++ b/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), + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/config.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/config.rs new file mode 100644 index 00000000..e52d2e7c --- /dev/null +++ b/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; +} + diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/mod.rs new file mode 100644 index 00000000..55e17b93 --- /dev/null +++ b/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(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(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( + 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)) +} + diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/wallet_block.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/state/wallet_block.rs new file mode 100644 index 00000000..2ae0bcab --- /dev/null +++ b/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; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/token2022_utils.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/program/src/token2022_utils.rs new file mode 100644 index 00000000..0f45d71a --- /dev/null +++ b/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) +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/readme.md b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/readme.md new file mode 100644 index 00000000..d61282c6 --- /dev/null +++ b/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 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 +``` + +Remove a wallet from the block list: +``` +target/debug/block-list-cli unblock-wallet +``` + + +### 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 +``` + +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 +``` + +## 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. \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/Cargo.toml b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/Cargo.toml new file mode 100644 index 00000000..0bb1eb28 --- /dev/null +++ b/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" \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/config.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/config.rs new file mode 100644 index 00000000..8cc23e79 --- /dev/null +++ b/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. +//! +//! +//! + +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::") + )] + 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::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 { + 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 { + 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, 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>, 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> = 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, 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>, 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> = 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 { + 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]; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/extra_metas.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/extra_metas.rs new file mode 100644 index 00000000..1ae481a1 --- /dev/null +++ b/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. +//! +//! +//! + +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::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 { + 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 { + 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, 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>, 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> = 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, 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>, 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> = 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 { + 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]; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/mod.rs new file mode 100644 index 00000000..e9b3a41c --- /dev/null +++ b/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. +//! +//! +//! + +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::*; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/wallet_block.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/accounts/wallet_block.rs new file mode 100644 index 00000000..72ad046e --- /dev/null +++ b/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. +//! +//! +//! + +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::") + )] + 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::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 { + 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 { + 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, 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>, 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> = 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, 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>, 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> = 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 { + 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]; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/errors/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/errors/mod.rs new file mode 100644 index 00000000..6172ba60 --- /dev/null +++ b/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. +//! +//! +//! diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/block_wallet.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/block_wallet.rs new file mode 100644 index 00000000..976c59d0 --- /dev/null +++ b/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. +//! +//! +//! + +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, + config: Option, + wallet: Option, + wallet_block: Option, + system_program: Option, + __remaining_accounts: Vec, +} + +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>, +} + +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, + )>, +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/init.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/init.rs new file mode 100644 index 00000000..6313c10f --- /dev/null +++ b/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. +//! +//! +//! + +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, + config: Option, + system_program: Option, + __remaining_accounts: Vec, +} + +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>, +} + +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, + )>, +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/mod.rs new file mode 100644 index 00000000..6f7ca375 --- /dev/null +++ b/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. +//! +//! +//! + +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::*; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/setup_extra_metas.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/setup_extra_metas.rs new file mode 100644 index 00000000..f884b4e3 --- /dev/null +++ b/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. +//! +//! +//! + +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, + config: Option, + mint: Option, + extra_metas: Option, + system_program: Option, + check_both_wallets: Option, + __remaining_accounts: Vec, +} + +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>, +} + +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, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )>, +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/unblock_wallet.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/instructions/unblock_wallet.rs new file mode 100644 index 00000000..e5fe721a --- /dev/null +++ b/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. +//! +//! +//! + +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, + config: Option, + wallet_block: Option, + system_program: Option, + __remaining_accounts: Vec, +} + +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>, +} + +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, + )>, +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/mod.rs new file mode 100644 index 00000000..75684016 --- /dev/null +++ b/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. +//! +//! +//! + +pub mod accounts; +pub mod errors; +pub mod instructions; +pub mod programs; +pub mod shared; + +pub(crate) use programs::*; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/programs.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/programs.rs new file mode 100644 index 00000000..50753d5d --- /dev/null +++ b/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. +//! +//! +//! + +use solana_program::{pubkey, pubkey::Pubkey}; + +/// `block_list` program ID. +pub const BLOCK_LIST_ID: Pubkey = pubkey!("BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf"); diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/shared.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/client/shared.rs new file mode 100644 index 00000000..4b9a0d5f --- /dev/null +++ b/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. +//! +//! +//! + +#[cfg(feature = "fetch")] +#[derive(Debug, Clone)] +pub struct DecodedAccount { + pub address: solana_program::pubkey::Pubkey, + pub account: solana_sdk::account::Account, + pub data: T, +} + +#[cfg(feature = "fetch")] +#[derive(Debug, Clone)] +pub enum MaybeAccount { + Exists(DecodedAccount), + NotFound(solana_program::pubkey::Pubkey), +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/lib.rs b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/lib.rs new file mode 100644 index 00000000..7c4d99ee --- /dev/null +++ b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/rust/src/lib.rs @@ -0,0 +1,2 @@ +pub mod client; +pub use client::*; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/config.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/config.ts new file mode 100644 index 00000000..3787e254 --- /dev/null +++ b/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 { + return getStructEncoder([ + ['discriminator', getU8Encoder()], + ['authority', getAddressEncoder()], + ['blockedWalletsCount', getU64Encoder()], + ]); +} + +export function getConfigDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['authority', getAddressDecoder()], + ['blockedWalletsCount', getU64Decoder()], + ]); +} + +export function getConfigCodec(): Codec { + return combineCodec(getConfigEncoder(), getConfigDecoder()); +} + +export function decodeConfig( + encodedAccount: EncodedAccount +): Account; +export function decodeConfig( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeConfig( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): Account | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getConfigDecoder() + ); +} + +export async function fetchConfig( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeConfig(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeConfig( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeConfig(maybeAccount); +} + +export async function fetchAllConfig( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeConfig(rpc, addresses, config); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeConfig( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + 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[0], + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const maybeAccount = await fetchMaybeConfigFromSeeds(rpc, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeConfigFromSeeds( + rpc: Parameters[0], + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const { programAddress, ...fetchConfig } = config; + const [address] = await findConfigPda({ programAddress }); + return await fetchMaybeConfig(rpc, address, fetchConfig); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/extraMetas.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/extraMetas.ts new file mode 100644 index 00000000..625e81cd --- /dev/null +++ b/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 { + return getStructEncoder([]); +} + +export function getExtraMetasDecoder(): Decoder { + return getStructDecoder([]); +} + +export function getExtraMetasCodec(): Codec { + return combineCodec(getExtraMetasEncoder(), getExtraMetasDecoder()); +} + +export function decodeExtraMetas( + encodedAccount: EncodedAccount +): Account; +export function decodeExtraMetas( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeExtraMetas( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): Account | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getExtraMetasDecoder() + ); +} + +export async function fetchExtraMetas( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeExtraMetas(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeExtraMetas( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeExtraMetas(maybeAccount); +} + +export async function fetchAllExtraMetas( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeExtraMetas(rpc, addresses, config); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeExtraMetas( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config); + return maybeAccounts.map((maybeAccount) => decodeExtraMetas(maybeAccount)); +} + +export async function fetchExtraMetasFromSeeds( + rpc: Parameters[0], + seeds: ExtraMetasSeeds, + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const maybeAccount = await fetchMaybeExtraMetasFromSeeds(rpc, seeds, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeExtraMetasFromSeeds( + rpc: Parameters[0], + seeds: ExtraMetasSeeds, + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const { programAddress, ...fetchConfig } = config; + const [address] = await findExtraMetasPda(seeds, { programAddress }); + return await fetchMaybeExtraMetas(rpc, address, fetchConfig); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/index.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/index.ts new file mode 100644 index 00000000..ed620eb1 --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/walletBlock.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/accounts/walletBlock.ts new file mode 100644 index 00000000..67181d56 --- /dev/null +++ b/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 { + return getStructEncoder([['authority', getAddressEncoder()]]); +} + +export function getWalletBlockDecoder(): Decoder { + return getStructDecoder([['authority', getAddressDecoder()]]); +} + +export function getWalletBlockCodec(): Codec { + return combineCodec(getWalletBlockEncoder(), getWalletBlockDecoder()); +} + +export function decodeWalletBlock( + encodedAccount: EncodedAccount +): Account; +export function decodeWalletBlock( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeWalletBlock( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): Account | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getWalletBlockDecoder() + ); +} + +export async function fetchWalletBlock( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeWalletBlock(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeWalletBlock( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeWalletBlock(maybeAccount); +} + +export async function fetchAllWalletBlock( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeWalletBlock(rpc, addresses, config); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeWalletBlock( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + 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[0], + seeds: WalletBlockSeeds, + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const maybeAccount = await fetchMaybeWalletBlockFromSeeds(rpc, seeds, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeWalletBlockFromSeeds( + rpc: Parameters[0], + seeds: WalletBlockSeeds, + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const { programAddress, ...fetchConfig } = config; + const [address] = await findWalletBlockPda(seeds, { programAddress }); + return await fetchMaybeWalletBlock(rpc, address, fetchConfig); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/index.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/index.ts new file mode 100644 index 00000000..1002b820 --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/blockWallet.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/blockWallet.ts new file mode 100644 index 00000000..8a9c1aa4 --- /dev/null +++ b/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, + TAccountConfig extends string | IAccountMeta = string, + TAccountWallet extends string | IAccountMeta = string, + TAccountWalletBlock extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountAuthority extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority, + TAccountConfig extends string + ? WritableAccount + : TAccountConfig, + TAccountWallet extends string + ? ReadonlyAccount + : TAccountWallet, + TAccountWalletBlock extends string + ? WritableAccount + : TAccountWalletBlock, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type BlockWalletInstructionData = { discriminator: number }; + +export type BlockWalletInstructionDataArgs = {}; + +export function getBlockWalletInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 242 }) + ); +} + +export function getBlockWalletInstructionDataDecoder(): Decoder { + 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; + config?: Address; + wallet: Address; + walletBlock: Address; + systemProgram?: Address; +}; + +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; + config: Address; + wallet: Address; + walletBlock: Address; + systemProgram?: Address; +}; + +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; + 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 & + IInstructionWithAccounts & + IInstructionWithData +): ParsedBlockWalletInstruction { + 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), + }; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/index.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/index.ts new file mode 100644 index 00000000..864cb10b --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/init.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/init.ts new file mode 100644 index 00000000..c067c6ae --- /dev/null +++ b/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, + TAccountConfig extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountAuthority extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority, + TAccountConfig extends string + ? WritableAccount + : TAccountConfig, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type InitInstructionData = { discriminator: number }; + +export type InitInstructionDataArgs = {}; + +export function getInitInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 241 }) + ); +} + +export function getInitInstructionDataDecoder(): Decoder { + 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; + config?: Address; + systemProgram?: Address; +}; + +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; + config: Address; + systemProgram?: Address; +}; + +export function getInitInstruction< + TAccountAuthority extends string, + TAccountConfig extends string, + TAccountSystemProgram extends string, + TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS, +>( + input: InitInput, + 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; + accounts: { + authority: TAccountMetas[0]; + config: TAccountMetas[1]; + systemProgram: TAccountMetas[2]; + }; + data: InitInstructionData; +}; + +export function parseInitInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedInitInstruction { + 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), + }; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/setupExtraMetas.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/setupExtraMetas.ts new file mode 100644 index 00000000..92d5eaa2 --- /dev/null +++ b/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, + TAccountConfig extends string | IAccountMeta = string, + TAccountMint extends string | IAccountMeta = string, + TAccountExtraMetas extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountAuthority extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority, + TAccountConfig extends string + ? ReadonlyAccount + : TAccountConfig, + TAccountMint extends string + ? ReadonlyAccount + : TAccountMint, + TAccountExtraMetas extends string + ? WritableAccount + : TAccountExtraMetas, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type SetupExtraMetasInstructionData = { + discriminator: number; + checkBothWallets: boolean; +}; + +export type SetupExtraMetasInstructionDataArgs = { checkBothWallets?: boolean }; + +export function getSetupExtraMetasInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['checkBothWallets', getBooleanEncoder()], + ]), + (value) => ({ + ...value, + discriminator: 106, + checkBothWallets: value.checkBothWallets ?? false, + }) + ); +} + +export function getSetupExtraMetasInstructionDataDecoder(): Decoder { + 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; + config?: Address; + mint: Address; + extraMetas?: Address; + systemProgram?: Address; + 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; + config: Address; + mint: Address; + extraMetas: Address; + systemProgram?: Address; + 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; + 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 & + IInstructionWithAccounts & + IInstructionWithData +): ParsedSetupExtraMetasInstruction { + 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), + }; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/unblockWallet.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/instructions/unblockWallet.ts new file mode 100644 index 00000000..0d1eb1a3 --- /dev/null +++ b/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, + TAccountConfig extends string | IAccountMeta = string, + TAccountWalletBlock extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountAuthority extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority, + TAccountConfig extends string + ? WritableAccount + : TAccountConfig, + TAccountWalletBlock extends string + ? WritableAccount + : TAccountWalletBlock, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type UnblockWalletInstructionData = { discriminator: number }; + +export type UnblockWalletInstructionDataArgs = {}; + +export function getUnblockWalletInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 243 }) + ); +} + +export function getUnblockWalletInstructionDataDecoder(): Decoder { + 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; + config?: Address; + walletBlock: Address; + systemProgram?: Address; +}; + +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; + config: Address; + walletBlock: Address; + systemProgram?: Address; +}; + +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; + 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 & + IInstructionWithAccounts & + IInstructionWithData +): ParsedUnblockWalletInstruction { + 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), + }; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/config.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/config.ts new file mode 100644 index 00000000..831ec071 --- /dev/null +++ b/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 { + const { + programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>, + } = config; + return await getProgramDerivedAddress({ + programAddress, + seeds: [getUtf8Encoder().encode('config')], + }); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/extraMetas.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/extraMetas.ts new file mode 100644 index 00000000..6acdc5da --- /dev/null +++ b/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 { + const { + programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>, + } = config; + return await getProgramDerivedAddress({ + programAddress, + seeds: [ + getUtf8Encoder().encode('extra-account-metas'), + getAddressEncoder().encode(seeds.mint), + ], + }); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/index.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/index.ts new file mode 100644 index 00000000..ed620eb1 --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/walletBlock.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/pdas/walletBlock.ts new file mode 100644 index 00000000..44db9656 --- /dev/null +++ b/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 { + const { + programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>, + } = config; + return await getProgramDerivedAddress({ + programAddress, + seeds: [ + getUtf8Encoder().encode('wallet_block'), + getAddressEncoder().encode(seeds.wallet), + ], + }); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/programs/blockList.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/programs/blockList.ts new file mode 100644 index 00000000..aea17cf5 --- /dev/null +++ b/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) + | ({ + instructionType: BlockListInstruction.BlockWallet; + } & ParsedBlockWalletInstruction) + | ({ + instructionType: BlockListInstruction.UnblockWallet; + } & ParsedUnblockWalletInstruction) + | ({ + instructionType: BlockListInstruction.SetupExtraMetas; + } & ParsedSetupExtraMetasInstruction); diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/programs/index.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/programs/index.ts new file mode 100644 index 00000000..f945615b --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/shared/index.ts b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/sdk/ts/src/shared/index.ts new file mode 100644 index 00000000..7ba90539 --- /dev/null +++ b/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(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( + value: + | Address + | ProgramDerivedAddress + | TransactionSigner + | null + | undefined +): Address { + 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; +} + +/** + * Asserts that the given value is a PDA. + * @internal + */ +export function expectProgramDerivedAddress( + value: + | Address + | ProgramDerivedAddress + | TransactionSigner + | null + | undefined +): ProgramDerivedAddress { + 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( + value: + | Address + | ProgramDerivedAddress + | TransactionSigner + | null + | undefined +): TransactionSigner { + 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 + | ProgramDerivedAddress + | TransactionSigner + | null = + | Address + | ProgramDerivedAddress + | TransactionSigner + | 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( + value: + | Address + | ProgramDerivedAddress + | TransactionSigner +): value is TransactionSigner { + return ( + !!value && + typeof value === 'object' && + 'address' in value && + kitIsTransactionSigner(value) + ); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/pinocchio/tsconfig.json b/tokens/token-2022/transfer-hook/pblock-list/pinocchio/tsconfig.json new file mode 100644 index 00000000..acf5eb28 --- /dev/null +++ b/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"] +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/Cargo.toml b/tokens/token-2022/transfer-hook/pblock-list/program/Cargo.toml new file mode 100644 index 00000000..6b84fe4b --- /dev/null +++ b/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" diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/error.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/error.rs new file mode 100644 index 00000000..1d8a7ab7 --- /dev/null +++ b/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 for ProgramError { + fn from(e: BlockListError) -> Self { + ProgramError::Custom(e as u32) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/block_wallet.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/block_wallet.rs new file mode 100644 index 00000000..0cdea2d0 --- /dev/null +++ b/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::(&mut data)? + }; + wallet_block.discriminator = WalletBlock::DISCRIMINATOR; + wallet_block.address = *self.wallet.key(); + + let config = unsafe { load_mut_unchecked::(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 { + let [authority, config, wallet, wallet_block, system_program] = accounts else { + return Err(BlockListError::NotEnoughAccounts); + }; + + let cfg = unsafe { load::(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, + }) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/init.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/init.rs new file mode 100644 index 00000000..f186d8d4 --- /dev/null +++ b/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 { + 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::(&mut data)? + }; + config.discriminator = Config::DISCRIMINATOR; + config.authority = *self.authority.key(); + + Ok(()) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/mod.rs new file mode 100644 index 00000000..1f2ea6b9 --- /dev/null +++ b/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::*; \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/setup_extra_metas.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/setup_extra_metas.rs new file mode 100644 index 00000000..6e9a017c --- /dev/null +++ b/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 { + 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::(&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]; + diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/tx_hook.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/tx_hook.rs new file mode 100644 index 00000000..ad960dfa --- /dev/null +++ b/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::(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::(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 { + + /* + 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, + }) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/unblock_wallet.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/instructions/unblock_wallet.rs new file mode 100644 index 00000000..c482dd5c --- /dev/null +++ b/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::(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 { + let [authority, config, wallet_block, system_program] = accounts else { + return Err(BlockListError::NotEnoughAccounts); + }; + + let cfg = unsafe { load::(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::(wallet_block.borrow_data_unchecked()).is_err() }{ + return Err(BlockListError::InvalidAccountData); + } + + Ok(Self { + authority, + config, + wallet_block, + system_program, + }) + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/lib.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/lib.rs new file mode 100644 index 00000000..4efd3f60 --- /dev/null +++ b/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), + } +} \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/state/config.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/state/config.rs new file mode 100644 index 00000000..e52d2e7c --- /dev/null +++ b/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; +} + diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/state/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/state/mod.rs new file mode 100644 index 00000000..55e17b93 --- /dev/null +++ b/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(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(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( + 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)) +} + diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/state/wallet_block.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/state/wallet_block.rs new file mode 100644 index 00000000..2ae0bcab --- /dev/null +++ b/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; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/program/src/token2022_utils.rs b/tokens/token-2022/transfer-hook/pblock-list/program/src/token2022_utils.rs new file mode 100644 index 00000000..0f45d71a --- /dev/null +++ b/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) +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/readme.md b/tokens/token-2022/transfer-hook/pblock-list/readme.md new file mode 100644 index 00000000..d61282c6 --- /dev/null +++ b/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 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 +``` + +Remove a wallet from the block list: +``` +target/debug/block-list-cli unblock-wallet +``` + + +### 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 +``` + +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 +``` + +## 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. \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/Cargo.toml b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/Cargo.toml new file mode 100644 index 00000000..0bb1eb28 --- /dev/null +++ b/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" \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/config.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/config.rs new file mode 100644 index 00000000..8cc23e79 --- /dev/null +++ b/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. +//! +//! +//! + +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::") + )] + 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::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 { + 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 { + 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, 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>, 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> = 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, 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>, 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> = 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 { + 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]; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/extra_metas.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/extra_metas.rs new file mode 100644 index 00000000..1ae481a1 --- /dev/null +++ b/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. +//! +//! +//! + +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::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 { + 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 { + 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, 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>, 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> = 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, 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>, 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> = 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 { + 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]; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/mod.rs new file mode 100644 index 00000000..e9b3a41c --- /dev/null +++ b/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. +//! +//! +//! + +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::*; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/wallet_block.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/accounts/wallet_block.rs new file mode 100644 index 00000000..72ad046e --- /dev/null +++ b/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. +//! +//! +//! + +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::") + )] + 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::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 { + 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 { + 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, 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>, 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> = 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, 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>, 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> = 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 { + 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]; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/errors/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/errors/mod.rs new file mode 100644 index 00000000..6172ba60 --- /dev/null +++ b/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. +//! +//! +//! diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/block_wallet.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/block_wallet.rs new file mode 100644 index 00000000..976c59d0 --- /dev/null +++ b/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. +//! +//! +//! + +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, + config: Option, + wallet: Option, + wallet_block: Option, + system_program: Option, + __remaining_accounts: Vec, +} + +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>, +} + +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, + )>, +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/init.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/init.rs new file mode 100644 index 00000000..6313c10f --- /dev/null +++ b/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. +//! +//! +//! + +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, + config: Option, + system_program: Option, + __remaining_accounts: Vec, +} + +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>, +} + +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, + )>, +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/mod.rs new file mode 100644 index 00000000..6f7ca375 --- /dev/null +++ b/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. +//! +//! +//! + +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::*; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/setup_extra_metas.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/setup_extra_metas.rs new file mode 100644 index 00000000..f884b4e3 --- /dev/null +++ b/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. +//! +//! +//! + +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, + config: Option, + mint: Option, + extra_metas: Option, + system_program: Option, + check_both_wallets: Option, + __remaining_accounts: Vec, +} + +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>, +} + +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, + /// Additional instruction accounts `(AccountInfo, is_writable, is_signer)`. + __remaining_accounts: Vec<( + &'b solana_program::account_info::AccountInfo<'a>, + bool, + bool, + )>, +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/unblock_wallet.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/instructions/unblock_wallet.rs new file mode 100644 index 00000000..e5fe721a --- /dev/null +++ b/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. +//! +//! +//! + +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, + config: Option, + wallet_block: Option, + system_program: Option, + __remaining_accounts: Vec, +} + +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>, +} + +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, + )>, +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/mod.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/mod.rs new file mode 100644 index 00000000..75684016 --- /dev/null +++ b/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. +//! +//! +//! + +pub mod accounts; +pub mod errors; +pub mod instructions; +pub mod programs; +pub mod shared; + +pub(crate) use programs::*; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/programs.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/programs.rs new file mode 100644 index 00000000..50753d5d --- /dev/null +++ b/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. +//! +//! +//! + +use solana_program::{pubkey, pubkey::Pubkey}; + +/// `block_list` program ID. +pub const BLOCK_LIST_ID: Pubkey = pubkey!("BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf"); diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/shared.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/client/shared.rs new file mode 100644 index 00000000..4b9a0d5f --- /dev/null +++ b/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. +//! +//! +//! + +#[cfg(feature = "fetch")] +#[derive(Debug, Clone)] +pub struct DecodedAccount { + pub address: solana_program::pubkey::Pubkey, + pub account: solana_sdk::account::Account, + pub data: T, +} + +#[cfg(feature = "fetch")] +#[derive(Debug, Clone)] +pub enum MaybeAccount { + Exists(DecodedAccount), + NotFound(solana_program::pubkey::Pubkey), +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/lib.rs b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/lib.rs new file mode 100644 index 00000000..7c4d99ee --- /dev/null +++ b/tokens/token-2022/transfer-hook/pblock-list/sdk/rust/src/lib.rs @@ -0,0 +1,2 @@ +pub mod client; +pub use client::*; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/config.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/config.ts new file mode 100644 index 00000000..3787e254 --- /dev/null +++ b/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 { + return getStructEncoder([ + ['discriminator', getU8Encoder()], + ['authority', getAddressEncoder()], + ['blockedWalletsCount', getU64Encoder()], + ]); +} + +export function getConfigDecoder(): Decoder { + return getStructDecoder([ + ['discriminator', getU8Decoder()], + ['authority', getAddressDecoder()], + ['blockedWalletsCount', getU64Decoder()], + ]); +} + +export function getConfigCodec(): Codec { + return combineCodec(getConfigEncoder(), getConfigDecoder()); +} + +export function decodeConfig( + encodedAccount: EncodedAccount +): Account; +export function decodeConfig( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeConfig( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): Account | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getConfigDecoder() + ); +} + +export async function fetchConfig( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeConfig(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeConfig( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeConfig(maybeAccount); +} + +export async function fetchAllConfig( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeConfig(rpc, addresses, config); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeConfig( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + 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[0], + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const maybeAccount = await fetchMaybeConfigFromSeeds(rpc, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeConfigFromSeeds( + rpc: Parameters[0], + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const { programAddress, ...fetchConfig } = config; + const [address] = await findConfigPda({ programAddress }); + return await fetchMaybeConfig(rpc, address, fetchConfig); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/extraMetas.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/extraMetas.ts new file mode 100644 index 00000000..625e81cd --- /dev/null +++ b/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 { + return getStructEncoder([]); +} + +export function getExtraMetasDecoder(): Decoder { + return getStructDecoder([]); +} + +export function getExtraMetasCodec(): Codec { + return combineCodec(getExtraMetasEncoder(), getExtraMetasDecoder()); +} + +export function decodeExtraMetas( + encodedAccount: EncodedAccount +): Account; +export function decodeExtraMetas( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeExtraMetas( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): Account | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getExtraMetasDecoder() + ); +} + +export async function fetchExtraMetas( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeExtraMetas(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeExtraMetas( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeExtraMetas(maybeAccount); +} + +export async function fetchAllExtraMetas( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeExtraMetas(rpc, addresses, config); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeExtraMetas( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchEncodedAccounts(rpc, addresses, config); + return maybeAccounts.map((maybeAccount) => decodeExtraMetas(maybeAccount)); +} + +export async function fetchExtraMetasFromSeeds( + rpc: Parameters[0], + seeds: ExtraMetasSeeds, + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const maybeAccount = await fetchMaybeExtraMetasFromSeeds(rpc, seeds, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeExtraMetasFromSeeds( + rpc: Parameters[0], + seeds: ExtraMetasSeeds, + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const { programAddress, ...fetchConfig } = config; + const [address] = await findExtraMetasPda(seeds, { programAddress }); + return await fetchMaybeExtraMetas(rpc, address, fetchConfig); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/index.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/index.ts new file mode 100644 index 00000000..ed620eb1 --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/walletBlock.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/accounts/walletBlock.ts new file mode 100644 index 00000000..67181d56 --- /dev/null +++ b/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 { + return getStructEncoder([['authority', getAddressEncoder()]]); +} + +export function getWalletBlockDecoder(): Decoder { + return getStructDecoder([['authority', getAddressDecoder()]]); +} + +export function getWalletBlockCodec(): Codec { + return combineCodec(getWalletBlockEncoder(), getWalletBlockDecoder()); +} + +export function decodeWalletBlock( + encodedAccount: EncodedAccount +): Account; +export function decodeWalletBlock( + encodedAccount: MaybeEncodedAccount +): MaybeAccount; +export function decodeWalletBlock( + encodedAccount: EncodedAccount | MaybeEncodedAccount +): Account | MaybeAccount { + return decodeAccount( + encodedAccount as MaybeEncodedAccount, + getWalletBlockDecoder() + ); +} + +export async function fetchWalletBlock( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchMaybeWalletBlock(rpc, address, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeWalletBlock( + rpc: Parameters[0], + address: Address, + config?: FetchAccountConfig +): Promise> { + const maybeAccount = await fetchEncodedAccount(rpc, address, config); + return decodeWalletBlock(maybeAccount); +} + +export async function fetchAllWalletBlock( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + const maybeAccounts = await fetchAllMaybeWalletBlock(rpc, addresses, config); + assertAccountsExist(maybeAccounts); + return maybeAccounts; +} + +export async function fetchAllMaybeWalletBlock( + rpc: Parameters[0], + addresses: Array
, + config?: FetchAccountsConfig +): Promise[]> { + 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[0], + seeds: WalletBlockSeeds, + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const maybeAccount = await fetchMaybeWalletBlockFromSeeds(rpc, seeds, config); + assertAccountExists(maybeAccount); + return maybeAccount; +} + +export async function fetchMaybeWalletBlockFromSeeds( + rpc: Parameters[0], + seeds: WalletBlockSeeds, + config: FetchAccountConfig & { programAddress?: Address } = {} +): Promise> { + const { programAddress, ...fetchConfig } = config; + const [address] = await findWalletBlockPda(seeds, { programAddress }); + return await fetchMaybeWalletBlock(rpc, address, fetchConfig); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/index.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/index.ts new file mode 100644 index 00000000..1002b820 --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/blockWallet.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/blockWallet.ts new file mode 100644 index 00000000..8a9c1aa4 --- /dev/null +++ b/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, + TAccountConfig extends string | IAccountMeta = string, + TAccountWallet extends string | IAccountMeta = string, + TAccountWalletBlock extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountAuthority extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority, + TAccountConfig extends string + ? WritableAccount + : TAccountConfig, + TAccountWallet extends string + ? ReadonlyAccount + : TAccountWallet, + TAccountWalletBlock extends string + ? WritableAccount + : TAccountWalletBlock, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type BlockWalletInstructionData = { discriminator: number }; + +export type BlockWalletInstructionDataArgs = {}; + +export function getBlockWalletInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 242 }) + ); +} + +export function getBlockWalletInstructionDataDecoder(): Decoder { + 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; + config?: Address; + wallet: Address; + walletBlock: Address; + systemProgram?: Address; +}; + +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; + config: Address; + wallet: Address; + walletBlock: Address; + systemProgram?: Address; +}; + +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; + 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 & + IInstructionWithAccounts & + IInstructionWithData +): ParsedBlockWalletInstruction { + 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), + }; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/index.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/index.ts new file mode 100644 index 00000000..864cb10b --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/init.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/init.ts new file mode 100644 index 00000000..c067c6ae --- /dev/null +++ b/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, + TAccountConfig extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountAuthority extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority, + TAccountConfig extends string + ? WritableAccount + : TAccountConfig, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type InitInstructionData = { discriminator: number }; + +export type InitInstructionDataArgs = {}; + +export function getInitInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 241 }) + ); +} + +export function getInitInstructionDataDecoder(): Decoder { + 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; + config?: Address; + systemProgram?: Address; +}; + +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; + config: Address; + systemProgram?: Address; +}; + +export function getInitInstruction< + TAccountAuthority extends string, + TAccountConfig extends string, + TAccountSystemProgram extends string, + TProgramAddress extends Address = typeof BLOCK_LIST_PROGRAM_ADDRESS, +>( + input: InitInput, + 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; + accounts: { + authority: TAccountMetas[0]; + config: TAccountMetas[1]; + systemProgram: TAccountMetas[2]; + }; + data: InitInstructionData; +}; + +export function parseInitInstruction< + TProgram extends string, + TAccountMetas extends readonly IAccountMeta[], +>( + instruction: IInstruction & + IInstructionWithAccounts & + IInstructionWithData +): ParsedInitInstruction { + 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), + }; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/setupExtraMetas.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/setupExtraMetas.ts new file mode 100644 index 00000000..92d5eaa2 --- /dev/null +++ b/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, + TAccountConfig extends string | IAccountMeta = string, + TAccountMint extends string | IAccountMeta = string, + TAccountExtraMetas extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountAuthority extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority, + TAccountConfig extends string + ? ReadonlyAccount + : TAccountConfig, + TAccountMint extends string + ? ReadonlyAccount + : TAccountMint, + TAccountExtraMetas extends string + ? WritableAccount + : TAccountExtraMetas, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type SetupExtraMetasInstructionData = { + discriminator: number; + checkBothWallets: boolean; +}; + +export type SetupExtraMetasInstructionDataArgs = { checkBothWallets?: boolean }; + +export function getSetupExtraMetasInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([ + ['discriminator', getU8Encoder()], + ['checkBothWallets', getBooleanEncoder()], + ]), + (value) => ({ + ...value, + discriminator: 106, + checkBothWallets: value.checkBothWallets ?? false, + }) + ); +} + +export function getSetupExtraMetasInstructionDataDecoder(): Decoder { + 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; + config?: Address; + mint: Address; + extraMetas?: Address; + systemProgram?: Address; + 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; + config: Address; + mint: Address; + extraMetas: Address; + systemProgram?: Address; + 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; + 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 & + IInstructionWithAccounts & + IInstructionWithData +): ParsedSetupExtraMetasInstruction { + 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), + }; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/unblockWallet.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/instructions/unblockWallet.ts new file mode 100644 index 00000000..0d1eb1a3 --- /dev/null +++ b/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, + TAccountConfig extends string | IAccountMeta = string, + TAccountWalletBlock extends string | IAccountMeta = string, + TAccountSystemProgram extends + | string + | IAccountMeta = '11111111111111111111111111111111', + TRemainingAccounts extends readonly IAccountMeta[] = [], +> = IInstruction & + IInstructionWithData & + IInstructionWithAccounts< + [ + TAccountAuthority extends string + ? WritableSignerAccount & + IAccountSignerMeta + : TAccountAuthority, + TAccountConfig extends string + ? WritableAccount + : TAccountConfig, + TAccountWalletBlock extends string + ? WritableAccount + : TAccountWalletBlock, + TAccountSystemProgram extends string + ? ReadonlyAccount + : TAccountSystemProgram, + ...TRemainingAccounts, + ] + >; + +export type UnblockWalletInstructionData = { discriminator: number }; + +export type UnblockWalletInstructionDataArgs = {}; + +export function getUnblockWalletInstructionDataEncoder(): Encoder { + return transformEncoder( + getStructEncoder([['discriminator', getU8Encoder()]]), + (value) => ({ ...value, discriminator: 243 }) + ); +} + +export function getUnblockWalletInstructionDataDecoder(): Decoder { + 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; + config?: Address; + walletBlock: Address; + systemProgram?: Address; +}; + +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; + config: Address; + walletBlock: Address; + systemProgram?: Address; +}; + +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; + 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 & + IInstructionWithAccounts & + IInstructionWithData +): ParsedUnblockWalletInstruction { + 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), + }; +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/config.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/config.ts new file mode 100644 index 00000000..831ec071 --- /dev/null +++ b/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 { + const { + programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>, + } = config; + return await getProgramDerivedAddress({ + programAddress, + seeds: [getUtf8Encoder().encode('config')], + }); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/extraMetas.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/extraMetas.ts new file mode 100644 index 00000000..6acdc5da --- /dev/null +++ b/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 { + const { + programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>, + } = config; + return await getProgramDerivedAddress({ + programAddress, + seeds: [ + getUtf8Encoder().encode('extra-account-metas'), + getAddressEncoder().encode(seeds.mint), + ], + }); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/index.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/index.ts new file mode 100644 index 00000000..ed620eb1 --- /dev/null +++ b/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/walletBlock.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/pdas/walletBlock.ts new file mode 100644 index 00000000..44db9656 --- /dev/null +++ b/tokens/token-2022/transfer-hook/pblock-list/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 { + const { + programAddress = 'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf' as Address<'BLoCKLSG2qMQ9YxEyrrKKAQzthvW4Lu8Eyv74axF6mf'>, + } = config; + return await getProgramDerivedAddress({ + programAddress, + seeds: [ + getUtf8Encoder().encode('wallet_block'), + getAddressEncoder().encode(seeds.wallet), + ], + }); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/programs/blockList.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/programs/blockList.ts new file mode 100644 index 00000000..aea17cf5 --- /dev/null +++ b/tokens/token-2022/transfer-hook/pblock-list/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) + | ({ + instructionType: BlockListInstruction.BlockWallet; + } & ParsedBlockWalletInstruction) + | ({ + instructionType: BlockListInstruction.UnblockWallet; + } & ParsedUnblockWalletInstruction) + | ({ + instructionType: BlockListInstruction.SetupExtraMetas; + } & ParsedSetupExtraMetasInstruction); diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/programs/index.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/programs/index.ts new file mode 100644 index 00000000..f945615b --- /dev/null +++ b/tokens/token-2022/transfer-hook/pblock-list/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'; diff --git a/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/shared/index.ts b/tokens/token-2022/transfer-hook/pblock-list/sdk/ts/src/shared/index.ts new file mode 100644 index 00000000..7ba90539 --- /dev/null +++ b/tokens/token-2022/transfer-hook/pblock-list/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(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( + value: + | Address + | ProgramDerivedAddress + | TransactionSigner + | null + | undefined +): Address { + 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; +} + +/** + * Asserts that the given value is a PDA. + * @internal + */ +export function expectProgramDerivedAddress( + value: + | Address + | ProgramDerivedAddress + | TransactionSigner + | null + | undefined +): ProgramDerivedAddress { + 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( + value: + | Address + | ProgramDerivedAddress + | TransactionSigner + | null + | undefined +): TransactionSigner { + 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 + | ProgramDerivedAddress + | TransactionSigner + | null = + | Address + | ProgramDerivedAddress + | TransactionSigner + | 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( + value: + | Address + | ProgramDerivedAddress + | TransactionSigner +): value is TransactionSigner { + return ( + !!value && + typeof value === 'object' && + 'address' in value && + kitIsTransactionSigner(value) + ); +} diff --git a/tokens/token-2022/transfer-hook/pblock-list/tsconfig.json b/tokens/token-2022/transfer-hook/pblock-list/tsconfig.json new file mode 100644 index 00000000..acf5eb28 --- /dev/null +++ b/tokens/token-2022/transfer-hook/pblock-list/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"] +} \ No newline at end of file