Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

token-js: added an e2e test for transferring using a mint with a transfer hook extension #5138

Merged
merged 8 commits into from
Sep 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/pull-request-token.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ jobs:
- name: Build and test transfer hook example
run: ./ci/cargo-test-sbf.sh token/transfer-hook-example

- name: Upload program
uses: actions/upload-artifact@v2
with:
name: spl-transfer-hook-example
path: "target/deploy/*.so"
if-no-files-found: error

cargo-test-sbf-associated-token-account:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -266,6 +273,11 @@ jobs:
with:
name: associated-token-account-program
path: target/deploy
- name: Download spl-transfer-hook-example program
uses: actions/download-artifact@v2
with:
name: spl-transfer-hook-example
path: target/deploy
- run: ./ci/js-test-token.sh

cargo-build-test-cli:
Expand Down
2 changes: 1 addition & 1 deletion token/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"test": "npm run test:unit && npm run test:e2e-built && npm run test:e2e-native && npm run test:e2e-2022",
"test:unit": "mocha test/unit",
"test:e2e-built": "start-server-and-test 'solana-test-validator --bpf-program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA ../../target/deploy/spl_token.so --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e'",
"test:e2e-2022": "TEST_PROGRAM_ID=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb start-server-and-test 'solana-test-validator --bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL ../../target/deploy/spl_associated_token_account.so --bpf-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb ../../target/deploy/spl_token_2022.so --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e*'",
"test:e2e-2022": "TEST_PROGRAM_ID=TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb start-server-and-test 'solana-test-validator --bpf-program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL ../../target/deploy/spl_associated_token_account.so --bpf-program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb ../../target/deploy/spl_token_2022.so --bpf-program TokenHookExampLe8smaVNrxTBezWTRbEwxwb1Zykrb ../../target/deploy/spl_transfer_hook_example.so --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e*'",
"test:e2e-native": "start-server-and-test 'solana-test-validator --reset --quiet' http://127.0.0.1:8899/health 'mocha test/e2e'",
"test:build-programs": "cargo build-sbf --manifest-path ../program/Cargo.toml && cargo build-sbf --manifest-path ../program-2022/Cargo.toml && cargo build-sbf --manifest-path ../../associated-token-account/program/Cargo.toml",
"deploy": "npm run deploy:docs",
Expand Down
2 changes: 2 additions & 0 deletions token/js/test/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ export async function getConnection(): Promise<Connection> {
export const TEST_PROGRAM_ID = process.env.TEST_PROGRAM_ID
? new PublicKey(process.env.TEST_PROGRAM_ID)
: TOKEN_PROGRAM_ID;

export const TRANSFER_HOOK_TEST_PROGRAM_ID = new PublicKey('TokenHookExampLe8smaVNrxTBezWTRbEwxwb1Zykrb');
135 changes: 125 additions & 10 deletions token/js/test/e2e-2022/transferHook.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import chai, { expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';
chai.use(chaiAsPromised);

import type { Connection, Signer } from '@solana/web3.js';
import { PublicKey } from '@solana/web3.js';
import type { AccountMeta, Connection, Signer } from '@solana/web3.js';
import { PublicKey, TransactionInstruction } from '@solana/web3.js';
import { sendAndConfirmTransaction, Keypair, SystemProgram, Transaction } from '@solana/web3.js';
import {
createInitializeMintInstruction,
Expand All @@ -15,31 +15,56 @@ import {
updateTransferHook,
AuthorityType,
setAuthority,
createAssociatedTokenAccountInstruction,
getAssociatedTokenAddressSync,
ASSOCIATED_TOKEN_PROGRAM_ID,
createMintToCheckedInstruction,
getExtraAccountMetaAccount,
ExtraAccountMetaListLayout,
ExtraAccountMetaLayout,
transferCheckedWithTransferHook,
createAssociatedTokenAccountIdempotent,
} from '../../src';
import { TEST_PROGRAM_ID, newAccountWithLamports, getConnection } from '../common';
import { TEST_PROGRAM_ID, newAccountWithLamports, getConnection, TRANSFER_HOOK_TEST_PROGRAM_ID } from '../common';
import { createHash } from 'crypto';

const TEST_TOKEN_DECIMALS = 2;
const EXTENSIONS = [ExtensionType.TransferHook];
describe('transferHook', () => {
let connection: Connection;
let payer: Signer;
let payerAta: PublicKey;
let destinationAuthority: PublicKey;
let destinationAta: PublicKey;
let transferHookAuthority: Keypair;
let pdaExtraAccountMeta: PublicKey;
let mint: PublicKey;
let transferHookProgramId: PublicKey;
let newTransferHookProgramId: PublicKey;
before(async () => {
connection = await getConnection();
payer = await newAccountWithLamports(connection, 1000000000);
destinationAuthority = Keypair.generate().publicKey;
transferHookAuthority = Keypair.generate();
transferHookProgramId = Keypair.generate().publicKey;
newTransferHookProgramId = Keypair.generate().publicKey;
});
beforeEach(async () => {
const mintKeypair = Keypair.generate();
mint = mintKeypair.publicKey;
pdaExtraAccountMeta = getExtraAccountMetaAccount(TRANSFER_HOOK_TEST_PROGRAM_ID, mint);
wjthieme marked this conversation as resolved.
Show resolved Hide resolved
payerAta = getAssociatedTokenAddressSync(
mint,
payer.publicKey,
false,
TEST_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
);
destinationAta = getAssociatedTokenAddressSync(
mint,
destinationAuthority,
false,
TEST_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
);
const mintLen = getMintLen(EXTENSIONS);
const lamports = await connection.getMinimumBalanceForRentExemption(mintLen);

const transaction = new Transaction().add(
SystemProgram.createAccount({
fromPubkey: payer.publicKey,
Expand All @@ -51,7 +76,7 @@ describe('transferHook', () => {
createInitializeTransferHookInstruction(
mint,
transferHookAuthority.publicKey,
transferHookProgramId,
TRANSFER_HOOK_TEST_PROGRAM_ID,
TEST_PROGRAM_ID
),
createInitializeMintInstruction(mint, TEST_TOKEN_DECIMALS, payer.publicKey, null, TEST_PROGRAM_ID)
Expand All @@ -65,10 +90,11 @@ describe('transferHook', () => {
expect(transferHook).to.not.be.null;
if (transferHook !== null) {
expect(transferHook.authority).to.eql(transferHookAuthority.publicKey);
expect(transferHook.programId).to.eql(transferHookProgramId);
expect(transferHook.programId).to.eql(TRANSFER_HOOK_TEST_PROGRAM_ID);
}
});
it('can be updated', async () => {
const newTransferHookProgramId = Keypair.generate().publicKey;
await updateTransferHook(
connection,
payer,
Expand Down Expand Up @@ -106,4 +132,93 @@ describe('transferHook', () => {
expect(transferHook.authority).to.eql(PublicKey.default);
}
});
it('transferChecked', async () => {
const extraAccount = Keypair.generate().publicKey;
const keys: AccountMeta[] = [
{ pubkey: pdaExtraAccountMeta, isSigner: false, isWritable: true },
{ pubkey: mint, isSigner: false, isWritable: false },
{ pubkey: payer.publicKey, isSigner: true, isWritable: false },
{ pubkey: SystemProgram.programId, isSigner: false, isWritable: false },
];

const data = Buffer.alloc(8 + 4 + ExtraAccountMetaLayout.span);
const discriminator = createHash('sha256')
.update('spl-transfer-hook-interface:initialize-extra-account-metas')
.digest()
.subarray(0, 8);
discriminator.copy(data);
ExtraAccountMetaListLayout.encode(
{
count: 1,
extraAccounts: [
{
discriminator: 0,
addressConfig: extraAccount.toBuffer(),
isSigner: false,
isWritable: false,
},
],
},
data,
8
);

const initExtraAccountMetaInstruction = new TransactionInstruction({
keys,
data,
programId: TRANSFER_HOOK_TEST_PROGRAM_ID,
});

const setupTransaction = new Transaction().add(
initExtraAccountMetaInstruction,
SystemProgram.transfer({
fromPubkey: payer.publicKey,
toPubkey: pdaExtraAccountMeta,
lamports: 10000000,
}),
createAssociatedTokenAccountInstruction(
payer.publicKey,
payerAta,
payer.publicKey,
mint,
TEST_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
),
createMintToCheckedInstruction(
mint,
payerAta,
payer.publicKey,
5 * 10 ** TEST_TOKEN_DECIMALS,
TEST_TOKEN_DECIMALS,
[],
TEST_PROGRAM_ID
)
);

await sendAndConfirmTransaction(connection, setupTransaction, [payer]);

await createAssociatedTokenAccountIdempotent(
connection,
payer,
mint,
destinationAuthority,
undefined,
TEST_PROGRAM_ID,
ASSOCIATED_TOKEN_PROGRAM_ID
);

await transferCheckedWithTransferHook(
connection,
payer,
payerAta,
mint,
destinationAta,
payer,
BigInt(10 ** TEST_TOKEN_DECIMALS),
TEST_TOKEN_DECIMALS,
[],
undefined,
TEST_PROGRAM_ID
);
});
});
4 changes: 2 additions & 2 deletions token/js/test/unit/transferHook.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { getExtraAccountMetas, resolveExtraAccountMeta } from '../../src';
import { expect } from 'chai';
import { AccountMeta, Keypair, PublicKey } from '@solana/web3.js';
import { PublicKey } from '@solana/web3.js';

describe('transferHookExtraAccounts', () => {
const testProgramId = new PublicKey('7N4HggYEJAtCLJdnHGCtFqfxcB5rhQCsQTze3ftYstVj');
Expand Down Expand Up @@ -70,7 +70,7 @@ describe('transferHookExtraAccounts', () => {

const extraAccountList = Buffer.concat([
Buffer.from([0, 0, 0, 0, 0, 0, 0, 0]), // u64 accountDiscriminator
Buffer.from([0, 0, 0, 0]), // u32 arrayDiscriminator
Buffer.from([0, 0, 0, 0]), // u32 length
Buffer.from([3, 0, 0, 0]), // u32 count
plainExtraAccount,
pdaExtraAccount,
Expand Down
Loading