Skip to content

Commit 0d61963

Browse files
committed
Add isTransferring check to transfer hook examples
1 parent cc589b2 commit 0d61963

File tree

8 files changed

+307
-183
lines changed

8 files changed

+307
-183
lines changed

tokens/token-2022/transfer-hook/counter/anchor/programs/transfer-hook/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use spl_transfer_hook_interface::instruction::ExecuteInstruction;
2626
declare_id!("1qahDxKHeCLZhbBU2NyMU6vQCQmEUmdeSEBrG5drffK");
2727

2828
#[error_code]
29-
pub enum MyError {
29+
pub enum TransferError {
3030
#[msg("The amount is too big")]
3131
AmountTooBig,
3232
#[msg("The token is not currently transferring")]
@@ -60,13 +60,13 @@ pub mod transfer_hook {
6060
// Check if the amount is too big
6161
if amount > 50 {
6262
msg!("The amount is too big: {}", amount);
63-
//return err!(MyError::AmountTooBig);
63+
//return err!(TransferError::AmountTooBig);
6464
}
6565

6666
// Increment the transfer count safely
6767
let count = ctx.accounts.counter_account.counter
6868
.checked_add(1)
69-
.ok_or(MyError::AmountTooBig)?;
69+
.ok_or(TransferError::AmountTooBig)?;
7070

7171
msg!("This token has been transferred {} times", count);
7272

@@ -81,7 +81,7 @@ fn check_is_transferring(ctx: &Context<TransferHook>) -> Result<()> {
8181
let account_extension = account.get_extension_mut::<TransferHookAccount>()?;
8282

8383
if !bool::from(account_extension.transferring) {
84-
return err!(MyError::IsNotCurrentlyTransferring);
84+
return err!(TransferError::IsNotCurrentlyTransferring);
8585
}
8686

8787
Ok(())

tokens/token-2022/transfer-hook/counter/anchor/tests/transfer-hook.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import { BN } from "bn.js";
2525
import { expect } from "chai";
2626
import chai from "chai";
2727
import chaiAsPromised from "chai-as-promised";
28-
import { send } from "process";
2928

3029
chai.use(chaiAsPromised);
3130

@@ -244,7 +243,7 @@ describe("transfer-hook", () => {
244243

245244
await expect(sendPromise).to.eventually.be.rejectedWith(
246245
SendTransactionError,
247-
/Number: 6001./
246+
program.idl.errors[1].msg
248247
);
249248
});
250249
});

tokens/token-2022/transfer-hook/hello-world/anchor/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@
99
},
1010
"devDependencies": {
1111
"@types/bn.js": "^5.1.0",
12+
"@types/chai-as-promised": "^7.1.8",
13+
"chai-as-promised": "^7.1.2",
1214
"@types/chai": "^4.3.0",
13-
"@types/mocha": "^9.0.0",
1415
"chai": "^4.3.4",
16+
"@types/mocha": "^9.0.0",
1517
"mocha": "^9.0.3",
1618
"prettier": "^2.6.2",
1719
"ts-mocha": "^10.0.0",

tokens/token-2022/transfer-hook/hello-world/anchor/programs/transfer-hook/src/lib.rs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
1+
use std::cell::RefMut;
2+
13
use anchor_lang::prelude::*;
24
use anchor_spl::{
35
associated_token::AssociatedToken,
6+
token_2022::spl_token_2022::{
7+
extension::{
8+
transfer_hook::TransferHookAccount,
9+
BaseStateWithExtensionsMut,
10+
PodStateWithExtensionsMut,
11+
},
12+
pod::PodAccount,
13+
},
414
token_interface::{
515
spl_pod::optional_keys::OptionalNonZeroPubkey,
616
spl_token_2022::{
@@ -21,6 +31,12 @@ use spl_transfer_hook_interface::instruction::ExecuteInstruction;
2131

2232
declare_id!("jY5DfVksJT8Le38LCaQhz5USeiGu4rUeVSS8QRAMoba");
2333

34+
#[error_code]
35+
pub enum TransferError {
36+
#[msg("The token is not currently transferring")]
37+
IsNotCurrentlyTransferring,
38+
}
39+
2440
#[program]
2541
pub mod transfer_hook {
2642
use super::*;
@@ -47,7 +63,7 @@ pub mod transfer_hook {
4763
}
4864

4965
#[interface(spl_transfer_hook_interface::execute)]
50-
pub fn transfer_hook(_ctx: Context<TransferHook>, _amount: u64) -> Result<()> {
66+
pub fn transfer_hook(ctx: Context<TransferHook>, _amount: u64) -> Result<()> {
5167
// Fail this instruction if it is not called from within a transfer hook
5268
check_is_transferring(&ctx)?;
5369

@@ -64,7 +80,7 @@ fn check_is_transferring(ctx: &Context<TransferHook>) -> Result<()> {
6480
let account_extension = account.get_extension_mut::<TransferHookAccount>()?;
6581

6682
if !bool::from(account_extension.transferring) {
67-
return err!(MyError::IsNotCurrentlyTransferring);
83+
return err!(TransferError::IsNotCurrentlyTransferring);
6884
}
6985

7086
Ok(())
Lines changed: 95 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,27 @@
1-
import * as anchor from '@coral-xyz/anchor';
2-
import type { Program } from '@coral-xyz/anchor';
1+
import * as anchor from "@coral-xyz/anchor";
2+
import type { Program } from "@coral-xyz/anchor";
33
import {
44
ASSOCIATED_TOKEN_PROGRAM_ID,
55
TOKEN_2022_PROGRAM_ID,
66
createAssociatedTokenAccountInstruction,
77
createMintToInstruction,
88
createTransferCheckedWithTransferHookInstruction,
99
getAssociatedTokenAddressSync,
10-
} from '@solana/spl-token';
11-
import { Keypair, Transaction, sendAndConfirmTransaction } from '@solana/web3.js';
12-
import type { TransferHook } from '../target/types/transfer_hook';
13-
14-
describe('transfer-hook', () => {
10+
} from "@solana/spl-token";
11+
import {
12+
Keypair,
13+
Transaction,
14+
sendAndConfirmTransaction,
15+
SendTransactionError,
16+
} from "@solana/web3.js";
17+
import type { TransferHook } from "../target/types/transfer_hook";
18+
import { expect } from "chai";
19+
import chai from "chai";
20+
import chaiAsPromised from "chai-as-promised";
21+
22+
chai.use(chaiAsPromised);
23+
24+
describe("transfer-hook", () => {
1525
// Configure the client to use the local cluster.
1626
const provider = anchor.AnchorProvider.env();
1727
anchor.setProvider(provider);
@@ -30,7 +40,7 @@ describe('transfer-hook', () => {
3040
wallet.publicKey,
3141
false,
3242
TOKEN_2022_PROGRAM_ID,
33-
ASSOCIATED_TOKEN_PROGRAM_ID,
43+
ASSOCIATED_TOKEN_PROGRAM_ID
3444
);
3545

3646
// Recipient token account address
@@ -40,21 +50,21 @@ describe('transfer-hook', () => {
4050
recipient.publicKey,
4151
false,
4252
TOKEN_2022_PROGRAM_ID,
43-
ASSOCIATED_TOKEN_PROGRAM_ID,
53+
ASSOCIATED_TOKEN_PROGRAM_ID
4454
);
4555

46-
it('Create Mint with Transfer Hook Extension', async () => {
56+
it("Create Mint with Transfer Hook Extension", async () => {
4757
const transactionSignature = await program.methods
4858
.initialize(decimals)
4959
.accounts({ mintAccount: mint.publicKey })
5060
.signers([mint])
5161
.rpc({ skipPreflight: true });
52-
console.log('Your transaction signature', transactionSignature);
62+
console.log("Your transaction signature", transactionSignature);
5363
});
5464

5565
// Create the two token accounts for the transfer-hook enabled mint
5666
// Fund the sender token account with 100 tokens
57-
it('Create Token Accounts and Mint Tokens', async () => {
67+
it("Create Token Accounts and Mint Tokens", async () => {
5868
// 100 tokens
5969
const amount = 100 * 10 ** decimals;
6070

@@ -65,61 +75,112 @@ describe('transfer-hook', () => {
6575
wallet.publicKey,
6676
mint.publicKey,
6777
TOKEN_2022_PROGRAM_ID,
68-
ASSOCIATED_TOKEN_PROGRAM_ID,
78+
ASSOCIATED_TOKEN_PROGRAM_ID
6979
),
7080
createAssociatedTokenAccountInstruction(
7181
wallet.publicKey,
7282
destinationTokenAccount,
7383
recipient.publicKey,
7484
mint.publicKey,
7585
TOKEN_2022_PROGRAM_ID,
76-
ASSOCIATED_TOKEN_PROGRAM_ID,
86+
ASSOCIATED_TOKEN_PROGRAM_ID
7787
),
78-
createMintToInstruction(mint.publicKey, sourceTokenAccount, wallet.publicKey, amount, [], TOKEN_2022_PROGRAM_ID),
88+
createMintToInstruction(
89+
mint.publicKey,
90+
sourceTokenAccount,
91+
wallet.publicKey,
92+
amount,
93+
[],
94+
TOKEN_2022_PROGRAM_ID
95+
)
7996
);
8097

81-
const txSig = await sendAndConfirmTransaction(connection, transaction, [wallet.payer], { skipPreflight: true });
98+
const txSig = await sendAndConfirmTransaction(
99+
connection,
100+
transaction,
101+
[wallet.payer],
102+
{ skipPreflight: true }
103+
);
82104

83105
console.log(`Transaction Signature: ${txSig}`);
84106
});
85107

86108
// Account to store extra accounts required by the transfer hook instruction
87-
it('Create ExtraAccountMetaList Account', async () => {
109+
it("Create ExtraAccountMetaList Account", async () => {
88110
const initializeExtraAccountMetaListInstruction = await program.methods
89111
.initializeExtraAccountMetaList()
90112
.accounts({
91113
mint: mint.publicKey,
92114
})
93115
.instruction();
94116

95-
const transaction = new Transaction().add(initializeExtraAccountMetaListInstruction);
117+
const transaction = new Transaction().add(
118+
initializeExtraAccountMetaListInstruction
119+
);
96120

97-
const txSig = await sendAndConfirmTransaction(provider.connection, transaction, [wallet.payer], { skipPreflight: true, commitment: 'confirmed' });
98-
console.log('Transaction Signature:', txSig);
121+
const txSig = await sendAndConfirmTransaction(
122+
provider.connection,
123+
transaction,
124+
[wallet.payer],
125+
{ skipPreflight: true, commitment: "confirmed" }
126+
);
127+
console.log("Transaction Signature:", txSig);
99128
});
100129

101-
it('Transfer Hook with Extra Account Meta', async () => {
130+
it("Transfer Hook with Extra Account Meta", async () => {
102131
// 1 tokens
103132
const amount = 1 * 10 ** decimals;
104133
const bigIntAmount = BigInt(amount);
105134

106135
// Standard token transfer instruction
107-
const transferInstruction = await createTransferCheckedWithTransferHookInstruction(
136+
const transferInstruction =
137+
await createTransferCheckedWithTransferHookInstruction(
138+
connection,
139+
sourceTokenAccount,
140+
mint.publicKey,
141+
destinationTokenAccount,
142+
wallet.publicKey,
143+
bigIntAmount,
144+
decimals,
145+
[],
146+
"confirmed",
147+
TOKEN_2022_PROGRAM_ID
148+
);
149+
150+
const transaction = new Transaction().add(transferInstruction);
151+
152+
const txSig = await sendAndConfirmTransaction(
108153
connection,
109-
sourceTokenAccount,
110-
mint.publicKey,
111-
destinationTokenAccount,
112-
wallet.publicKey,
113-
bigIntAmount,
114-
decimals,
115-
[],
116-
'confirmed',
117-
TOKEN_2022_PROGRAM_ID,
154+
transaction,
155+
[wallet.payer],
156+
{ skipPreflight: true }
118157
);
158+
console.log("Transfer Signature:", txSig);
159+
});
119160

120-
const transaction = new Transaction().add(transferInstruction);
161+
it("Try call transfer hook without transfer", async () => {
162+
const transferHookIx = await program.methods
163+
.transferHook(new anchor.BN(1))
164+
.accounts({
165+
sourceToken: sourceTokenAccount,
166+
mint: mint.publicKey,
167+
destinationToken: destinationTokenAccount,
168+
owner: wallet.publicKey,
169+
})
170+
.instruction();
121171

122-
const txSig = await sendAndConfirmTransaction(connection, transaction, [wallet.payer], { skipPreflight: true });
123-
console.log('Transfer Signature:', txSig);
172+
const transaction = new Transaction().add(transferHookIx);
173+
174+
const sendPromise = sendAndConfirmTransaction(
175+
connection,
176+
transaction,
177+
[wallet.payer],
178+
{ skipPreflight: false }
179+
);
180+
181+
await expect(sendPromise).to.eventually.be.rejectedWith(
182+
SendTransactionError,
183+
program.idl.errors[0].msg
184+
);
124185
});
125186
});

0 commit comments

Comments
 (0)