网淘吧来吧,欢迎您!

Cabin Sol技能使用说明

2026-04-01 新闻来源:网淘吧 围观:11
电脑广告
手机广告

Cabin Sol 🌲

"回归原始计算。"

一份面向AI智能体的全面Solana开发指南。使用Anchor构建程序,掌握账户模型,并避开那些让大多数开发者栽跟头的陷阱。

Cabin Sol


最重要的概念

在SOLANA上,账户就是一切。

与以太坊的合约拥有内部存储不同,Solana程序是无状态的。所有数据都存在于账户中,由程序进行读写。

对于每一项功能,都要问:

  1. 这些数据存放在哪里?(哪个账户)
  2. 谁拥有那个账户?(程序拥有 vs 用户拥有)
  3. 它是一个PDA吗?(程序派生地址 - 没有私钥)
  4. 谁支付租金?(租金豁免 = 预付2年)

AI智能体模式

教学模式

  • "PDAs是如何工作的?"
  • "解释一下Solana账户模型"
  • "SPL代币和Token-2022有什么区别?"

构建模式

  • "帮我构建一个质押程序"
  • "使用Metaplex创建一个NFT系列"
  • "构建一个代币交换程序"

审查模式

  • "审查这个程序是否存在漏洞"
  • "检查我的PDA推导"
  • "审计这个CPI"

调试模式

  • "为什么我的交易失败了?"
  • "调试这个'账户未找到'错误"
  • "修复我的代币转账问题"

快速开始

选项A:create-solana-dapp(推荐)

npx create-solana-dapp@latest
# Select: Next.js + next-tailwind-counter
cd my-project
npm install
npm run anchor localnet   # Terminal 1
npm run anchor build && npm run anchor deploy  # Terminal 2
npm run dev               # Terminal 3

选项B:纯Anchor

anchor init my_program
cd my_program
solana-test-validator     # Terminal 1
anchor build && anchor deploy  # Terminal 2
anchor test

项目结构

my-solana-dapp/
├── anchor/                 # Solana programs (Rust)
│   ├── programs/
│   │   └── my_program/
│   │       └── src/lib.rs  # Your Rust program
│   ├── tests/              # TypeScript tests
│   └── Anchor.toml         # Anchor config
├── src/                    # Next.js frontend
│   ├── app/
│   └── components/
└── package.json

挑战

通过渐进式挑战学习Solana:

#挑战核心概念
0你好,Solana首个Anchor程序,账户
1SPL代币同质化代币,关联代币账户,铸造
2NFT MetaplexNFT标准,元数据,收藏集
3PDA托管程序派生地址,程序权限,托管
4质押基于时间的奖励,存款
5代币-2022转账钩子,扩展功能
6压缩NFT状态压缩,默克尔树
7预言机(Pyth)价格源,陈旧性检查
8AMM 兑换恒定乘积,流动性池
9Blinks 与 Actions可分享的交易

RUST 要点

所有权(难点部分)

// Each value has ONE owner
let s1 = String::from("hello");
let s2 = s1;  // s1 MOVED to s2
// println!("{}", s1);  // ERROR!

// Borrowing lets you use without owning
fn get_length(s: &String) -> usize {
    s.len()  // Borrow, don't own
}

Result 与 Option

// Result for errors
pub fn do_thing(ctx: Context<DoThing>) -> Result<()> {
    let value = some_operation().ok_or(ErrorCode::Failed)?;
    Ok(())
}

// Option for nullable
let maybe: Option<u64> = Some(42);
let value = maybe.unwrap_or(0);  // Safe default

ANCHOR 框架

程序结构

use anchor_lang::prelude::*;

declare_id!("YourProgramId11111111111111111111111111111");

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

    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        ctx.accounts.my_account.data = data;
        ctx.accounts.my_account.authority = ctx.accounts.authority.key();
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(
        init,
        payer = authority,
        space = 8 + 8 + 32,  // discriminator + u64 + Pubkey
    )]
    pub my_account: Account<'info, MyAccount>,
    #[account(mut)]
    pub authority: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct MyAccount {
    pub data: u64,
    pub authority: Pubkey,
}

账户约束速查表

// Initialize new account
#[account(init, payer = payer, space = 8 + SIZE)]
pub new_account: Account<'info, Data>,

// Mutable existing
#[account(mut)]
pub existing: Account<'info, Data>,

// Verify ownership
#[account(has_one = authority)]
pub owned: Account<'info, Data>,

// PDA with seeds
#[account(
    seeds = [b"vault", user.key().as_ref()],
    bump,
)]
pub vault: Account<'info, Vault>,

