Full lesson
solana-contract
What it does
From the project README:
PersonalVault is a decentralized personal asset management contract built on Solana, enabling users to securely store, manage, and trade multiple token assets. Developed using the Anchor framework, the contract provides comprehensive asset management and DEX integration capabilities.
- Original
- TradingFlow-OpenSource/solana-contract
- SuperStack fork
- Blockchains/solana-contract @
03d31f3 - Stars
- 0
- Last push (original)
- 2025-10-27
- Languages
- TypeScript, Rust, Shell
- Detected stack
- Anchorweb3.js
- Categories
- Other
- Licence
- MIT
Architecture
12 source files. By extension: .rs ×8, .ts ×4.
| Top-level directory | Source files |
|---|---|
PersonalVault | 12 |
Dapp: devnet deploy pending
⏳ Devnet deploy pending — goes live when the devnet wallet is topped up. This fork's own dapp is not deployed yet, so there is no program address or transaction to show. It is queued in dapp-queue.txt; once devnet SOL is available it is deployed, exercised with real devnet transactions, and this section switches to the live dapp at /dapps/solana-contract/. Until then, the lesson and step-by-step guide below are complete, and you can run it locally with the commands below.
Key code, explained
Excerpts chosen automatically where the code uses the most Solana concepts, pinned to the fork's commit.
PersonalVault/src/instructions.rs L229–252 @ 03d31f3 · MIT · original let cpi_ctx = CpiContext::new( ctx.accounts.token_program.to_account_info(), transfer_instruction, ); token::transfer(cpi_ctx, amount)?; msg!("代币转移成功,更新内部余额记录..."); // 更新内部余额记录 let current_balance = get_token_balance(vault, ctx.accounts.mint.key()); msg!("当前余额记录: {}", current_balance); let new_balance = current_balance.checked_add(amount) .ok_or(ErrorCode::MathOverflow)?; set_token_balance(vault, ctx.accounts.mint.key(), new_balance); msg!("更新后余额记录: {}", new_balance); emit!(UserDepositEvent { user: ctx.accounts.user.key(), token: ctx.accounts.mint.key(), asset_metadata: ctx.accounts.mint.key(), // 对应 Aptos 的 Object<Metadata amount, timestamp: Clock::get()?.unix_timestamp,- Line 229
let cpi_ctx = CpiContext::new(: Makes a cross-program invocation into another program. - Line 234
token::transfer(cpi_ctx, amount)?;: An SPL token transfer through the Token Program. - Line 236
msg!("代币转移成功,更新内部余额记录...");: Logs to the program log, which is visible in explorers and the test output. - Line 242
let new_balance = current_balance.checked_add(amount): Uses checked arithmetic so overflow fails instead of wrapping. - Line 247
emit!(UserDepositEvent {: Emits an Anchor event that clients can parse from the logs. - Line 252
timestamp: Clock::get()?.unix_timestamp,: Reads on-chain time from the Clock sysvar.
PersonalVault/src/structs.rs L37–60 @ 03d31f3 · MIT · original /// PDA bump seed pub bump: u8,} /// 初始化全局配置上下文#[derive(Accounts)]pub struct InitializeGlobalConfig<'info> { #[account( init, payer = admin, space = 8 + 32 + 32 + 32 + 32 + 1, seeds = [b"global_config"], bump )] pub global_config: Account<'info, GlobalConfig>, #[account(mut)] pub admin: Signer<'info>, pub system_program: Program<'info, System>,} /// 创建余额管理器上下文#[derive(Accounts)]- Line 37
/// PDA bump seed: Uses the bump seed, the extra byte that pushes the derived address off the ed25519 curve. - Line 45
init,: Creates and pays for a new account. Space and payer must be specified. - Line 48
seeds = [b"global_config"],: Declares the PDA seeds. Anchor re-derives the address and rejects the account if it doesn't match. - Line 54
pub admin: Signer<'info>,: Requires this account to have signed the transaction.
PersonalVault/src/dex/raydium_clmm.rs L85–108 @ 03d31f3 · MIT · original msg!("✅ 确认为临时 WSOL 账户"); msg!(" Owner: {}", token_acc.owner); msg!(" Mint: {}", token_acc.mint); msg!(" Amount: {}", token_acc.amount); Ok(true) } /// 关闭临时 WSOL 账户 /// /// 使用 CPI 调用 Token Program 的 CloseAccount 指令 fn close_temp_wsol_account<'info>( temp_account: &AccountInfo<'info>, destination: &AccountInfo<'info>, authority: &AccountInfo<'info>, signer_seeds: &[&[&[u8]]], ) -> Result<()> { msg!("🔧 关闭临时 WSOL 账户..."); msg!(" 临时账户: {}", temp_account.key()); msg!(" 租金接收者: {}", destination.key()); msg!(" 权限账户: {}", authority.key()); use anchor_lang::solana_program::program::invoke_signed;- Line 86
msg!("✅ 确认为临时 WSOL 账户");: Logs to the program log, which is visible in explorers and the test output. - Line 108
use anchor_lang::solana_program::program::invoke_signed;: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.
PersonalVault/src/dex/raydium_amm.rs L145–168 @ 03d31f3 · MIT · original msg!(" 程序 ID: {}", instruction.program_id); msg!(" 账户数量: {}", instruction.accounts.len()); msg!(" 数据大小: {} 字节", instruction.data.len()); // 🔧 验证 AccountInfo 数组 msg!("🔧 验证 CPI 调用所需的 AccountInfo 数组..."); if account_infos.len() < instruction.accounts.len() { msg!("❌ AccountInfo 数组长度不足"); msg!(" 需要: {} 个账户", instruction.accounts.len()); msg!(" 提供: {} 个账户", account_infos.len()); return Err(error!( crate::constants::ErrorCode::InvalidAccountAddressFormat )); } msg!("✅ AccountInfo 数组验证通过"); msg!("🚀 开始执行 Raydium AMM V4 交换(使用 PDA 签名)..."); // 🎯 执行真正的 CPI 调用! // 🔐 使用 invoke_signed 来让 vault PDA 签名 match anchor_lang::solana_program::program::invoke_signed(&instruction, account_infos, signer_seeds) { Ok(()) => { msg!("✅ Raydium AMM V4 交换执行成功!");- Line 145
msg!(" 程序 ID: {}", instruction.program_id);: Logs to the program log, which is visible in explorers and the test output. - Line 165
// 🔐 使用 invoke_signed 来让 vault PDA 签名: Makes a CPI signed by a PDA. The signer seeds prove the program controls that address.
How to run, build and test
These commands come from the repo's own manifests (package.json scripts, Anchor.toml, Cargo, go.mod, Makefile). Check the README for prerequisites and environment variables.
| Directory | Command | Runs |
|---|---|---|
PersonalVault | npm run build | tsc |
PersonalVault | npm run test | ts-node test/test.ts |
PersonalVault/test | npm run dev | ts-node --watch test.ts |
PersonalVault/test | npm run build | tsc |
PersonalVault/test | npm run test | ts-node test.ts |
PersonalVault | anchor build | compile programs and generate IDLs |
PersonalVault | anchor test | local validator + test suite |
PersonalVault | anchor run test | cd test && yarn run ts-mocha -p tsconfig.json -t 1000000 test.ts |
What you could build from it
- Fork the SuperStack copy and extend it within the Other gaps listed on the idea page.
- Learn the pattern first: Program Derived Addresses (PDAs)
- Learn the pattern first: Cross-Program Invocations (CPIs)