diff --git a/docs/src/token-2022/extensions.mdx b/docs/src/token-2022/extensions.mdx
index 810dbaafc97..b4c1bf3a0a4 100644
--- a/docs/src/token-2022/extensions.mdx
+++ b/docs/src/token-2022/extensions.mdx
@@ -1388,7 +1388,60 @@ Signature: 3ug4Ejs16jJgEm1WyBwDDxzh9xqPzQ3a2cmy1hSYiPFcLQi9U12HYF1Dbhzb2bx75SSyd
-Coming soon!
+```jsx
+import {
+ clusterApiUrl,
+ sendAndConfirmTransaction,
+ Connection,
+ Keypair,
+ PublicKey,
+ SystemProgram,
+ Transaction,
+ LAMPORTS_PER_SOL,
+} from '@solana/web3.js';
+
+import {
+ ExtensionType,
+ createInitializeMintInstruction,
+ createInitializeTransferHookInstruction,
+ mintTo,
+ createAccount,
+ getMintLen,
+ TOKEN_2022_PROGRAM_ID,
+} from '../src';
+
+(async () => {
+ const payer = Keypair.generate();
+
+ const mintAuthority = Keypair.generate();
+ const mintKeypair = Keypair.generate();
+ const mint = mintKeypair.publicKey;
+
+ const extensions = [ExtensionType.TransferHook];
+ const mintLen = getMintLen(extensions);
+ const decimals = 9;
+ const transferHookProgramId = new PublicKey('7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj')
+
+ const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
+
+ const airdropSignature = await connection.requestAirdrop(payer.publicKey, 2 * LAMPORTS_PER_SOL);
+ await connection.confirmTransaction({ signature: airdropSignature, ...(await connection.getLatestBlockhash()) });
+
+ const mintLamports = await connection.getMinimumBalanceForRentExemption(mintLen);
+ const mintTransaction = new Transaction().add(
+ SystemProgram.createAccount({
+ fromPubkey: payer.publicKey,
+ newAccountPubkey: mint,
+ space: mintLen,
+ lamports: mintLamports,
+ programId: TOKEN_2022_PROGRAM_ID,
+ }),
+ createInitializeTransferHookInstruction(mint, payer.publicKey, transferHookProgramId, TOKEN_2022_PROGRAM_ID),
+ createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID)
+ );
+ await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined);
+})();
+```
@@ -1407,7 +1460,17 @@ Signature: 3Ffw6yjseDsL3Az5n2LjdwXXwVPYxDF3JUU1JC1KGAEb1LE68S9VN4ebtAyvKeYMHvhjd
-Coming soon!
+```js
+await updateTransferHook(
+ connection,
+ payer, mint,
+ newTransferHookProgramId,
+ payer.publicKey,
+ [],
+ undefined,
+ TOKEN_2022_PROGRAM_ID
+);
+```
diff --git a/token/js/examples/transferHook.ts b/token/js/examples/transferHook.ts
new file mode 100644
index 00000000000..a76265bde9b
--- /dev/null
+++ b/token/js/examples/transferHook.ts
@@ -0,0 +1,63 @@
+import {
+ clusterApiUrl,
+ sendAndConfirmTransaction,
+ Connection,
+ Keypair,
+ PublicKey,
+ SystemProgram,
+ Transaction,
+ LAMPORTS_PER_SOL,
+} from '@solana/web3.js';
+
+import {
+ ExtensionType,
+ createInitializeMintInstruction,
+ createInitializeTransferHookInstruction,
+ getMintLen,
+ TOKEN_2022_PROGRAM_ID,
+ updateTransferHook,
+} from '../src';
+
+(async () => {
+ const payer = Keypair.generate();
+
+ const mintAuthority = Keypair.generate();
+ const mintKeypair = Keypair.generate();
+ const mint = mintKeypair.publicKey;
+
+ const extensions = [ExtensionType.TransferHook];
+ const mintLen = getMintLen(extensions);
+ const decimals = 9;
+ const transferHookPogramId = new PublicKey('7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj');
+ const newTransferHookProgramId = new PublicKey('7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj');
+
+ const connection = new Connection(clusterApiUrl('devnet'), 'confirmed');
+
+ const airdropSignature = await connection.requestAirdrop(payer.publicKey, 2 * LAMPORTS_PER_SOL);
+ await connection.confirmTransaction({ signature: airdropSignature, ...(await connection.getLatestBlockhash()) });
+
+ const mintLamports = await connection.getMinimumBalanceForRentExemption(mintLen);
+ const mintTransaction = new Transaction().add(
+ SystemProgram.createAccount({
+ fromPubkey: payer.publicKey,
+ newAccountPubkey: mint,
+ space: mintLen,
+ lamports: mintLamports,
+ programId: TOKEN_2022_PROGRAM_ID,
+ }),
+ createInitializeTransferHookInstruction(mint, payer.publicKey, transferHookPogramId, TOKEN_2022_PROGRAM_ID),
+ createInitializeMintInstruction(mint, decimals, mintAuthority.publicKey, null, TOKEN_2022_PROGRAM_ID)
+ );
+ await sendAndConfirmTransaction(connection, mintTransaction, [payer, mintKeypair], undefined);
+
+ await updateTransferHook(
+ connection,
+ payer,
+ mint,
+ newTransferHookProgramId,
+ payer.publicKey,
+ [],
+ undefined,
+ TOKEN_2022_PROGRAM_ID
+ );
+})();
diff --git a/token/js/src/extensions/extensionType.ts b/token/js/src/extensions/extensionType.ts
index fabfb8f7499..75172abec48 100644
--- a/token/js/src/extensions/extensionType.ts
+++ b/token/js/src/extensions/extensionType.ts
@@ -12,6 +12,7 @@ import { MINT_CLOSE_AUTHORITY_SIZE } from './mintCloseAuthority.js';
import { NON_TRANSFERABLE_SIZE, NON_TRANSFERABLE_ACCOUNT_SIZE } from './nonTransferable.js';
import { PERMANENT_DELEGATE_SIZE } from './permanentDelegate.js';
import { TRANSFER_FEE_AMOUNT_SIZE, TRANSFER_FEE_CONFIG_SIZE } from './transferFee/index.js';
+import { TRANSFER_HOOK_ACCOUNT_SIZE, TRANSFER_HOOK_SIZE } from './transferHook/index.js';
export enum ExtensionType {
Uninitialized,
@@ -28,6 +29,8 @@ export enum ExtensionType {
CpiGuard,
PermanentDelegate,
NonTransferableAccount,
+ TransferHook,
+ TransferHookAccount,
}
export const TYPE_SIZE = 2;
@@ -65,6 +68,10 @@ export function getTypeLen(e: ExtensionType): number {
return PERMANENT_DELEGATE_SIZE;
case ExtensionType.NonTransferableAccount:
return NON_TRANSFERABLE_ACCOUNT_SIZE;
+ case ExtensionType.TransferHook:
+ return TRANSFER_HOOK_SIZE;
+ case ExtensionType.TransferHookAccount:
+ return TRANSFER_HOOK_ACCOUNT_SIZE;
default:
throw Error(`Unknown extension type: ${e}`);
}
@@ -79,6 +86,7 @@ export function isMintExtension(e: ExtensionType): boolean {
case ExtensionType.NonTransferable:
case ExtensionType.InterestBearingConfig:
case ExtensionType.PermanentDelegate:
+ case ExtensionType.TransferHook:
return true;
case ExtensionType.Uninitialized:
case ExtensionType.TransferFeeAmount:
@@ -87,6 +95,7 @@ export function isMintExtension(e: ExtensionType): boolean {
case ExtensionType.MemoTransfer:
case ExtensionType.CpiGuard:
case ExtensionType.NonTransferableAccount:
+ case ExtensionType.TransferHookAccount:
return false;
default:
throw Error(`Unknown extension type: ${e}`);
@@ -101,6 +110,7 @@ export function isAccountExtension(e: ExtensionType): boolean {
case ExtensionType.MemoTransfer:
case ExtensionType.CpiGuard:
case ExtensionType.NonTransferableAccount:
+ case ExtensionType.TransferHookAccount:
return true;
case ExtensionType.Uninitialized:
case ExtensionType.TransferFeeConfig:
@@ -110,6 +120,7 @@ export function isAccountExtension(e: ExtensionType): boolean {
case ExtensionType.NonTransferable:
case ExtensionType.InterestBearingConfig:
case ExtensionType.PermanentDelegate:
+ case ExtensionType.TransferHook:
return false;
default:
throw Error(`Unknown extension type: ${e}`);
@@ -124,6 +135,8 @@ export function getAccountTypeOfMintType(e: ExtensionType): ExtensionType {
return ExtensionType.ConfidentialTransferAccount;
case ExtensionType.NonTransferable:
return ExtensionType.NonTransferableAccount;
+ case ExtensionType.TransferHook:
+ return ExtensionType.TransferHookAccount;
case ExtensionType.TransferFeeAmount:
case ExtensionType.ConfidentialTransferAccount:
case ExtensionType.CpiGuard:
@@ -135,6 +148,7 @@ export function getAccountTypeOfMintType(e: ExtensionType): ExtensionType {
case ExtensionType.InterestBearingConfig:
case ExtensionType.PermanentDelegate:
case ExtensionType.NonTransferableAccount:
+ case ExtensionType.TransferHookAccount:
return ExtensionType.Uninitialized;
}
}
diff --git a/token/js/src/extensions/index.ts b/token/js/src/extensions/index.ts
index 05b799502f1..08aa516c5cc 100644
--- a/token/js/src/extensions/index.ts
+++ b/token/js/src/extensions/index.ts
@@ -9,3 +9,4 @@ export * from './mintCloseAuthority.js';
export * from './nonTransferable.js';
export * from './transferFee/index.js';
export * from './permanentDelegate.js';
+export * from './transferHook/index.js';
diff --git a/token/js/src/extensions/transferHook/actions.ts b/token/js/src/extensions/transferHook/actions.ts
new file mode 100644
index 00000000000..6610c72416d
--- /dev/null
+++ b/token/js/src/extensions/transferHook/actions.ts
@@ -0,0 +1,67 @@
+import type { ConfirmOptions, Connection, PublicKey, Signer, TransactionSignature } from '@solana/web3.js';
+import { sendAndConfirmTransaction, Transaction } from '@solana/web3.js';
+import { getSigners } from '../../actions/internal.js';
+import { TOKEN_2022_PROGRAM_ID } from '../../constants.js';
+import { createInitializeTransferHookInstruction, createUpdateTransferHookInstruction } from './instructions.js';
+
+/**
+ * Initialize a transfer hook on a mint
+ *
+ * @param connection Connection to use
+ * @param payer Payer of the transaction fees
+ * @param mint Mint to initialize with extension
+ * @param authority Transfer hook authority account
+ * @param transferHookProgramId The transfer hook program account
+ * @param confirmOptions Options for confirming the transaction
+ * @param programId SPL Token program account
+ *
+ * @return Signature of the confirmed transaction
+ */
+export async function initializeTransferHook(
+ connection: Connection,
+ payer: Signer,
+ mint: PublicKey,
+ authority: PublicKey,
+ transferHookProgramId: PublicKey,
+ confirmOptions?: ConfirmOptions,
+ programId = TOKEN_2022_PROGRAM_ID
+): Promise {
+ const transaction = new Transaction().add(
+ createInitializeTransferHookInstruction(mint, authority, transferHookProgramId, programId)
+ );
+
+ return await sendAndConfirmTransaction(connection, transaction, [payer], confirmOptions);
+}
+
+/**
+ * Update the transfer hook program on a mint
+ *
+ * @param connection Connection to use
+ * @param payer Payer of the transaction fees
+ * @param mint Mint to modify
+ * @param transferHookProgramId New transfer hook program account
+ * @param authority Transfer hook update authority
+ * @param multiSigners Signing accounts if `freezeAuthority` is a multisig
+ * @param confirmOptions Options for confirming the transaction
+ * @param programId SPL Token program account
+ *
+ * @return Signature of the confirmed transaction
+ */
+export async function updateTransferHook(
+ connection: Connection,
+ payer: Signer,
+ mint: PublicKey,
+ transferHookProgramId: PublicKey,
+ authority: PublicKey,
+ multiSigners: Signer[] = [],
+ confirmOptions?: ConfirmOptions,
+ programId = TOKEN_2022_PROGRAM_ID
+): Promise {
+ const [authorityPublicKey, signers] = getSigners(authority, multiSigners);
+
+ const transaction = new Transaction().add(
+ createUpdateTransferHookInstruction(mint, authorityPublicKey, transferHookProgramId, signers, programId)
+ );
+
+ return await sendAndConfirmTransaction(connection, transaction, [payer, ...signers], confirmOptions);
+}
diff --git a/token/js/src/extensions/transferHook/index.ts b/token/js/src/extensions/transferHook/index.ts
new file mode 100644
index 00000000000..5e28fd6b10a
--- /dev/null
+++ b/token/js/src/extensions/transferHook/index.ts
@@ -0,0 +1,3 @@
+export * from './actions.js';
+export * from './instructions.js';
+export * from './state.js';
diff --git a/token/js/src/extensions/transferHook/instructions.ts b/token/js/src/extensions/transferHook/instructions.ts
new file mode 100644
index 00000000000..56d71921288
--- /dev/null
+++ b/token/js/src/extensions/transferHook/instructions.ts
@@ -0,0 +1,114 @@
+import { struct, u8 } from '@solana/buffer-layout';
+import type { PublicKey, Signer } from '@solana/web3.js';
+import { TransactionInstruction } from '@solana/web3.js';
+import { programSupportsExtensions, TOKEN_2022_PROGRAM_ID } from '../../constants.js';
+import { TokenUnsupportedInstructionError } from '../../errors.js';
+import { addSigners } from '../../instructions/internal.js';
+import { TokenInstruction } from '../../instructions/types.js';
+import { publicKey } from '@solana/buffer-layout-utils';
+
+export enum TransferHookInstruction {
+ Initialize = 0,
+ Update = 1,
+}
+
+/** Deserialized instruction for the initiation of an transfer hook */
+export interface InitializeTransferHookInstructionData {
+ instruction: TokenInstruction.TransferHookExtension;
+ transferHookInstruction: TransferHookInstruction.Initialize;
+ authority: PublicKey;
+ transferHookProgramId: PublicKey;
+}
+
+/** The struct that represents the instruction data as it is read by the program */
+export const initializeTransferHookInstructionData = struct([
+ u8('instruction'),
+ u8('transferHookInstruction'),
+ publicKey('authority'),
+ publicKey('transferHookProgramId'),
+]);
+
+/**
+ * Construct an InitializeTransferHook instruction
+ *
+ * @param mint Token mint account
+ * @param authority Transfer hook authority account
+ * @param transferHookProgramId Transfer hook program account
+ * @param programId SPL Token program account
+ *
+ * @return Instruction to add to a transaction
+ */
+export function createInitializeTransferHookInstruction(
+ mint: PublicKey,
+ authority: PublicKey,
+ transferHookProgramId: PublicKey,
+ programId: PublicKey
+): TransactionInstruction {
+ if (!programSupportsExtensions(programId)) {
+ throw new TokenUnsupportedInstructionError();
+ }
+ const keys = [{ pubkey: mint, isSigner: false, isWritable: true }];
+
+ const data = Buffer.alloc(initializeTransferHookInstructionData.span);
+ initializeTransferHookInstructionData.encode(
+ {
+ instruction: TokenInstruction.TransferHookExtension,
+ transferHookInstruction: TransferHookInstruction.Initialize,
+ authority,
+ transferHookProgramId,
+ },
+ data
+ );
+
+ return new TransactionInstruction({ keys, programId, data });
+}
+
+/** Deserialized instruction for the initiation of an transfer hook */
+export interface UpdateTransferHookInstructionData {
+ instruction: TokenInstruction.TransferHookExtension;
+ transferHookInstruction: TransferHookInstruction.Update;
+ transferHookProgramId: PublicKey;
+}
+
+/** The struct that represents the instruction data as it is read by the program */
+export const updateTransferHookInstructionData = struct([
+ u8('instruction'),
+ u8('transferHookInstruction'),
+ publicKey('transferHookProgramId'),
+]);
+
+/**
+ * Construct an UpdateTransferHook instruction
+ *
+ * @param mint Mint to update
+ * @param authority The mint's transfer hook authority
+ * @param transferHookProgramId The new transfer hook program account
+ * @param signers The signer account(s) for a multisig
+ * @param tokenProgramId SPL Token program account
+ *
+ * @return Instruction to add to a transaction
+ */
+export function createUpdateTransferHookInstruction(
+ mint: PublicKey,
+ authority: PublicKey,
+ transferHookProgramId: PublicKey,
+ multiSigners: (Signer | PublicKey)[] = [],
+ programId = TOKEN_2022_PROGRAM_ID
+): TransactionInstruction {
+ if (!programSupportsExtensions(programId)) {
+ throw new TokenUnsupportedInstructionError();
+ }
+
+ const keys = addSigners([{ pubkey: mint, isSigner: false, isWritable: true }], authority, multiSigners);
+ const data = Buffer.alloc(updateTransferHookInstructionData.span);
+ updateTransferHookInstructionData.encode(
+ {
+ instruction: TokenInstruction.TransferHookExtension,
+ transferHookInstruction: TransferHookInstruction.Update,
+ transferHookProgramId,
+ },
+ data
+ );
+
+ return new TransactionInstruction({ keys, programId, data });
+}
diff --git a/token/js/src/extensions/transferHook/state.ts b/token/js/src/extensions/transferHook/state.ts
new file mode 100644
index 00000000000..4f7952c9e49
--- /dev/null
+++ b/token/js/src/extensions/transferHook/state.ts
@@ -0,0 +1,51 @@
+import { struct } from '@solana/buffer-layout';
+import type { Mint } from '../../state/mint.js';
+import { ExtensionType, getExtensionData } from '../extensionType.js';
+import type { PublicKey } from '@solana/web3.js';
+import { bool, publicKey } from '@solana/buffer-layout-utils';
+import type { Account } from '../../state/account.js';
+
+/** TransferHook as stored by the program */
+export interface TransferHook {
+ /** The transfer hook update authrority */
+ authority: PublicKey;
+ /** The transfer hook program account */
+ programId: PublicKey;
+}
+
+/** Buffer layout for de/serializing a transfer hook extension */
+export const TransferHookLayout = struct([publicKey('authority'), publicKey('programId')]);
+
+export const TRANSFER_HOOK_SIZE = TransferHookLayout.span;
+
+export function getTransferHook(mint: Mint): TransferHook | null {
+ const extensionData = getExtensionData(ExtensionType.TransferHook, mint.tlvData);
+ if (extensionData !== null) {
+ return TransferHookLayout.decode(extensionData);
+ } else {
+ return null;
+ }
+}
+
+/** TransferHookAccount as stored by the program */
+export interface TransferHookAccount {
+ /**
+ * Whether or not this account is currently tranferring tokens
+ * True during the transfer hook cpi, otherwise false
+ */
+ transferring: boolean;
+}
+
+/** Buffer layout for de/serializing a transfer hook account extension */
+export const TransferHookAccountLayout = struct([bool('transferring')]);
+
+export const TRANSFER_HOOK_ACCOUNT_SIZE = TransferHookAccountLayout.span;
+
+export function getTransferHookAccount(account: Account): TransferHookAccount | null {
+ const extensionData = getExtensionData(ExtensionType.TransferHookAccount, account.tlvData);
+ if (extensionData !== null) {
+ return TransferHookAccountLayout.decode(extensionData);
+ } else {
+ return null;
+ }
+}
diff --git a/token/js/src/instructions/types.ts b/token/js/src/instructions/types.ts
index eb62fe8fe69..4448cbf3469 100644
--- a/token/js/src/instructions/types.ts
+++ b/token/js/src/instructions/types.ts
@@ -36,4 +36,5 @@ export enum TokenInstruction {
InterestBearingMintExtension = 33,
CpiGuardExtension = 34,
InitializePermanentDelegate = 35,
+ TransferHookExtension = 36,
}
diff --git a/token/js/test/e2e-2022/transferHook.test.ts b/token/js/test/e2e-2022/transferHook.test.ts
new file mode 100644
index 00000000000..f88511d1910
--- /dev/null
+++ b/token/js/test/e2e-2022/transferHook.test.ts
@@ -0,0 +1,80 @@
+import chai, { expect } from 'chai';
+import chaiAsPromised from 'chai-as-promised';
+chai.use(chaiAsPromised);
+
+import type { Connection, PublicKey, Signer } from '@solana/web3.js';
+import { sendAndConfirmTransaction, Keypair, SystemProgram, Transaction } from '@solana/web3.js';
+import {
+ createInitializeMintInstruction,
+ getMint,
+ getMintLen,
+ ExtensionType,
+ createInitializeTransferHookInstruction,
+ getTransferHook,
+ updateTransferHook,
+} from '../../src';
+import { TEST_PROGRAM_ID, newAccountWithLamports, getConnection } from '../common';
+
+const TEST_TOKEN_DECIMALS = 2;
+const EXTENSIONS = [ExtensionType.TransferHook];
+describe('transferHook', () => {
+ let connection: Connection;
+ let payer: Signer;
+ let mint: PublicKey;
+ let transferHookProgramId: PublicKey;
+ let newTransferHookProgramId: PublicKey;
+ before(async () => {
+ connection = await getConnection();
+ payer = await newAccountWithLamports(connection, 1000000000);
+ transferHookProgramId = Keypair.generate().publicKey;
+ newTransferHookProgramId = Keypair.generate().publicKey;
+ });
+ beforeEach(async () => {
+ const mintKeypair = Keypair.generate();
+ mint = mintKeypair.publicKey;
+ const mintLen = getMintLen(EXTENSIONS);
+ const lamports = await connection.getMinimumBalanceForRentExemption(mintLen);
+
+ const transaction = new Transaction().add(
+ SystemProgram.createAccount({
+ fromPubkey: payer.publicKey,
+ newAccountPubkey: mint,
+ space: mintLen,
+ lamports,
+ programId: TEST_PROGRAM_ID,
+ }),
+ createInitializeTransferHookInstruction(mint, payer.publicKey, transferHookProgramId, TEST_PROGRAM_ID),
+ createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, payer.publicKey, null, TEST_PROGRAM_ID)
+ );
+
+ await sendAndConfirmTransaction(connection, transaction, [payer, mintKeypair], undefined);
+ });
+ it('is initialized', async () => {
+ const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID);
+ const transferHook = getTransferHook(mintInfo);
+ expect(transferHook).to.not.be.null;
+ if (transferHook !== null) {
+ expect(transferHook.authority.toString()).to.eql(payer.publicKey.toString());
+ expect(transferHook.programId.toString()).to.eql(transferHookProgramId.toString());
+ }
+ });
+ it('can be updated', async () => {
+ await updateTransferHook(
+ connection,
+ payer,
+ mint,
+ newTransferHookProgramId,
+ payer.publicKey,
+ [],
+ undefined,
+ TEST_PROGRAM_ID
+ );
+ const mintInfo = await getMint(connection, mint, undefined, TEST_PROGRAM_ID);
+ const transferHook = getTransferHook(mintInfo);
+ expect(transferHook).to.not.be.null;
+ if (transferHook !== null) {
+ expect(transferHook.authority.toString()).to.eql(payer.publicKey.toString());
+ expect(transferHook.programId.toString()).to.eql(newTransferHookProgramId.toString());
+ }
+ });
+});
diff --git a/token/js/test/unit/programId.test.ts b/token/js/test/unit/programId.test.ts
index b192792a2ce..e9a435b85a9 100644
--- a/token/js/test/unit/programId.test.ts
+++ b/token/js/test/unit/programId.test.ts
@@ -16,6 +16,7 @@ import {
TokenUnsupportedInstructionError,
createInitializePermanentDelegateInstruction,
createEnableCpiGuardInstruction,
+ createInitializeTransferHookInstruction,
} from '../../src';
chai.use(chaiAsPromised);
@@ -24,6 +25,7 @@ describe('unsupported extensions in spl-token', () => {
const account = new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z');
const authority = new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z');
const payer = new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z');
+ const transferHookProgramId = new PublicKey('7o36UsWR1JQLpZ9PE2gn9L4SQ69CNNiWAXd4Jt7rqz9Z');
it('initializeMintCloseAuthority', () => {
expect(function () {
createInitializeMintCloseAuthorityInstruction(mint, null, TOKEN_PROGRAM_ID);
@@ -64,6 +66,14 @@ describe('unsupported extensions in spl-token', () => {
createCreateNativeMintInstruction(payer, NATIVE_MINT_2022, TOKEN_2022_PROGRAM_ID);
}).to.not.throw(TokenUnsupportedInstructionError);
});
+ it('transferHook', () => {
+ expect(function () {
+ createInitializeTransferHookInstruction(mint, authority, transferHookProgramId, TOKEN_PROGRAM_ID);
+ }).to.throw(TokenUnsupportedInstructionError);
+ expect(function () {
+ createInitializeTransferHookInstruction(mint, authority, transferHookProgramId, TOKEN_2022_PROGRAM_ID);
+ }).to.not.throw(TokenUnsupportedInstructionError);
+ });
it('nonTransferableMint', () => {
expect(function () {
createInitializeNonTransferableMintInstruction(mint, TOKEN_PROGRAM_ID);