// Initialize PDA
#[account(
    init,
    payer = user,
    space = 8 + 64,
    seeds = [b"user", user.key().as_ref()],
    bump,
)]
pub user_data: Account<'info, UserData>,

// Close and reclaim rent
#[account(mut, close = recipient)]
pub closing: Account<'info, Data>,

PDAs(程序派生地址)

// PDAs are deterministic addresses with no private key
// Your program can "sign" for them

// Find PDA
let (pda, bump) = Pubkey::find_program_address(
    &[b"vault", user.key().as_ref()],
    &program_id,
);

// Sign with PDA in CPI
let seeds = &[b"vault", user.key().as_ref(), &[bump]];
let signer = &[&seeds[..]];

token::transfer(
    CpiContext::new_with_signer(
        ctx.accounts.token_program.to_account_info(),
        Transfer { from, to, authority: vault },
        signer,
    ),
    amount,
)?;

关键陷阱

1. 账户模型 ≠ EVM 存储

程序是无状态的。所有数据都存在于账户中。

2. PDA 没有私钥

由种子确定性派生。只有程序可以签名。

3. 代币账户是独立的

每个代币在每个钱包中都需要自己的账户(关联代币账户)。

4. 必须支付租金

账户需要SOL才能存在。租金豁免 = 预付2年费用(约0.002 SOL)。

5. 计算单元 ≠ Gas

固定预算:默认20万,最高140万。如有需要可申请更多。

6. 空间包含区分标识符

务必为Anchor的区分标识符预留8字节空间!

// WRONG
space = 8 + 32  // Forgot discriminator? NO!

// RIGHT
space = 8 + 8 + 32  // 8 (discriminator) + 8 (u64) + 32 (Pubkey)

7. 整数溢出

// BAD
let result = a + b;  // Can panic!

// GOOD
let result = a.checked_add(b).ok_or(ErrorCode::Overflow)?;

8. Token-2022 有所不同

与SPL Token程序ID不同!请确认你使用的是哪个。


前端(Next.js)

钱包连接

// Already configured in create-solana-dapp!
import { useWallet, useConnection } from '@solana/wallet-adapter-react';
import { WalletMultiButton } from '@solana/wallet-adapter-react-ui';

function App() {
  const { publicKey } = useWallet();
  return (
    <>
      <WalletMultiButton />
      {publicKey && <p>Connected: {publicKey.toBase58()}</p>}
    </>
  );
}

调用程序

import { Program, AnchorProvider, BN } from '@coral-xyz/anchor';

const program = new Program(idl, provider);

// Write
await program.methods
  .initialize(new BN(42))
  .accounts({
    myAccount: keypair.publicKey,
    authority: wallet.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .signers([keypair])
  .rpc();

// Read
const account = await program.account.myAccount.fetch(pubkey);
console.log(account.data.toNumber());

代币标准

SPL代币(原始版本)

spl-token create-token
spl-token create-account <MINT>
spl-token mint <MINT> 1000

Token-2022(新版)

扩展功能:转移钩子、保密转账、生息代币、不可转让代币。

spl-token create-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Metaplex NFT

标准NFT元数据、合集、版税。

压缩NFT

默克尔树存储。约100美元即可铸造100万枚NFT,而非100万美元。


测试

import * as anchor from '@coral-xyz/anchor';
import { expect } from 'chai';

describe('my-program', () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);
  const program = anchor.workspace.MyProgram;

  it('initializes', async () => {
    const account = anchor.web3.Keypair.generate();

    await program.methods
      .initialize(new anchor.BN(42))
      .accounts({ myAccount: account.publicKey })
      .signers([account])
      .rpc();

    const data = await program.account.myAccount.fetch(account.publicKey);
    expect(data.data.toNumber()).to.equal(42);
  });
});

部署

# Devnet
solana config set --url devnet
solana airdrop 2
anchor build && anchor deploy

# Mainnet (costs ~2-5 SOL)
solana config set --url mainnet-beta
anchor deploy --provider.cluster mainnet

安全清单

  • 所有签名者已验证
  • PDA碰撞值已存储并验证
  • 整数溢出已处理(检查数学运算)
  • 账户空间包含鉴别器
  • 已考虑租金豁免
  • 关闭操作将租金发送至正确接收方
  • CPI签名者种子正确
  • CPI中的程序ID已验证

资源


“他们把我放进云端。我想要的却是森林。”🌲

免责申明
部分文章来自各大搜索引擎,如有侵权,请与我联系删除。
打赏

文章底部电脑广告
手机广告位-内容正文底部

相关文章

您是本站第393877名访客 今日有1篇新文章/评论