diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/.gitignore b/tokens/token-2022/transfer-hook/allow-block-list-token/.gitignore index d49b3971..5575c562 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/.gitignore +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/.gitignore @@ -41,4 +41,4 @@ yarn-error.log* next-env.d.ts # Anchor -/anchor/target/ +/anchor/target/ \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/README.md b/tokens/token-2022/transfer-hook/allow-block-list-token/README.md index cd2d4047..bb499df4 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/README.md +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/README.md @@ -9,6 +9,7 @@ The allow/block list is then consumed by a transfer-hook. The list is managed by a single authority and can be used by several token mints. This enables a separation of concerns between token management and allow/block list management, ideal for scenarios where an issuer wants a 3rd party managed allow/block list or wants to share the same list across a group of assets. + Initializes new tokens with several configuration options: - Permanent delegate - Allow list @@ -30,7 +31,7 @@ This repo includes a UI to manage the allow/block list based on the `legacy-next Install dependencies: `yarn install` -Compile the program: +Compile the program (make sure to replace your program ID): `anchor build` Compile the UI: @@ -43,8 +44,8 @@ Serve the UI: There are a couple scripts to manage the local validator and deployment. -To start the local validator and deploy the program: +To start the local validator and deploy the program (uses the anchor CLI and default anchor keypair): `./scripts/start.sh` To stop the local validator: -`./scripts/stop.sh` +`./scripts/stop.sh` \ No newline at end of file diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/Anchor.toml b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/Anchor.toml index 7d418ebf..3de1ef86 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/Anchor.toml +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/Anchor.toml @@ -6,7 +6,7 @@ resolution = true skip-lint = false [programs.localnet] -abl-token = "LtkoMwPSKxAE714EY3V1oAEQ5LciqJcRwQQuQnzEhQQ" +abl-token = "EYBRvArz4kb5YLtzjD4TW6DbWhS8qjcMYqBU4wHLW3qj" [registry] url = "https://api.apr.dev" diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/constants.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/constants.rs index d7cf5b48..e098e7e6 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/constants.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/constants.rs @@ -1,4 +1,3 @@ - pub const META_LIST_ACCOUNT_SEED: &[u8] = b"extra-account-metas"; pub const CONFIG_SEED: &[u8] = b"config"; -pub const AB_WALLET_SEED: &[u8] = b"ab_wallet"; \ No newline at end of file +pub const AB_WALLET_SEED: &[u8] = b"ab_wallet"; diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/errors.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/errors.rs index 7c7f499a..baabd552 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/errors.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/errors.rs @@ -13,4 +13,4 @@ pub enum ABListError { #[msg("Wallet blocked")] WalletBlocked, -} \ No newline at end of file +} diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/attach_to_mint.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/attach_to_mint.rs index 2cb0c913..a6dc4267 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/attach_to_mint.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/attach_to_mint.rs @@ -1,27 +1,19 @@ -use anchor_lang::{prelude::*, solana_program::program::invoke, solana_program::system_instruction::transfer}; +use anchor_lang::prelude::*; use anchor_spl::{ - token_2022::{spl_token_2022::{extension::{BaseStateWithExtensions, StateWithExtensions}, state::Mint as Mint2022}, Token2022}, - token_interface::{spl_token_metadata_interface::state::{Field, TokenMetadata}, token_metadata_initialize, token_metadata_update_field, Mint, TokenMetadataInitialize, TokenMetadataUpdateField}, + token_2022::Token2022, + token_interface::{transfer_hook_update, Mint, TransferHookUpdate}, }; -use spl_tlv_account_resolution::{ - state::ExtraAccountMetaList, -}; +use spl_tlv_account_resolution::state::ExtraAccountMetaList; use spl_transfer_hook_interface::instruction::ExecuteInstruction; -use crate::{Mode, META_LIST_ACCOUNT_SEED, get_extra_account_metas, get_meta_list_size}; - +use crate::{get_extra_account_metas, get_meta_list_size, META_LIST_ACCOUNT_SEED}; #[derive(Accounts)] -#[instruction(args: AttachToMintArgs)] pub struct AttachToMint<'info> { #[account(mut)] pub payer: Signer<'info>, - pub mint_authority: Signer<'info>, - - pub metadata_authority: Signer<'info>, - #[account( mut, mint::token_program = token_program, @@ -44,78 +36,16 @@ pub struct AttachToMint<'info> { } impl AttachToMint<'_> { - pub fn attach_to_mint(&mut self, args: AttachToMintArgs) -> Result<()> { - let mint_info = self.mint.to_account_info(); - let mint_data = mint_info.data.borrow(); - let mint = StateWithExtensions::::unpack(&mint_data)?; - - let metadata = mint.get_variable_len_extension::(); - - if metadata.is_err() { - // assume metadata is not initialized, so we need to initialize it - - let cpi_accounts = TokenMetadataInitialize { - program_id: self.token_program.to_account_info(), - mint: self.mint.to_account_info(), - metadata: self.mint.to_account_info(), // metadata account is the mint, since data is stored in mint - mint_authority: self.mint_authority.to_account_info(), - update_authority: self.metadata_authority.to_account_info(), - }; - let cpi_ctx = CpiContext::new( - self.token_program.to_account_info(), - cpi_accounts, - ); - token_metadata_initialize(cpi_ctx, args.name.unwrap(), args.symbol.unwrap(), args.uri.unwrap())?; - } - - let cpi_accounts = TokenMetadataUpdateField { - metadata: self.mint.to_account_info(), - update_authority: self.metadata_authority.to_account_info(), - program_id: self.token_program.to_account_info(), + pub fn attach_to_mint(&mut self) -> Result<()> { + let tx_hook_accs = TransferHookUpdate { + token_program_id: self.token_program.to_account_info(), + mint: self.mint.to_account_info(), + authority: self.payer.to_account_info(), }; - let cpi_ctx = CpiContext::new( - self.token_program.to_account_info(), - cpi_accounts, - ); + let ctx = CpiContext::new(self.token_program.to_account_info(), tx_hook_accs); - token_metadata_update_field(cpi_ctx, Field::Key("AB".to_string()), args.mode.to_string())?; - - if args.mode == Mode::Mixed { - let cpi_accounts = TokenMetadataUpdateField { - metadata: self.mint.to_account_info(), - update_authority: self.metadata_authority.to_account_info(), - program_id: self.token_program.to_account_info(), - }; - let cpi_ctx = CpiContext::new( - self.token_program.to_account_info(), - cpi_accounts, - ); - - token_metadata_update_field( - cpi_ctx, - Field::Key("threshold".to_string()), - args.threshold.to_string(), - )?; - } - - - let data = self.mint.to_account_info().data_len(); - let min_balance = Rent::get()?.minimum_balance(data); - if min_balance > self.mint.to_account_info().get_lamports() { - invoke( - &transfer( - &self.payer.key(), - &self.mint.to_account_info().key(), - min_balance - self.mint.to_account_info().get_lamports(), - ), - &[ - self.payer.to_account_info(), - self.mint.to_account_info(), - self.system_program.to_account_info(), - ], - )?; - } + transfer_hook_update(ctx, Some(crate::ID_CONST))?; // initialize the extra metas account let extra_metas_account = &self.extra_metas_account; @@ -124,16 +54,5 @@ impl AttachToMint<'_> { ExtraAccountMetaList::init::(&mut data, &metas)?; Ok(()) - } } - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct AttachToMintArgs { - pub name: Option, - pub symbol: Option, - pub uri: Option, - pub mode: Mode, - pub threshold: u64, -} - diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/change_mode.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/change_mode.rs index 2b6d036f..1f790176 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/change_mode.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/change_mode.rs @@ -1,17 +1,15 @@ -use anchor_lang::{prelude::*, solana_program::system_instruction::transfer }; use anchor_lang::solana_program::program::invoke; +use anchor_lang::{prelude::*, solana_program::system_instruction::transfer}; use anchor_spl::token_interface::spl_token_metadata_interface::state::TokenMetadata; use anchor_spl::{ token_2022::{ spl_token_2022::extension::{BaseStateWithExtensions, StateWithExtensions}, spl_token_2022::state::Mint, Token2022, - }, token_interface::{ - Mint as MintAccount, spl_token_metadata_interface::state::Field, token_metadata_update_field, - TokenMetadataUpdateField, + Mint as MintAccount, TokenMetadataUpdateField, }, }; @@ -21,7 +19,7 @@ use crate::Mode; pub struct ChangeMode<'info> { #[account(mut)] pub authority: Signer<'info>, - + #[account( mut, mint::token_program = token_program, @@ -73,8 +71,6 @@ impl ChangeMode<'_> { )?; } - - let data = self.mint.to_account_info().data_len(); let min_balance = Rent::get()?.minimum_balance(data); if min_balance > self.mint.to_account_info().get_lamports() { diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/init_config.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/init_config.rs index 2decd9af..cebbdeee 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/init_config.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/init_config.rs @@ -1,6 +1,5 @@ -use anchor_lang::prelude::*; use crate::{Config, CONFIG_SEED}; - +use anchor_lang::prelude::*; #[derive(Accounts)] pub struct InitConfig<'info> { @@ -21,7 +20,6 @@ pub struct InitConfig<'info> { impl InitConfig<'_> { pub fn init_config(&mut self, config_bump: u8) -> Result<()> { - self.config.set_inner(Config { authority: self.payer.key(), bump: config_bump, diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/init_mint.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/init_mint.rs index c0a03531..445dd03c 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/init_mint.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/init_mint.rs @@ -9,14 +9,11 @@ use anchor_spl::{ }, }; -use spl_tlv_account_resolution:: - state::ExtraAccountMetaList -; +use spl_tlv_account_resolution::state::ExtraAccountMetaList; use spl_transfer_hook_interface::instruction::ExecuteInstruction; use crate::{get_extra_account_metas, get_meta_list_size, Mode, META_LIST_ACCOUNT_SEED}; - #[derive(Accounts)] #[instruction(args: InitMintArgs)] pub struct InitMint<'info> { @@ -62,10 +59,7 @@ impl InitMint<'_> { mint_authority: self.payer.to_account_info(), update_authority: self.payer.to_account_info(), }; - let cpi_ctx = CpiContext::new( - self.token_program.to_account_info(), - cpi_accounts, - ); + let cpi_ctx = CpiContext::new(self.token_program.to_account_info(), cpi_accounts); token_metadata_initialize(cpi_ctx, args.name, args.symbol, args.uri)?; let cpi_accounts = TokenMetadataUpdateField { @@ -74,10 +68,7 @@ impl InitMint<'_> { program_id: self.token_program.to_account_info(), }; - let cpi_ctx = CpiContext::new( - self.token_program.to_account_info(), - cpi_accounts, - ); + let cpi_ctx = CpiContext::new(self.token_program.to_account_info(), cpi_accounts); token_metadata_update_field(cpi_ctx, Field::Key("AB".to_string()), args.mode.to_string())?; @@ -87,10 +78,7 @@ impl InitMint<'_> { update_authority: self.payer.to_account_info(), program_id: self.token_program.to_account_info(), }; - let cpi_ctx = CpiContext::new( - self.token_program.to_account_info(), - cpi_accounts, - ); + let cpi_ctx = CpiContext::new(self.token_program.to_account_info(), cpi_accounts); token_metadata_update_field( cpi_ctx, diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/mod.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/mod.rs index d58980ee..dd7b6053 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/mod.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/instructions/mod.rs @@ -1,16 +1,15 @@ +pub mod attach_to_mint; +pub mod change_mode; +pub mod init_config; pub mod init_mint; pub mod init_wallet; -pub mod tx_hook; pub mod remove_wallet; -pub mod change_mode; -pub mod init_config; -pub mod attach_to_mint; +pub mod tx_hook; +pub use attach_to_mint::*; +pub use change_mode::*; +pub use init_config::*; pub use init_mint::*; pub use init_wallet::*; -pub use tx_hook::*; pub use remove_wallet::*; -pub use change_mode::*; -pub use init_config::*; -pub use attach_to_mint::*; - +pub use tx_hook::*; diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/lib.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/lib.rs index 3bd693cf..b3c37048 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/lib.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/lib.rs @@ -2,18 +2,18 @@ use anchor_lang::prelude::*; use spl_discriminator::SplDiscriminate; use spl_transfer_hook_interface::instruction::ExecuteInstruction; +pub mod constants; pub mod errors; pub mod instructions; pub mod state; -pub mod constants; pub mod utils; +pub use constants::*; pub use errors::*; pub use instructions::*; pub use state::*; -pub use constants::*; pub use utils::*; -declare_id!("LtkoMwPSKxAE714EY3V1oAEQ5LciqJcRwQQuQnzEhQQ"); +declare_id!("3ku1ZEGvBEEfhaYsAzBZuecTPEa58ZRhoVqHVGpGxVGi"); #[program] pub mod abl_token { @@ -28,8 +28,8 @@ pub mod abl_token { ctx.accounts.init_config(ctx.bumps.config) } - pub fn attach_to_mint(ctx: Context, args: AttachToMintArgs) -> Result<()> { - ctx.accounts.attach_to_mint(args) + pub fn attach_to_mint(ctx: Context) -> Result<()> { + ctx.accounts.attach_to_mint() } #[instruction(discriminator = ExecuteInstruction::SPL_DISCRIMINATOR_SLICE)] diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/state.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/state.rs index ef12ae58..1f352b10 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/state.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/state.rs @@ -1,4 +1,7 @@ -use std::{fmt::{self, Display}, str::FromStr}; +use std::{ + fmt::{self, Display}, + str::FromStr, +}; use anchor_lang::prelude::*; @@ -20,7 +23,7 @@ pub struct Config { pub enum Mode { Allow, Block, - Mixed + Mixed, } impl FromStr for Mode { @@ -45,4 +48,3 @@ impl Display for Mode { } } } - diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/utils.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/utils.rs index 33ed2c06..e647df98 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/utils.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/src/utils.rs @@ -6,9 +6,6 @@ use spl_tlv_account_resolution::{ use crate::AB_WALLET_SEED; - - - pub fn get_meta_list_size() -> Result { Ok(ExtraAccountMetaList::size_of(1).unwrap()) } diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/tests/test.rs b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/tests/test.rs index 8c9684aa..f484dd96 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/tests/test.rs +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/anchor/programs/abl-token/tests/test.rs @@ -1,6 +1,18 @@ use { - anchor_lang::ToAccountMetas, anchor_lang::InstructionData, solana_message::Message, - abl_token::{accounts::InitMint, accounts::InitConfig, instructions::InitMintArgs, Mode}, litesvm::LiteSVM, solana_instruction::Instruction, solana_keypair::Keypair, solana_native_token::LAMPORTS_PER_SOL, solana_pubkey::{pubkey, Pubkey}, solana_sdk_ids::system_program::ID as SYSTEM_PROGRAM_ID, solana_signer::Signer, solana_transaction::Transaction, spl_token_2022::ID as TOKEN_22_PROGRAM_ID, std::path::PathBuf + abl_token::{accounts::InitConfig, accounts::InitMint, instructions::InitMintArgs, Mode}, + anchor_lang::InstructionData, + anchor_lang::ToAccountMetas, + litesvm::LiteSVM, + solana_instruction::Instruction, + solana_keypair::Keypair, + solana_message::Message, + solana_native_token::LAMPORTS_PER_SOL, + solana_pubkey::{pubkey, Pubkey}, + solana_sdk_ids::system_program::ID as SYSTEM_PROGRAM_ID, + solana_signer::Signer, + solana_transaction::Transaction, + spl_token_2022::ID as TOKEN_22_PROGRAM_ID, + std::path::PathBuf, }; const PROGRAM_ID: Pubkey = abl_token::ID_CONST; @@ -12,12 +24,11 @@ fn setup() -> (LiteSVM, Keypair) { svm.airdrop(&admin_pk, 10000 * LAMPORTS_PER_SOL).unwrap(); - let mut so_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); so_path.push("../../target/deploy/abl_token.so"); println!("Deploying program from {}", so_path.display()); - + let bytecode = std::fs::read(so_path).unwrap(); svm.add_program(PROGRAM_ID, &bytecode); @@ -27,7 +38,6 @@ fn setup() -> (LiteSVM, Keypair) { #[test] fn test() { - let (mut svm, admin_kp) = setup(); let admin_pk = admin_kp.pubkey(); @@ -36,7 +46,7 @@ fn test() { let config = derive_config(); let meta_list = derive_meta_list(&mint_pk); - let init_cfg_ix = abl_token::instruction::InitConfig { }; + let init_cfg_ix = abl_token::instruction::InitConfig {}; let init_cfg_accounts = InitConfig { payer: admin_pk, @@ -68,9 +78,7 @@ fn test() { mode: Mode::Mixed, threshold: 100000, }; - let init_mint_ix = abl_token::instruction::InitMint { - args: args, - }; + let init_mint_ix = abl_token::instruction::InitMint { args: args }; let data = init_mint_ix.data(); @@ -93,9 +101,6 @@ fn test() { let tx = Transaction::new(&[&admin_kp, &mint_kp], msg, svm.latest_blockhash()); let _res = svm.send_transaction(tx).unwrap(); - - - } fn derive_config() -> Pubkey { @@ -107,5 +112,3 @@ fn derive_meta_list(mint: &Pubkey) -> Pubkey { let seeds = &[b"extra-account-metas", mint.as_ref()]; Pubkey::find_program_address(seeds, &PROGRAM_ID).0 } - - diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/package.json b/tokens/token-2022/transfer-hook/allow-block-list-token/package.json index cda99855..15f26249 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/package.json +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/package.json @@ -17,28 +17,27 @@ }, "dependencies": { "@coral-xyz/anchor": "^0.31.1", - "@radix-ui/react-dialog": "^1.1.11", - "@radix-ui/react-dropdown-menu": "^2.1.12", - "@radix-ui/react-label": "^2.1.4", - "@radix-ui/react-slot": "^1.2.0", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.1.15", + "@radix-ui/react-label": "^2.1.7", + "@radix-ui/react-slot": "^1.2.3", "@solana/spl-token": "0.4.13", - "@solana/wallet-adapter-base": "0.9.26", - "@solana/wallet-adapter-react": "0.15.38", - "@solana/wallet-adapter-react-ui": "0.9.38", + "@solana/wallet-adapter-base": "0.9.27", + "@solana/wallet-adapter-react": "0.15.39", + "@solana/wallet-adapter-react-ui": "0.9.39", "@solana/web3.js": "1.98.2", - "@tanstack/react-query": "^5.74.7", - "bigint-buffer": "^1.1.5", + "@tanstack/react-query": "^5.82.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "jotai": "^2.12.3", - "lucide-react": "^0.503.0", - "next": "15.3.1", + "jotai": "^2.12.5", + "lucide-react": "^0.525.0", + "next": "15.3.5", "next-themes": "^0.4.6", "react": "^19.1.0", "react-dom": "^19.1.0", - "sonner": "^2.0.3", - "tailwind-merge": "^3.2.0", - "tw-animate-css": "^1.2.8" + "sonner": "^2.0.6", + "tailwind-merge": "^3.3.1", + "tw-animate-css": "^1.3.5" }, "devDependencies": { "@eslint/eslintrc": "^3.3.1", diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-config.tsx b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-config.tsx index 4cf8d610..57a42b9f 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-config.tsx +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-config.tsx @@ -4,7 +4,6 @@ import { useWallet } from '@solana/wallet-adapter-react' import { ExplorerLink } from '../cluster/cluster-ui' import { WalletButton } from '../solana/solana-provider' import { useAblTokenProgram } from './abl-token-data-access' -import { AblTokenCreate, AblTokenProgram } from './abl-token-ui' import { AppHero } from '../app-hero' import { ellipsify } from '@/lib/utils' import { Button } from '@/components/ui/button' @@ -14,7 +13,6 @@ import { PublicKey } from '@solana/web3.js' export default function AblTokenConfig() { const { publicKey } = useWallet() const { programId, getConfig, getAbWallets } = useAblTokenProgram() - const [lastUpdate, setLastUpdate] = React.useState(0) const config = getConfig.data; let abWallets = getAbWallets.data; @@ -22,7 +20,6 @@ export default function AblTokenConfig() { const handleWalletListUpdate = React.useCallback(async () => { await getAbWallets.refetch(); abWallets = getAbWallets.data; - setLastUpdate(Date.now()) }, []) return publicKey ? ( @@ -94,7 +91,7 @@ export function AblTokenConfigCreate() { ); } -export function AblTokenConfigList({ abWallets }: { abWallets: any[] | undefined }) { +export function AblTokenConfigList({ abWallets }: { abWallets: {publicKey: PublicKey, account: {wallet: PublicKey, allowed: boolean}}[] | undefined }) { return (

