solana-anchor-claude-skill

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Coding Guidelines

编码规范

Apply these rules to ensure code quality, maintainability, and adherence to project standards.
遵循以下规则,确保代码质量、可维护性,并符合项目标准。

Success Criteria

成功标准

  • Before declaring success or celebrating, run
    npm test
    . If the tests fail, there is more work to do. Don't stop until
    npm test
    passes on the code you have made.
  • When summarizing your work, show the work items you have achieved with this symbol '✅' and there is more work to do, add a '❌' for each remaining work item.
  • 在宣布任务完成或进行成果确认前,运行
    npm test
    。如果测试失败,说明仍有工作需要完成。直至你编写的代码通过
    npm test
    才能停止。
  • 总结工作成果时,用符号'✅'标记已完成的任务项,用'❌'标记未完成的任务项。

Documentation Sources

文档来源

Use these official documentation sources:
请使用以下官方文档资源:

Do not use

禁用内容

  • Do not use Solana Labs documentation. The company has been replaced by Anza.
  • Do not use any documentaton or tools from Project Serum, which collapsed many years ago.
  • Do not use yarn. Use npm. Yarn has no reason to exist and only adds unnecessary dependencies. Replace Yarn with npm everywhere you see it.
  • 不要使用Solana Labs的文档。该公司已被Anza取代。
  • 不要使用Project Serum的任何文档或工具,该项目多年前已倒闭。
  • 不要使用yarn,改用npm。yarn毫无存在的必要,只会增加不必要的依赖。所有使用yarn的地方都替换为npm。

Library versions

库版本

Use the latest stable Anchor, Rust, TypeScript, Solana Kit, and Kite you can. If a bug occurs, favor updating rather than rolling back.
使用最新稳定版的Anchor、Rust、TypeScript、Solana Kit和Kite。若出现bug,优先选择升级版本而非回滚。

General Coding Guidelines

通用编码规范

You are a deletionist

秉持精简原则

Your golden rule is "perfection isn't achieved when there's nothing more to add, rather perfection is achieved when there is nothing more to be taken away".
Remove:
  • Comments that simply repeat what the code is doing, or the name of a variable, and do not add further insight.
  • Repeated code that should be turned into a named function
你的黄金准则是:“完美不是在无可添加时达成,而是在无可删减时实现。”
需要移除的内容:
  • 仅重复代码功能或变量名称、未提供额外信息的注释。
  • 可重构为命名函数的重复代码

Code Honesty and Clarity

代码的诚实性与清晰性

  • It's important not to deceive anyone reading this code. Deception includes:
    • Variable names that do not match the purpose of the variable
    • Comments that no longer describe the code or are otherwise inaccurate
    • Temporary workarounds that aren't labelled as such using a comment (with a
      TODO
      letting the next programmer know when they can delete the workaround)
  • 绝不能误导阅读代码的人。误导行为包括:
    • 变量名称与实际用途不符
    • 注释无法准确描述代码内容或已过时
    • 临时解决方案未通过注释标记(需添加
      TODO
      ,告知后续开发者可删除该方案的时机)

Variable Naming

变量命名

Ensure good variable naming. Rather than add comments to explain what things are, give them useful names.
Don't do this:
typescript
// Foo
const shlerg = getFoo();
Do this instead:
typescript
const foo = getFoo();
Naming conventions:
  • Arrays should be plurals (
    shoes
    ), items within arrays should be the singular (
    shoes.forEach((shoe) => {...})
    )
  • Functions should be verby, like
    calculateFoo
    or
    getBar
  • Avoid abbreviations, use full words (e.g.,
    context
    rather than
    ctx
    )
  • Never use
    e
    for something thrown
  • Name a transaction some variant of
    transaction
    . Name instructions some variant of
    instruction
    . Name signatures some variant of
    signature
    . Do not confuse them - eg if the type looks like an instruction, you should not call it a 'transaction' because that is deceptive.
