Skip to main content

anchor expense

lib.rs

use anchor_lang::prelude::*;

// 在这里定义程序的 ID,程序 ID 是部署到 Solana 上的智能合约的唯一标识符
declare_id!("9sDEVVjBZzVq36tGT1SKcduMKBNb5YbjPdF4kFrZwscA");

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

// 初始化一个新的支出账户
// `id`: 支出记录的唯一标识符
// `merchant_name`: 商家的名称
// `amount`: 支出的金额
pub fn initialize_expense(
ctx: Context<InitializeExpense>, // 上下文,包含账户信息
id: u64, // 支出记录的唯一 ID
merchant_name: String, // 商家名称
amount: u64, // 支出金额
) -> Result<()> {
let expense_account = &mut ctx.accounts.expense_account; // 获取支出账户的可变引用

// 在支出账户中设置初始值
expense_account.id = id;
expense_account.merchant_name = merchant_name;
expense_account.amount = amount;
expense_account.owner = *ctx.accounts.authority.key; // 设置支出账户的所有者为调用者

Ok(())
}

// 修改现有的支出账户
// `merchant_name`: 更新后的商家名称
// `amount`: 更新后的支出金额
pub fn modify_expense(
ctx: Context<ModifyExpense>, // 上下文,包含账户信息
_id: u64, // 支出记录的唯一 ID(不需要更改)
merchant_name: String, // 更新后的商家名称
amount: u64, // 更新后的支出金额
) -> Result<()> {
let expense_account = &mut ctx.accounts.expense_account; // 获取支出账户的可变引用
// 更新商家名称和支出金额
expense_account.merchant_name = merchant_name;
expense_account.amount = amount;

Ok(())
}

// 删除现有的支出账户
pub fn delete_expense(_ctx: Context<DeleteExpense>, _id: u64) -> Result<()> {
// 删除逻辑已经在账户声明中通过 `close` 语句实现
Ok(())
}
}

// 初始化支出账户的上下文
#[derive(Accounts)]
#[instruction(id : u64)] // 通过 `id` 参数初始化支出账户
pub struct InitializeExpense<'info> {
#[account(mut)]
pub authority: Signer<'info>, // 签名者,即支出账户的所有者

#[account(
init, // 初始化新的账户
payer = authority, // 由签名者支付账户创建的费用
space = 8 + 8 + 32 + (4 + 12) + 8 + 1, // 分配账户的空间大小
seeds = [b"expense", authority.key().as_ref(), id.to_le_bytes().as_ref()], // PDA 的种子
bump // PDA 的 bump 值
)]
pub expense_account: Account<'info, ExpenseAccount>, // 支出账户

pub system_program: Program<'info, System>, // 系统程序,用于创建账户
}

// 修改支出账户的上下文
#[derive(Accounts)]
#[instruction(id : u64)] // 通过 `id` 参数修改支出账户
pub struct ModifyExpense<'info> {
#[account(mut)]
pub authority: Signer<'info>, // 签名者,即支出账户的所有者

#[account(
mut, // 允许修改账户数据
seeds = [b"expense", authority.key().as_ref(), id.to_le_bytes().as_ref()], // PDA 的种子
bump // PDA 的 bump 值
)]
pub expense_account: Account<'info, ExpenseAccount>, // 支出账户

pub system_program: Program<'info, System>, // 系统程序
}

// 删除支出账户的上下文
#[derive(Accounts)]
#[instruction(id : u64)] // 通过 `id` 参数删除支出账户
pub struct DeleteExpense<'info> {
#[account(mut)]
pub authority: Signer<'info>, // 签名者,即支出账户的所有者

#[account(
mut, // 允许修改账户数据
close = authority, // 关闭账户并将剩余的 SOL 退还给签名者
seeds = [b"expense", authority.key().as_ref(), id.to_le_bytes().as_ref()], // PDA 的种子
bump // PDA 的 bump 值
)]
pub expense_account: Account<'info, ExpenseAccount>, // 支出账户

pub system_program: Program<'info, System>, // 系统程序
}

// 支出账户的结构体,存储支出的相关信息
#[account]
#[derive(Default)]
pub struct ExpenseAccount {
pub id: u64, // 支出记录的唯一 ID
pub owner: Pubkey, // 支出账户的所有者(签名者的公钥)
pub merchant_name: String, // 商家名称
pub amount: u64, // 支出金额
}

index.test.ts

describe("Expense Tracker", async () => {
// 初始化测试数据
let merchantName = "test"; // 初始商家名称
let amount = 100; // 初始支出金额
let id = 1; // 支出记录的唯一 ID

let merchantName2 = "test 2"; // 修改后的商家名称
let amount2 = 200; // 修改后的支出金额

// 使用 PDA(程序派生地址)计算支出账户的地址
// PDA 是由 "expense"、用户的公钥和支出 ID 生成的
let [expense_account] = anchor.web3.PublicKey.findProgramAddressSync(
[
Buffer.from("expense"), // 固定字符串 "expense" 作为种子
pg.wallet.publicKey.toBuffer(), // 用户的钱包公钥作为种子
new BN(id).toArrayLike(Buffer, "le", 8), // 支出记录的 ID 转换为小端字节数组作为种子
],
pg.program.programId // 程序的 ID 作为生成 PDA 的依据
);

// 测试初始化支出账户
it("Initialize Expense", async () => {
await pg.program.methods
.initializeExpense(new anchor.BN(id), merchantName, new anchor.BN(amount)) // 调用智能合约的 `initializeExpense` 方法
.accounts({
expenseAccount: expense_account, // 传入计算出的 PDA 作为支出账户
authority: pg.wallet.publicKey, // 传入调用者的钱包公钥作为签名者
})
.rpc(); // 发送交易
});

// 测试修改支出账户
it("Modify Expense", async () => {
await pg.program.methods
.modifyExpense(new anchor.BN(id), merchantName2, new anchor.BN(amount2)) // 调用智能合约的 `modifyExpense` 方法,传入新的商家名称和金额
.accounts({
expenseAccount: expense_account, // 传入支出账户的 PDA
authority: pg.wallet.publicKey, // 传入调用者的钱包公钥作为签名者
})
.rpc(); // 发送交易
});

// 测试删除支出账户
it("Delete Expense", async () => {
await pg.program.methods
.deleteExpense(new anchor.BN(id)) // 调用智能合约的 `deleteExpense` 方法
.accounts({
expenseAccount: expense_account, // 传入支出账户的 PDA
authority: pg.wallet.publicKey, // 传入调用者的钱包公钥作为签名者
})
.rpc(); // 发送交易
});
});

数据

alt text