solana-anchor-claude-skill
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCoding Guidelines
编码规范
Apply these rules to ensure code quality, maintainability, and adherence to project standards.
遵循以下规则,确保代码质量、可维护性,并符合项目标准。
Success Criteria
成功标准
-
Before declaring success or celebrating, run. If the tests fail, there is more work to do. Don't stop until
npm testpasses on the code you have made.npm test -
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:
- Anchor: https://www.anchor-lang.com/docs
- Solana Kite: https://solanakite.org
- Solana Kit: https://solanakit.com
- Agave (Solana CLI): https://docs.anza.xyz/ (Anza makes the Solana CLI and Agave.
- Switchboard (if used): https://docs.switchboard.xyz/docs-by-chain/solana-svm
- Arcium (if used): https://docs.arcium.com/developers
请使用以下官方文档资源:
- Anchor: https://www.anchor-lang.com/docs
- Solana Kite: https://solanakite.org
- Solana Kit: https://solanakit.com
- Agave (Solana CLI): https://docs.anza.xyz/(Anza开发了Solana CLI和Agave)
- Switchboard(若使用): https://docs.switchboard.xyz/docs-by-chain/solana-svm
- Arcium(若使用): https://docs.arcium.com/developers
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 letting the next programmer know when they can delete the workaround)
TODO
- 绝不能误导阅读代码的人。误导行为包括:
- 变量名称与实际用途不符
- 注释无法准确描述代码内容或已过时
- 临时解决方案未通过注释标记(需添加,告知后续开发者可删除该方案的时机)
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 (), items within arrays should be the singular (
shoes)shoes.forEach((shoe) => {...}) - Functions should be verby, like or
calculateFoogetBar - Avoid abbreviations, use full words (e.g., rather than
context)ctx - Never use for something thrown
e - Name a transaction some variant of . Name instructions some variant of
transaction. Name signatures some variant ofinstruction. Do not confuse them - eg if the type looks like an instruction, you should not call it a 'transaction' because that is deceptive.signature
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) => {...}) - 函数名称应使用动词形式,如或
calculateFoogetBar - 避免缩写,使用完整单词(例如用而非
context)ctx - 永远不要用表示抛出的异常
e - 交易应命名为包含的变体,指令应命名为包含
transaction的变体,签名应命名为包含instruction的变体。不要混淆它们——例如,若类型是指令,就不能将其命名为“transaction”,这会造成误导。signature
你仍可添加注释提供额外上下文,但需注意避免注释内容可通过优化变量命名更好地传达的情况。
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 or
// In production we'd do this differentlyor**Implementation incomplete** - Needs program config handling and proper PDA derivationsin the final code you produce, or functions that return placeholder data. Instead: do the fucking work.**WORK IN PROGRESS** - 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 unless it's needed, as we use to run most typescript and it doesn't usually need one. If you do need a , 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.jsontsxtsconfig.json除非必要,否则避免使用,因为我们主要使用运行TypeScript代码,通常不需要该文件。若确实需要,请在文件顶部说明原因,且可使用最新版本的ECMAScript/JavaScript(最高至2023版本)。
tsconfig.jsontsxtsconfig.jsonAsync/await
Async/await
Favor / and over or or using callbacks for flow control. has top level so you don't need to wrap top level in IIFEs.
asyncawaittry/catch.then().catch()tsxawaitawait优先使用/和,而非、或回调函数进行流程控制。支持顶层,无需将顶层包裹在IIFE中。
asyncawaittry/catch.then().catch()tsxawaitawaitType System
类型系统
- Always use , never use
Array<item>for consistency with other generic syntax likeitem[],Promise<T>, andMap<K, V>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 version 1 code. Do not make new code using
@solana/web3.jspackage. 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.@coral-xyz/anchor - Use Kite's to turn seeds into PDAs and bumps
connection.getPDAAndBump() - 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 npm package.
bs58
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'.
- 不要编写新的版本1代码。不要使用
@solana/web3.js包编写新代码。不要用web3.js版本1代码替换Solana Kit。web3.js版本1已属于遗留代码,最终应被移除。Solana Kit曾被称为web3.js版本2。请使用Solana Kit,优先通过Solana Kite使用。@coral-xyz/anchor - 使用Kite的将种子转换为PDA和bump值
connection.getPDAAndBump() - 在Solana Kit中,你可以通过Codama从IDL生成TS客户端来创建指令。使用以下命令即可为已安装的IDL生成Codama客户端:
npx create-codama-clients- 不要使用npm包。
bs58
错误示例:
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 directory
tests - Use the Node.js inbuilt test and assertion libraries (then start the tests using instead of
tsx)ts-mocha
Unit testing imports:
typescript
import { before, describe, test } from "node:test";
import assert from "node:assert";- Use rather than
testit
- 在目录下编写TypeScript单元测试
tests - 使用Node.js内置的测试和断言库(通过启动测试,而非
tsx)ts-mocha
单元测试导入示例:
typescript
import { before, describe, test } from "node:test";
import assert from "node:assert";- 使用而非
testit
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 or
lib.rswhen making changesAnchor.toml - Create files inside the folder for whatever state is needed
state - Create files inside the or
instructionsfolders (whichever exists) for whatever instruction handlers are neededhandlers - Put Account Constraints in instruction files, but ensure the names end with rather than just naming them the same thing as the function
AccountConstraints - Handlers that are only for the admin should be in a new folder called inside whichever parent folder exists (
adminorinstructions/admin/)handlers/admin/
- 修改代码时,绝不要修改或
lib.rs中的程序IDAnchor.toml - 所需的状态相关代码请放在文件夹内
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 not
context.bumps.foo- the latter is outdatedcontext.bumps.get("foo").unwrap()
- 使用而非
context.bumps.foo——后者已过时context.bumps.get("foo").unwrap()
Data Structures
数据结构
- When making structs ensure strings and Vectors have a attribute
max_len - Vectors have two numbers for : the first is the max length of the vector, the second is the max length of the items in the vector
max_len
- 定义结构体时,确保字符串和Vector添加属性
max_len - Vector的需要两个数值:第一个是Vector的最大长度,第二个是Vector中元素的最大长度
max_len
Space Calculation (CRITICAL - NO MAGIC NUMBERS)
空间计算(至关重要 - 禁止魔法数值)
- Do not use magic numbers anywhere. I don't want to see or whatever
8 + 32 - Do not make constants for the sizes of various data structures
- For , use syntax like:
spacespace = SomeStruct::DISCRIMINATOR.len() + SomeStruct::INIT_SPACE, - All structs should have added to them, to get the
#[derive(InitSpace)]traitINIT_SPACE - 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 - 不要为各种数据结构的大小定义常量
- 对于,请使用如下语法:
spacespace = 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 to every struct stored in PDA
pub bump: u8 - 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 rather than
Clock::get()?;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.
- 处理该项目时,请确认已遵循以上规范,以表明你已阅读并理解这些规则,且确认它们适用于当前项目。