Jacky Gu

Solana开发笔记: anchor测试中常用typescript代码

01 Jan 2025 Share to

anchor测试中常用typescript代码

1- 获取provider,初始化链接:

const provider = anchor.AnchorProvider.env();
anchor.setProvider(provider);
const connection = provider.connection;

2- 获取程序

const program = anchor.workspace.Marketplace as Program<Marketplace>; //Marketplace用idl文件中的合约名称替代
const programId = program.programId;

3- 获取PDA账号

const [newGameDataAccount] = anchor.web3.PublicKey.findProgramAddressSync(
  [Buffer.from("level1", "utf8"), mintAccount], // seeds根据合约中的要求配置,必须是Buffer数据
  program.programId
);

4- 调用合约方法

4.1- 使用rpc(),用于一个指令的处理

const txHash = await program.methods
    .initialize() // 方法名称,如果有参数的话在()加入参数值
    .accounts({    // 对应的Accounts指令结构
      newGameDataAccount: newGameDataAccount,
      signer: provider.wallet.publicKey,
      systemProgram: anchor.web3.SystemProgram.programId,
    })
    .signer([签名keypairs]) // 如果是自己签名,不需要
    .rpc();    // 运行该指令
// 等待交易确认
await provider.connection.confirmTransaction(txHash, "confirmed");

4.2- 使用instruction(),可以在前端处理多个指令

const tx = new anchor.web3.Transaction();
const ix = await program.methods
    .initialize() // 方法名称,如果有参数的话在()加入参数值
    .accounts({    // 对应的Accounts指令结构
      newGameDataAccount: newGameDataAccount,
      signer: provider.wallet.publicKey,
      systemProgram: anchor.web3.SystemProgram.programId,
    })
    .instruction();
tx.add(ix);
await provider.sendAndConfirm(tx);

5- 根据keypair获取默认账号

const secretKey = Uint8Array.from(...) // 用solana-keygen new生成,keypair保存在本地~/.config/solana/id.json中,是一个数组。这个地址应该和solana address一致。
const myAccount = anchor.web3.Keypair.fromSecretKey(secretKey);

如果只需要获取当前钱包账号的Pubkey,使用:

const pubkey = provider.wallet.publicKey;

也可以不需要secretKey获取:

const wallet = provider.wallet as anchor.Wallet;
const myAccount = wallet.payer;

如果私钥保存在env中,可以使用

import { getKeypairFromEnvironment } from "@solana-developers/helpers";
const keypair = getKeypairFromEnvironment("SECRET_KEY"); // 不需要process.env

6- 生成新账号(keypair)

import {Keypair} from "@solana/web3.js";
const tokenAccount = Keypair.generate();

7- 读取账号中的数据

await program.account.<Account Struct name>.fetch(<account address>);

注意Account struct name对应#[account]中的定义,但是转成驼峰格式,如ConfigInfo转成configInfo

8- 获取SystemProgram

const systemProgram = anchor.web3.SystemProgram.programId;

9- 获取交易事件

注意:solana本身不具备event功能,使用anchor实现

9.1- 在合约端定义Event结构,如:

use anchor_lang::prelude::*;

#[event]
pub struct ListEvent {
  pub lister: Pubkey,
  pub order_id: String,
  pub tick: String,
  pub amount: u64,
  pub price: u64,
  pub expired_at: i64,
}

9.2- 在合约的方法中使用emit!宏发送Event:

emit!(ListEvent{
  lister: ctx.accounts.lister.key(),
  order_id: order.order_id.clone(),
  tick: order.tick.clone(),
  amount: amount,
  price: price,
  expired_at: expired_at,
});

9.3- 在前端使用idl文件解析Event:

await sleep(1000); // waiting for tx confirmed,only for testing,otherwise txData will be null
const txData = await anchor.getProvider().connection.getTransaction(tx, {
  commitment: "confirmed",
  maxSupportedTransactionVersion: 1,
});
const eventParser = new anchor.EventParser(program.programId, new anchor.BorshCoder(program.idl));
const events = eventParser.parseLogs(txData.meta?.logMessages);
for(let event of events) {
  console.log(event.data);
}

也可以直接使用getEvents方法:

  const getEvents = async (tx: string) => {
    await sleep(1000); // waiting for tx confirmed
    const txData = await anchor.getProvider().connection.getTransaction(tx, {
      commitment: "confirmed",
      maxSupportedTransactionVersion: 1,
    });
    const eventParser = new anchor.EventParser(program.programId, new anchor.BorshCoder(program.idl));
    const events = eventParser.parseLogs(txData.meta?.logMessages);
    return events;
  }

10- 获取账户余额

const balance = await anchor.getProvider().connection.getBalance(accountPublicKey);
console.log('Balance:', balance);

11- 给账户空投

const airdropSignature = await anchor.getProvider().connection.requestAirdrop(account.publicKey, amount);
await provider.connection.confirmTransaction(airdropSignature);

12- 定位ATA地址

方法1: 用anchor库

const [mintAccount] = web3.PublicKey.findProgramAddressSync([Buffer.from(MINT_SEED)], program.programId);
const destination = anchor.utils.token.associatedAddress({
  mint: mintAccount,
  owner: myAccount.publicKey,
});

// 获取ATA中的代币数量
const balance = await provider.connection.getTokenAccountBalance(destination);

方法2: 用@solana/spl-token库:

const ataAddress = await getAssociatedTokenAddress(programPubkey, ownerPubkey);

方法3: 不确定是否已经创建ata账户时(可能要发送交易)

  const tokenAccount = await token.getOrCreateAssociatedTokenAccount(
    connection,
    payer,
    tokenMintAccount,
    owner.publicKey,
  );

13- 将Uint8Array私钥转为bs58格式的私钥

import {bs58} from "@coral-xyz/anchor/dist/cjs/utils/bytes";
const privateKey58 = [8,43,141,...,21,217,144];
const strPrivateKey = bs58.encode(privateKey58);
const owner = "AUVyevF2Wv...tWj6iS6s";
// decode
bs58.decode(strPrivateKey);