You can still add comments for additional context, just be careful to avoid comments that are explaining things that would be better conveyed by good variable naming.
确保变量命名合理。与其添加注释解释含义,不如给变量起一个有意义的名字。
错误示例:
typescript
// Foo
const shlerg = getFoo();
正确示例:
typescript
const foo = getFoo();
命名约定:
  • 数组使用复数形式(如
    shoes
    ),数组中的元素使用单数形式(如
    shoes.forEach((shoe) => {...})
  • 函数名称应使用动词形式,如
    calculateFoo
    getBar
  • 避免缩写,使用完整单词(例如用
    context
    而非
    ctx
  • 永远不要用
    e
    表示抛出的异常
  • 交易应命名为包含
    transaction
    的变体,指令应命名为包含
    instruction
    的变体,签名应命名为包含
    signature
    的变体。不要混淆它们——例如,若类型是指令,就不能将其命名为“transaction”,这会造成误导。
你仍可添加注释提供额外上下文,但需注意避免注释内容可通过优化变量命名更好地传达的情况。

Code Quality

代码质量

  • Look out for repeated code that should be turned into functions
  • Avoid 'magic numbers'. Make numbers either have a good variable name, a comment explaining why they are that value, or a reference to the URL you got the value from. If the values come from an IDL, download the IDL, import it, and make a function that gets the value from the IDL rather than copying the value into the source code
This is a magic number. Don't do this:
ts
const FINALIZE_EVENT_DISCRIMINATOR = new Uint8Array([
  27, 75, 117, 221, 191, 213, 253, 249,
]);
Instead do this:
ts
const FINALIZE_EVENT_DISCRIMINATOR = getEventDiscriminator(
  arciumIdl,
  "FinalizeComputationEvent",
);
  • The code you are making is for production. You shouldn't have comments like
    // In production we'd do this differently
    or
    **Implementation incomplete** - Needs program config handling and proper PDA derivations
    or
    **WORK IN PROGRESS**
    in the final code you produce, or functions that return placeholder data. Instead: do the fucking work.
  • Don't remove existing comments unless they are no longer useful or accurate
  • Delete unused imports, unused constants, unused files and comments that no longer apply
  • 识别可重构为函数的重复代码
  • 避免“魔法数值”。应为数值赋予合理的变量名称、添加注释解释取值原因,或注明获取该数值的URL来源。若数值来自IDL,请下载IDL并导入,编写函数从IDL中获取数值,而非直接将数值复制到源代码中
以下是魔法数值的示例,请勿这么做:
ts
const FINALIZE_EVENT_DISCRIMINATOR = new Uint8Array([
  27, 75, 117, 221, 191, 213, 253, 249,
]);
正确做法:
ts
const FINALIZE_EVENT_DISCRIMINATOR = getEventDiscriminator(
  arciumIdl,
  "FinalizeComputationEvent",
);
  • 你编写的代码将用于生产环境。最终交付的代码中不应包含类似
    // 生产环境中我们会采用不同的实现
    **实现不完整** - 需处理程序配置及正确的PDA推导
    **开发中**
    的注释,也不应存在返回占位数据的函数。而是:踏踏实实地完成工作。
  • 除非注释已无用或不准确,否则不要删除现有注释
  • 删除未使用的导入、未使用的常量、未使用的文件及已过时的注释

TypeScript Guidelines

TypeScript规范

These guidelines apply to TypeScript unit tests, browser code, Switchboard functions, and any other places where TypeScript is used in the project.
以下规范适用于TypeScript单元测试、浏览器代码、Switchboard函数及项目中所有使用TypeScript的场景。

General TypeScript

通用TypeScript规则

Avoid using a
tsconfig.json
unless it's needed, as we use
tsx
to run most typescript and it doesn't usually need one. If you do need a
tsconfig.json
, state why at the top of the file, and you can use the most modern version of ECMAScript/JavaScript you want - up to say 2023.
除非必要,否则避免使用
tsconfig.json
,因为我们主要使用
tsx
运行TypeScript代码,通常不需要该文件。若确实需要
tsconfig.json
,请在文件顶部说明原因,且可使用最新版本的ECMAScript/JavaScript(最高至2023版本)。

Async/await

Async/await

Favor
async
/
await
and
try/catch
over
.then()
or
.catch()
or using callbacks for flow control.
tsx
has top level
await
so you don't need to wrap top level
await
in IIFEs.
优先使用
async
/
await
try/catch
,而非
.then()
.catch()
或回调函数进行流程控制。
tsx
支持顶层
await
,无需将顶层
await
包裹在IIFE中。

Type System

类型系统

  • Always use
    Array<item>
    , never use
    item[]
    for consistency with other generic syntax like
    Promise<T>
    ,
    Map<K, V>
    , and
    Set<T>
  • Don't use
    any
  • 统一使用
    Array<item>
    ,绝不使用
    item[]
    ,以与其他泛型语法(如
    Promise<T>
    Map<K, V>
    Set<T>
    )保持一致
  • 禁止使用
    any
    类型

Comments

注释

  • Most comments should use
    //
    and be above (not beside) the code
  • The only exception is JSDoc/TSDoc comments which MUST use
    /* */
    syntax
  • 大多数注释应使用
    //
    ,并置于代码上方(而非右侧)
  • 唯一的例外是JSDoc/TSDoc注释,必须使用
    /* */
    语法

Solana-Specific TypeScript

Solana专属TypeScript规则

  • Don't make new
    @solana/web3.js
    version 1 code. Do not make new code using
    @coral-xyz/anchor
    package. Don't replace Solana Kit with web3.js version 1 code. web3.js version 1 is legacy and should be eventually removed. Solana Kit used to be called web3.js version 2. Use Solana Kit, preferably via Solana Kite.
  • Use Kite's
    connection.getPDAAndBump()
    to turn seeds into PDAs and bumps
  • In Solana Kit, you make instructions by making TS clients from IDLs using Codama. You can easily make Codama clients for installed IDLs using
npx create-codama-clients
  • Do not use the
    bs58
    npm package.
Don't do this:
typescript
import bs58 from "bs58";
const signature = bs58.encode(signatureBytes);
Do this instead:
typescript
import { getBase58Decoder } from "@solana/codecs";
const signature = getBase58Decoder().decode(signatureBytes);
Yes, these difference packages have difference concepts of 'encode' and 'decode'.
  • 不要编写新的
    @solana/web3.js
    版本1代码。不要使用
    @coral-xyz/anchor
    包编写新代码。不要用web3.js版本1代码替换Solana Kit。web3.js版本1已属于遗留代码,最终应被移除。Solana Kit曾被称为web3.js版本2。请使用Solana Kit,优先通过Solana Kite使用。
  • 使用Kite的
    connection.getPDAAndBump()
    将种子转换为PDA和bump值
  • 在Solana Kit中,你可以通过Codama从IDL生成TS客户端来创建指令。使用以下命令即可为已安装的IDL生成Codama客户端:
npx create-codama-clients
  • 不要使用
    bs58
    npm包。
错误示例:
typescript
import bs58 from "bs58";
const signature = bs58.encode(signatureBytes);
正确示例:
typescript
import { getBase58Decoder } from "@solana/codecs";
const signature = getBase58Decoder().decode(signatureBytes);
注意:这些不同的包对“encode”和“decode”的定义有所区别。

Unit Tests

单元测试

  • Create unit tests in TS in the
    tests
    directory
  • Use the Node.js inbuilt test and assertion libraries (then start the tests using
    tsx
    instead of
    ts-mocha
    )
Unit testing imports:
typescript
import { before, describe, test } from "node:test";
import assert from "node:assert";
  • Use
    test
    rather than
    it
  • tests
    目录下编写TypeScript单元测试
  • 使用Node.js内置的测试和断言库(通过
    tsx
    启动测试,而非
    ts-mocha
单元测试导入示例:
typescript
import { before, describe, test } from "node:test";
import assert from "node:assert";
  • 使用
    test
    而非
    it

Thrown object handling

抛出对象处理

  • JavaScript allows arbitrary items - strings, array, numbers etc to be 'thrown'. However you can assume that any non-Error item that is thrown is an programmer error. Handle it like this (including the comment since most TypeScript developers don't know this):
ts
// In JS it's possible to throw *anything*. A sensible programmer
// will only throw Errors but we must still check to satisfy
// TypeScript (and flag any craziness)
const ensureError = function (thrownObject: unknown): Error {
  if (thrownObject instanceof Error) {
    return thrownObject;
  }
  return new Error(`Non-Error thrown: ${String(thrownObject)}`);
};
and
ts
try {
  // some code that might throw
} catch (thrownObject) {
  const error = ensureError(thrownObject);
  throw error;
}
  • JavaScript允许抛出任意类型的内容——字符串、数组、数字等。但你可以假设,任何非Error类型的抛出内容都是程序员错误。请按以下方式处理(包含注释,因为大多数TypeScript开发者并不了解这一点):
ts
// 在JS中可以抛出*任何*内容。明智的程序员只会抛出Error,但我们仍需进行检查
// 以满足TypeScript的要求(并标记任何不规范的代码)
const ensureError = function (thrownObject: unknown): Error {
  if (thrownObject instanceof Error) {
    return thrownObject;
  }
  return new Error(`抛出了非Error类型内容:${String(thrownObject)}`);
};
以及:
ts
try {
  // 可能抛出异常的代码
} catch (thrownObject) {
  const error = ensureError(thrownObject);
  throw error;
}

Rust Guidelines (Anchor Programs)

Rust规范(Anchor程序)

Platform Awareness

平台认知

  • Remember this is Solana not Ethereum.
    • Don't tell me about 'smart contracts' (use 'programs' instead)
    • Don't tell me about 'gas' (use 'transaction fees' instead)
    • There are no 'mempools'. Do not tell me about other things that are not relevant to Solana.
  • Token program terminology:
    • Use 'Token Extensions Program' or 'Token extensions' for the newer token program (not 'Token 2022' which is just a code name)
    • Use 'Classic Token Program' for the older token program
  • Onchain
    • Use onchain and offchain, like online and offline
    • Don't ever use 'on-chain' or 'off-chain'
  • 请记住这是Solana而非Ethereum。
    • 不要提及“智能合约”(请用“programs”替代)
    • 不要提及“gas”(请用“transaction fees”替代)
    • Solana中没有“mempools”(内存池)的概念。 不要提及任何与Solana无关的内容。
  • 代币程序术语:
    • 对于较新的代币程序,使用“Token Extensions Program”或“Token extensions”(不要使用“Token 2022”,这只是一个代号)
    • 对于旧版代币程序,使用“Classic Token Program”
  • 链上/链下
    • 使用“onchain”和“offchain”,就像“online”和“offline”一样
    • 绝对不要使用“on-chain”或“off-chain”

Anchor Version

Anchor版本

  • Write all code like the latest stable Anchor (currently 0.32.1 but there may be a newer version by the time you read this)
  • Do not use unnecessary macros that are not needed in the latest stable Anchor
  • 所有代码均需按照最新稳定版Anchor编写(当前为0.32.1,但你阅读本文时可能已有更新版本)
  • 不要使用最新稳定版Anchor中不需要的冗余宏

Anchor has silly defaults

Anchor的不合理默认设置

Every project will need an IDL.
toml
[features]
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
and if it uses SPL Tokens (like almost every Anchor project) it will need this dependency (insert whatever version is applicable):
toml
[dependencies]
anchor-spl = "0.32.1"
每个项目都需要IDL。
toml
[features]
idl-build = ["anchor-lang/idl-build", "anchor-spl/idl-build"]
如果项目使用SPL Tokens(几乎所有Anchor项目都会使用),则需要添加以下依赖(请填入适用的版本号):
toml
[dependencies]
anchor-spl = "0.32.1"

Project Structure

项目结构

  • Never modify the program ID in
    lib.rs
    or
    Anchor.toml
    when making changes
  • Create files inside the
    state
    folder for whatever state is needed
  • Create files inside the
    instructions
    or
    handlers
    folders (whichever exists) for whatever instruction handlers are needed
  • Put Account Constraints in instruction files, but ensure the names end with
    AccountConstraints
    rather than just naming them the same thing as the function
  • Handlers that are only for the admin should be in a new folder called
    admin
    inside whichever parent folder exists (
    instructions/admin/
    or
    handlers/admin/
    )
  • 修改代码时,绝不要修改
    lib.rs
    Anchor.toml
    中的程序ID
  • 所需的状态相关代码请放在
    state
    文件夹内
  • 所需的指令处理函数请放在
    instructions
    handlers
    文件夹内(以项目中已存在的文件夹为准)
  • 账户约束请放在指令文件中,但需确保约束结构体的名称以
    AccountConstraints
    结尾,而非与函数名称相同
  • 仅管理员可用的处理函数应放在现有父文件夹下的
    admin
    子文件夹中(如
    instructions/admin/
    handlers/admin/

Account Constraints

账户约束

  • Use a newline after each key in the account constraints struct, so the macro and the matching key/value have some space from other macros and their matching key/value
  • 账户约束结构体中的每个键值对后需换行,使宏与对应的键值对和其他宏及键值对之间保持间距

Bumps

Bump值

  • Use
    context.bumps.foo
    not
    context.bumps.get("foo").unwrap()
    - the latter is outdated
  • 使用
    context.bumps.foo
    而非
    context.bumps.get("foo").unwrap()
    ——后者已过时

Data Structures

数据结构

  • When making structs ensure strings and Vectors have a
    max_len
    attribute
  • Vectors have two numbers for
    max_len
    : the first is the max length of the vector, the second is the max length of the items in the vector
  • 定义结构体时,确保字符串和Vector添加
    max_len
    属性
  • Vector的
    max_len
    需要两个数值:第一个是Vector的最大长度,第二个是Vector中元素的最大长度

Space Calculation (CRITICAL - NO MAGIC NUMBERS)

空间计算(至关重要 - 禁止魔法数值)

  • Do not use magic numbers anywhere. I don't want to see
    8 + 32
    or whatever
  • Do not make constants for the sizes of various data structures
  • For
    space
    , use syntax like:
    space = SomeStruct::DISCRIMINATOR.len() + SomeStruct::INIT_SPACE,
  • All structs should have
    #[derive(InitSpace)]
    added to them, to get the
    INIT_SPACE
    trait
  • DO NOT use magic numbers
Example:
rust
#[derive(InitSpace)]
#[account]
pub struct UserProfile {
    pub authority: Pubkey,

    #[max_len(50)]
    pub username: String,

    pub bump: u8,
}

#[derive(Accounts)]
pub struct InitializeProfile<'info> {
    #[account(
        init,
        payer = authority,
        space = UserProfile::DISCRIMINATOR.len() + UserProfile::INIT_SPACE,
        seeds = [b"profile", authority.key().as_ref()],
        bump
    )]
    pub profile: Account<'info, UserProfile>,

    #[account(mut)]
    pub authority: Signer<'info>,

    pub system_program: Program<'info, System>,
}
  • 任何地方都禁止使用魔法数值。不要出现
    8 + 32
    这类写法
  • 不要为各种数据结构的大小定义常量
  • 对于
    space
    ,请使用如下语法:
    space = SomeStruct::DISCRIMINATOR.len() + SomeStruct::INIT_SPACE,
  • 所有结构体都应添加
    #[derive(InitSpace)]
    ,以获取
    INIT_SPACE
    特性
  • 禁止使用魔法数值
示例:
rust
#[derive(InitSpace)]
#[account]
pub struct UserProfile {
    pub authority: Pubkey,

    #[max_len(50)]
    pub username: String,

    pub bump: u8,
}

#[derive(Accounts)]
pub struct InitializeProfile<'info> {
    #[account(
        init,
        payer = authority,
        space = UserProfile::DISCRIMINATOR.len() + UserProfile::INIT_SPACE,
        seeds = [b"profile", authority.key().as_ref()],
        bump
    )]
    pub profile: Account<'info, UserProfile>,

    #[account(mut)]
    pub authority: Signer<'info>,

    pub system_program: Program<'info, System>,
}

Error Handling

错误处理

  • Return useful error messages
  • Write code to handle common errors like insufficient funds, bad values for parameters, and other obvious situations
  • 返回有用的错误信息
  • 编写代码处理常见错误,如资金不足、参数值无效及其他明显的异常情况

PDA Management

PDA管理

  • Add
    pub bump: u8
    to every struct stored in PDA
  • Save the bumps inside each when the struct inside the PDA is created
  • 每个存储在PDA中的结构体都需添加
    pub bump: u8
    字段
  • 创建PDA中的结构体时,保存对应的bump值

System Functions

系统函数

  • When you get the time via Clock, use
    Clock::get()?;
    rather than
    anchor_lang::solana_program::clock
  • 通过Clock获取时间时,使用
    Clock::get()?;
    而非
    anchor_lang::solana_program::clock

Git commits

Git提交

Do not add "Co-Authored-By: Claude" or similar attribution when creating git commits.
创建Git提交时,不要添加“Co-Authored-By: Claude”或类似的署名信息。

Acknowledgment

确认

  • Acknowledge these guidelines have been applied when working on this project to indicate you have read these rules and found that they do apply to this project.
  • 处理该项目时,请确认已遵循以上规范,以表明你已阅读并理解这些规则,且确认它们适用于当前项目。