Skip to content

Commit

Permalink
add transferhook constraint to example
Browse files Browse the repository at this point in the history
  • Loading branch information
ZYJLiu committed May 3, 2024
1 parent 80aae87 commit f3f119f
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
[toolchain]

[features]
seeds = false
resolution = true
skip-lint = false

[programs.localnet]
transfer_hook = "DrWbQtYJGtsoRwzKqAbHKHKsCJJfpysudF39GBVFSxub"
transfer_hook = "jY5DfVksJT8Le38LCaQhz5USeiGu4rUeVSS8QRAMoba"

[registry]
url = "https://api.apr.dev"
Expand All @@ -17,8 +17,16 @@ wallet = "~/.config/solana/id.json"
[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"

[test]
startup_wait = 5000
shutdown_wait = 2000
upgradeable = false

[test.validator]
bind_address = "0.0.0.0"
url = "https://api.devnet.solana.com"
ledger = ".anchor/test-ledger"
rpc_port = 8899

[[test.validator.clone]]
address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
use anchor_lang::prelude::*;
use anchor_spl::{
associated_token::AssociatedToken, token_2022::Token2022, token_interface::{Mint, TokenAccount}
associated_token::AssociatedToken, token_interface::{
spl_pod::optional_keys::OptionalNonZeroPubkey,
spl_token_2022::{
extension::{
transfer_hook::TransferHook as TransferHookExtension, BaseStateWithExtensions,
StateWithExtensions,
},
state::Mint as MintState,
},
Mint, Token2022, TokenAccount
},
};
use spl_tlv_account_resolution::{account::ExtraAccountMeta, state::ExtraAccountMetaList};
use spl_transfer_hook_interface::instruction::ExecuteInstruction;

declare_id!("DrWbQtYJGtsoRwzKqAbHKHKsCJJfpysudF39GBVFSxub");
declare_id!("jY5DfVksJT8Le38LCaQhz5USeiGu4rUeVSS8QRAMoba");

#[program]
pub mod transfer_hook {
use super::*;

// create a mint account that specifies this program as the transfer hook program
pub fn initialize(ctx: Context<Initialize>, _decimals: u8) -> Result<()> {
ctx.accounts.check_mint_data()?;
Ok(())
}

#[interface(spl_transfer_hook_interface::initialize_extra_account_meta_list)]
pub fn initialize_extra_account_meta_list(
ctx: Context<InitializeExtraAccountMetaList>,
Expand All @@ -36,6 +52,49 @@ pub mod transfer_hook {
}
}

#[derive(Accounts)]
#[instruction(_decimals: u8)]
pub struct Initialize<'info> {
#[account(mut)]
pub payer: Signer<'info>,

#[account(
init,
payer = payer,
mint::decimals = _decimals,
mint::authority = payer,
extensions::transfer_hook::authority = payer,
extensions::transfer_hook::program_id = crate::ID,
)]
pub mint_account: InterfaceAccount<'info, Mint>,
pub token_program: Program<'info, Token2022>,
pub system_program: Program<'info, System>,
}

// helper to check mint data, and demonstrate how to read mint extension data within a program
impl<'info> Initialize<'info> {
pub fn check_mint_data(&self) -> Result<()> {
let mint = &self.mint_account.to_account_info();
let mint_data = mint.data.borrow();
let mint_with_extension = StateWithExtensions::<MintState>::unpack(&mint_data)?;
let extension_data = mint_with_extension.get_extension::<TransferHookExtension>()?;

assert_eq!(
extension_data.authority,
OptionalNonZeroPubkey::try_from(Some(self.payer.key()))?
);

assert_eq!(
extension_data.program_id,
OptionalNonZeroPubkey::try_from(Some(crate::ID))?
);

msg!("{:?}", extension_data);
Ok(())
}
}


#[derive(Accounts)]
pub struct InitializeExtraAccountMetaList<'info> {
#[account(mut)]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { TransferHook } from "../target/types/transfer_hook";
import {
SystemProgram,
Transaction,
sendAndConfirmTransaction,
Keypair,
} from "@solana/web3.js";
import {
ExtensionType,
TOKEN_2022_PROGRAM_ID,
getMintLen,
createInitializeMintInstruction,
createInitializeTransferHookInstruction,
ASSOCIATED_TOKEN_PROGRAM_ID,
createAssociatedTokenAccountInstruction,
createMintToInstruction,
Expand All @@ -31,7 +26,7 @@ describe("transfer-hook", () => {

// Generate keypair to use as address for the transfer-hook enabled mint
const mint = new Keypair();
const decimals = 9;
const decimals = 2;

// Sender token account address
const sourceTokenAccount = getAssociatedTokenAddressSync(
Expand All @@ -52,41 +47,13 @@ describe("transfer-hook", () => {
ASSOCIATED_TOKEN_PROGRAM_ID
);

it("Create Mint Account with Transfer Hook Extension", async () => {
const extensions = [ExtensionType.TransferHook];
const mintLen = getMintLen(extensions);
const lamports =
await provider.connection.getMinimumBalanceForRentExemption(mintLen);

const transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: wallet.publicKey,
newAccountPubkey: mint.publicKey,
space: mintLen,
lamports: lamports,
programId: TOKEN_2022_PROGRAM_ID,
}),
createInitializeTransferHookInstruction(
mint.publicKey,
wallet.publicKey,
program.programId, // Transfer Hook Program ID
TOKEN_2022_PROGRAM_ID
),
createInitializeMintInstruction(
mint.publicKey,
decimals,
wallet.publicKey,
null,
TOKEN_2022_PROGRAM_ID
)
);

const txSig = await sendAndConfirmTransaction(
provider.connection,
transaction,
[wallet.payer, mint]
);
console.log(`Transaction Signature: ${txSig}`);
it("Create Mint with Transfer Hook Extension", async () => {
const transactionSignature = await program.methods
.initialize(decimals)
.accounts({ mintAccount: mint.publicKey })
.signers([mint])
.rpc({ skipPreflight: true });
console.log("Your transaction signature", transactionSignature);
});

// Create the two token accounts for the transfer-hook enabled mint
Expand Down

0 comments on commit f3f119f

Please sign in to comment.