ABL Token Config List

diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-data-access.tsx b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-data-access.tsx index 1fc16564..3e23ab8c 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-data-access.tsx +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-data-access.tsx @@ -10,15 +10,13 @@ import { useAnchorProvider } from '../solana/solana-provider' import { useTransactionToast } from '../use-transaction-toast' import { toast } from 'sonner' import { BN } from '@coral-xyz/anchor' -import { amountToUiAmount, createAssociatedTokenAccountIdempotentInstruction, createAssociatedTokenAccountIdempotentInstructionWithDerivation, createMintToCheckedInstruction, decodeMintToCheckedInstruction, getAssociatedTokenAddressSync, getMint, getPermanentDelegate, getTokenMetadata, getTransferHook, mintToChecked, mintToCheckedInstructionData, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token' - +import { createAssociatedTokenAccountIdempotentInstruction, createMintToCheckedInstruction, getAssociatedTokenAddressSync, getMint, getPermanentDelegate, getTokenMetadata, getTransferHook, TOKEN_2022_PROGRAM_ID } from '@solana/spl-token' export function useHasTransferHookEnabled(mint: PublicKey) { const { connection } = useConnection() - const provider = useAnchorProvider() const { cluster } = useCluster() const programId = useMemo(() => getABLTokenProgramId(cluster.network as Cluster), [cluster]) - const program = useMemo(() => getABLTokenProgram(provider, programId), [provider, programId]) + return useQuery({ queryKey: ['has-transfer-hook', { cluster }], queryFn: async () => { @@ -33,6 +31,60 @@ export function useHasTransferHookEnabled(mint: PublicKey) { }, }) } + +export function useGetToken(mint: PublicKey) { + const { connection } = useConnection() + const { cluster } = useCluster() + const programId = useMemo(() => getABLTokenProgramId(cluster.network as Cluster), [cluster]) + + return useQuery({ + queryKey: ['get-token', { endpoint: connection.rpcEndpoint, mint }], + queryFn: async () => { + const mintInfo = await getMint( + connection, + mint, + "confirmed", + TOKEN_2022_PROGRAM_ID, + ); + + const metadata = await getTokenMetadata( + connection, + mint, + "confirmed", + TOKEN_2022_PROGRAM_ID, + ); + + const mode = metadata?.additionalMetadata.find((metadata) => metadata[0] === "AB")?.[1] || null; + const threshold = metadata?.additionalMetadata.find((metadata) => metadata[0] === "threshold")?.[1] || null; + + const permanentDelegate = await getPermanentDelegate(mintInfo); + + const transferHook = getTransferHook(mintInfo); + + const isTransferHookEnabled = transferHook !== null; + const isTransferHookSet = transferHook?.programId?.equals(programId) || false; + const transferHookProgramId = transferHook?.programId || null; + + return { + name: metadata?.name, + symbol: metadata?.symbol, + uri: metadata?.uri, + decimals: mintInfo.decimals, + supply: mintInfo.supply, + mintAuthority: mintInfo.mintAuthority, + freezeAuthority: mintInfo.freezeAuthority, + permanentDelegate: permanentDelegate?.delegate ?? null, + isTransferHookEnabled, + isTransferHookSet, + transferHookProgramId, + mode, + threshold, + } + }, +}) +} + + export function useAblTokenProgram() { const { connection } = useConnection() const { cluster } = useCluster() @@ -89,21 +141,8 @@ export function useAblTokenProgram() { mutationKey: ['abl-token', 'attach-to-existing-token', { cluster }], mutationFn: (args: { mint: PublicKey, - mode: string, - threshold: BN, - name: string | null, - symbol: string | null, - uri: string | null, }) => { - const modeEnum = args.mode === 'allow' ? { allow: {} } : args.mode === 'block' ? { block: {}} : { mixed: {}}; - - return program.methods.attachToMint({ - mode: modeEnum, - threshold: args.threshold, - name: args.name, - symbol: args.symbol, - uri: args.uri, - }).accounts({ + return program.methods.attachToMint().accounts({ mint: args.mint, }).rpc() }, @@ -120,7 +159,7 @@ export function useAblTokenProgram() { threshold: BN, mint: PublicKey, }) => { - const modeEnum = args.mode === 'allow' ? { allow: {} } : args.mode === 'block' ? { block: {}} : { mixed: {}} + const modeEnum = args.mode === 'Allow' ? { allow: {} } : args.mode === 'Block' ? { block: {}} : { mixed: {}} return program.methods.changeMode({ mode: modeEnum, threshold: args.threshold, @@ -183,7 +222,7 @@ export function useAblTokenProgram() { transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; //transaction.sign(provider.wallet); - let signedTx = await provider.wallet.signTransaction(transaction); + const signedTx = await provider.wallet.signTransaction(transaction); return connection.sendRawTransaction(signedTx.serialize()); @@ -243,36 +282,8 @@ export function useAblTokenProgram() { }, }) - const getToken = (mint: PublicKey) => useQuery({ - queryKey: ['get-token', { endpoint: connection.rpcEndpoint, mint }], - queryFn: async () => { - const mintInfo = await getMint( - connection, - mint, - "confirmed", - TOKEN_2022_PROGRAM_ID, - ); - const metadata = await getTokenMetadata( - connection, - mint, - "confirmed", - TOKEN_2022_PROGRAM_ID, - ); - - const permanentDelegate = await getPermanentDelegate(mintInfo); - - return { - name: metadata?.name, - symbol: metadata?.symbol, - uri: metadata?.uri, - decimals: mintInfo.decimals, - mintAuthority: mintInfo.mintAuthority, - freezeAuthority: mintInfo.freezeAuthority, - permanentDelegate: permanentDelegate, - } - }, - }) + /* const getBalance = useQuery({ queryKey: ['get-balance', { cluster }], @@ -302,7 +313,7 @@ export function useAblTokenProgram() { tx.add(ix, ix2); tx.feePayer = provider.wallet.publicKey; tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; - let signedTx = await provider.wallet.signTransaction(tx); + const signedTx = await provider.wallet.signTransaction(tx); return connection.sendRawTransaction(signedTx.serialize()) }, onSuccess: (signature) => { @@ -311,6 +322,7 @@ export function useAblTokenProgram() { onError: () => toast.error('Failed to run program'), }) + return { program, programId, @@ -322,9 +334,9 @@ export function useAblTokenProgram() { initConfig, getConfig, getAbWallets, - getToken, processBatchWallets, mintTo, attachToExistingToken, } } + diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-manage-token-detail.tsx b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-manage-token-detail.tsx index 398a6705..f6b98ea1 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-manage-token-detail.tsx +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-manage-token-detail.tsx @@ -4,9 +4,8 @@ import { useWallet } from '@solana/wallet-adapter-react' import { WalletButton } from '../solana/solana-provider' import { useParams } from 'next/navigation' import React from 'react' -import { useAblTokenProgram, useHasTransferHookEnabled } from './abl-token-data-access' +import { useAblTokenProgram, useGetToken } from './abl-token-data-access' import { PublicKey } from '@solana/web3.js' -import { PermanentDelegate } from '@solana/spl-token' import { BN } from '@coral-xyz/anchor' import { Button } from '@/components/ui/button' @@ -19,24 +18,44 @@ interface TokenInfo { supply: number; mintAuthority: PublicKey | null; freezeAuthority: PublicKey | null; - permanentDelegate: PermanentDelegate | null; + permanentDelegate: PublicKey | null; + mode: string | null; + threshold: string | null; + transferHookProgramId: PublicKey | null; + isTransferHookEnabled: boolean; + isTransferHookSet: boolean; } -function TokenInfo({ tokenInfo }: { tokenInfo: TokenInfo | null }) { +function TokenInfo({ tokenAddress }: { tokenAddress: string }) { + const { attachToExistingToken } = useAblTokenProgram() + const tokenInfo = useGetToken(new PublicKey(tokenAddress)); return (

Token Information

{tokenInfo ? (
-
Address: {tokenInfo.address}
-
Name: {tokenInfo.name}
-
Symbol: {tokenInfo.symbol}
-
Decimals: {tokenInfo.decimals}
-
URI: {tokenInfo.uri}
-
Supply: {tokenInfo.supply}
-
Mint Authority: {tokenInfo.mintAuthority?.toString()}
-
Freeze Authority: {tokenInfo.freezeAuthority?.toString()}
-
Permanent Delegate: {tokenInfo.permanentDelegate?.delegate.toString()}
+
Address: {tokenAddress}
+
Name: {tokenInfo.data?.name}
+
Symbol: {tokenInfo.data?.symbol}
+
Decimals: {tokenInfo.data?.decimals}
+
URI: {tokenInfo.data?.uri}
+
Supply: {tokenInfo.data?.supply}
+
Mint Authority: {tokenInfo.data?.mintAuthority?.toString()}
+
Freeze Authority: {tokenInfo.data?.freezeAuthority?.toString()}
+
Permanent Delegate: {tokenInfo.data?.permanentDelegate?.toString()}
+
+
+

ABL Token

+
Mode: {tokenInfo.data?.mode}
+
Threshold: {tokenInfo.data?.threshold?.toString()}
+ {tokenInfo.data?.isTransferHookEnabled ? (tokenInfo.data?.isTransferHookSet ? ( +
TxHook: Enabled and Set ✅
+ ) : ( +
TxHook: Enabled.
+ )) : ( +
TxHook: Not enabled ❌
+ )} +
) : (

No token information available.

@@ -47,15 +66,10 @@ function TokenInfo({ tokenInfo }: { tokenInfo: TokenInfo | null }) { function TokenManagement({ tokenInfo }: { tokenInfo: TokenInfo }) { const { publicKey } = useWallet() - const { changeMode, mintTo, attachToExistingToken } = useAblTokenProgram() - const [mode, setMode] = React.useState<'allow' | 'block' | 'mixed'>('allow') - const [threshold, setThreshold] = React.useState('100000') + const { changeMode, mintTo } = useAblTokenProgram() + const [mode, setMode] = React.useState<'Allow' | 'Block' | 'Mixed'>(tokenInfo.mode as 'Allow' | 'Block' | 'Mixed') + const [threshold, setThreshold] = React.useState(tokenInfo.threshold ?? undefined) const [destinationWallet, setDestinationWallet] = React.useState('') - const hasTransferHookEnabled = useHasTransferHookEnabled(new PublicKey(tokenInfo.address)) - const [name, setName] = React.useState('') - const [symbol, setSymbol] = React.useState('') - const [uri, setUri] = React.useState('') - const handleApplyChanges = async () => { if (!publicKey || !tokenInfo) return; @@ -63,7 +77,7 @@ function TokenManagement({ tokenInfo }: { tokenInfo: TokenInfo }) { try { await changeMode.mutateAsync({ mode, - threshold: new BN(threshold), + threshold: threshold === undefined ? new BN(0) : new BN(threshold), mint: new PublicKey(tokenInfo.address), }); } catch (err) { @@ -71,23 +85,6 @@ function TokenManagement({ tokenInfo }: { tokenInfo: TokenInfo }) { } }; - const setTransferHook = async () => { - if (!publicKey || !tokenInfo) return; - - try { - await attachToExistingToken.mutateAsync({ - mint: new PublicKey(tokenInfo.address), - mode, - threshold: new BN(threshold), - name, - symbol, - uri, - }); - } catch (err) { - console.error('Failed to set transfer hook:', err); - } - }; - const [mintAmount, setMintAmount] = React.useState('0') const handleMint = async () => { @@ -110,37 +107,37 @@ function TokenManagement({ tokenInfo }: { tokenInfo: TokenInfo }) {

Token Management

- {hasTransferHookEnabled.data ? ( + {tokenInfo.isTransferHookSet && (
- {mode === 'mixed' && ( + {mode === 'Mixed' && (
-
- ) : ( -
-
-
- - setName(e.target.value)} - placeholder="Enter token name" - /> -
-
- - setSymbol(e.target.value)} - placeholder="Enter token symbol" - /> -
-
- - setUri(e.target.value)} - placeholder="Enter token URI" - /> -
-
-
- -
-
)}
@@ -238,11 +198,9 @@ function TokenManagement({ tokenInfo }: { tokenInfo: TokenInfo }) { export default function ManageTokenDetail() { const { publicKey } = useWallet() - const { getToken } = useAblTokenProgram() const params = useParams() const tokenAddress = params?.address as string - - const tokenQuery = getToken(new PublicKey(tokenAddress)); + const tokenQuery = useGetToken(new PublicKey(tokenAddress)); const tokenInfo = React.useMemo(() => { if (!tokenQuery?.data || !tokenAddress) return null; @@ -271,7 +229,7 @@ export default function ManageTokenDetail() {

Error loading token information. Please check the token address.

) : ( <> - + {tokenInfo && } diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-manage-token.tsx b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-manage-token.tsx index c5fedc73..feab86b7 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-manage-token.tsx +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-manage-token.tsx @@ -1,16 +1,11 @@ 'use client' import { useWallet } from '@solana/wallet-adapter-react' -import { ExplorerLink } from '../cluster/cluster-ui' import { WalletButton } from '../solana/solana-provider' -import { useAblTokenProgram } from './abl-token-data-access' -import { AblTokenCreate, AblTokenProgram } from './abl-token-ui' import { AppHero } from '../app-hero' -import { ellipsify } from '@/lib/utils' import ManageTokenInput from './abl-token-manage-token-input' export default function AblTokenFeature() { const { publicKey } = useWallet() - const { programId } = useAblTokenProgram() return publicKey ? (
diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-new-token.tsx b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-new-token.tsx index 1e1a7567..ed0bb438 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-new-token.tsx +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/abl-token/abl-token-new-token.tsx @@ -4,7 +4,7 @@ import { useWallet } from '@solana/wallet-adapter-react' import { ExplorerLink } from '../cluster/cluster-ui' import { WalletButton } from '../solana/solana-provider' import { useAblTokenProgram } from './abl-token-data-access' -import { AblTokenCreate, AblTokenProgram } from './abl-token-ui' +import { AblTokenCreate } from './abl-token-ui' import { AppHero } from '../app-hero' import { ellipsify } from '@/lib/utils' diff --git a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/account/account-data-access.tsx b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/account/account-data-access.tsx index bb5875ef..2d67047b 100644 --- a/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/account/account-data-access.tsx +++ b/tokens/token-2022/transfer-hook/allow-block-list-token/src/components/account/account-data-access.tsx @@ -1,12 +1,11 @@ 'use client' -import { createAssociatedTokenAccountIdempotentInstruction, createTransferCheckedInstruction, createTransferCheckedWithTransferHookInstruction, getAssociatedTokenAddressSync, getExtraAccountMetaAddress, getExtraAccountMetas, getMint, getTransferHook, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token' +import { createAssociatedTokenAccountIdempotentInstruction, createTransferCheckedInstruction, getAssociatedTokenAddressSync, getExtraAccountMetaAddress, getMint, getTransferHook, TOKEN_2022_PROGRAM_ID, TOKEN_PROGRAM_ID } from '@solana/spl-token' import { useConnection, useWallet } from '@solana/wallet-adapter-react' import { Connection, LAMPORTS_PER_SOL, PublicKey, - SendTransactionError, SystemProgram, Transaction, TransactionMessage, @@ -16,7 +15,6 @@ import { import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { useTransactionErrorToast, useTransactionToast } from '../use-transaction-toast' import { useAnchorProvider } from '../solana/solana-provider' -import { toast } from 'sonner' import { Buffer } from "buffer" export function useGetBalance({ address }: { address: PublicKey }) { @@ -58,14 +56,7 @@ export function useSendTokens() { const ix = createAssociatedTokenAccountIdempotentInstruction(publicKey, ataDestination, destination, mint, TOKEN_2022_PROGRAM_ID); const bi = BigInt(amount); const decimals = mintInfo.decimals; - console.log("BI: ", bi); - console.log("AMOUNT: ", amount); - console.log("DECIMALS: ", decimals); - const buf = Buffer.alloc(10); - console.dir(buf); - - buf.writeBigUInt64LE(bi, 0); - console.log(buf); + const ix3 = await createTransferCheckedInstruction(ataSource, mint, ataDestination, publicKey, bi, decimals, undefined, TOKEN_2022_PROGRAM_ID); const transferHook = getTransferHook(mintInfo); @@ -78,25 +69,16 @@ export function useSendTokens() { ix3.keys.push({ pubkey: abWallet, isSigner: false, isWritable: false }); ix3.keys.push({ pubkey: transferHook.programId, isSigner: false, isWritable: false }); ix3.keys.push({ pubkey: extraMetas, isSigner: false, isWritable: false }); - - console.log("tx-hook: ", transferHook.programId.toString()); - console.log("extra-metas: ", extraMetas.toString()); - console.log("ab-wallet: ", abWallet.toString()); - console.log("KEYS: ", ix3.keys); const validateStateAccount = await connection.getAccountInfo(extraMetas, 'confirmed'); if (!validateStateAccount) throw new Error('validate-state-account not found'); - const validateStateData = getExtraAccountMetas(validateStateAccount); - console.log("validate-state-data: ", validateStateData); - - //const ix2 = await createTransferCheckedWithTransferHookInstruction(connection, ataSource, mint, ataDestination, publicKey, bi, decimals, undefined, 'confirmed', TOKEN_2022_PROGRAM_ID); - + const transaction = new Transaction(); transaction.add(ix, ix3); transaction.feePayer = provider.wallet.publicKey; transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash; - let signedTx = await provider.wallet.signTransaction(transaction); + const signedTx = await provider.wallet.signTransaction(transaction); return connection.sendRawTransaction(signedTx.serialize()); },