Skip to content

Commit f89e041

Browse files
committed
i18n: sync and fix instructino Anchor for Dummies for Simplified Chinese
1 parent 309acdf commit f89e041

File tree

1 file changed

+138
-4
lines changed
  • src/app/content/courses/anchor-for-dummies/anchor-accounts

1 file changed

+138
-4
lines changed

src/app/content/courses/anchor-for-dummies/anchor-accounts/zh-CN.mdx

Lines changed: 138 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,63 @@ pub account: Account<'info, CustomAccountType>,
154154
pub account: Account<'info, CustomAccountType>,
155155
```
156156

157+
### 惰性账户
158+
159+
从Anchor 0.31.0开始,`LazyAccount`提供了一种更高效的方式来读取账户数据。与将整个账户反序列化到堆栈上的标准`Account`类型不同,`LazyAccount`是一个只读的、堆分配的账户,仅使用24字节的堆栈内存,并允许您选择性地加载特定字段。
160+
161+
首先在您的`Cargo.toml`中启用该功能:
162+
163+
```
164+
anchor-lang = { version = "0.31.1", features = ["lazy-account"] }
165+
```
166+
167+
现在我们可以像这样使用它:
168+
169+
```rust
170+
#[derive(Accounts)]
171+
pub struct MyInstruction<'info> {
172+
pub account: LazyAccount<'info, CustomAccountType>,
173+
}
174+
175+
#[account(discriminator = 1)]
176+
pub struct CustomAccountType {
177+
pub balance: u64,
178+
pub metadata: String,
179+
}
180+
181+
pub fn handler(ctx: Context<MyInstruction>) -> Result<()> {
182+
// Load specific field
183+
let balance = ctx.accounts.account.get_balance()?;
184+
let metadata = ctx.accounts.account.get_metadata()?;
185+
186+
Ok(())
187+
}
188+
```
189+
190+
> `LazyAccount`是只读的。尝试修改字段会导致panic,因为您操作的是引用,而不是堆栈分配的数据。
191+
192+
当CPI修改账户时,缓存的值会变得过时。因此,您需要使用`unload()`函数来刷新:
193+
194+
```rust
195+
// Load the initial value
196+
let initial_value = ctx.accounts.my_account.load_field()?;
197+
198+
// Do CPI...
199+
200+
// We still have a reference to the account from `initial_value`, drop it before `unload`
201+
drop(initial_value);
202+
203+
// Load the updated value
204+
let updated_value = ctx.accounts.my_account.unload()?.load_field()?;
205+
```
206+
157207
<ArticleSection name="Token Accounts" id="token-program" level="h2" />
158208

159209
Token Program 是 Solana Program Library (SPL) 的一部分,是用于铸造和转移任何非原生 SOL 资产的内置工具包。它提供了创建代币、铸造新供应、转移余额、销毁、冻结等指令。
160210

161211
该程序拥有两种关键账户类型:
162-
- **铸造账户**:存储特定代币的元数据:供应量、小数位、铸造权限、冻结权限等。
163-
- **代币账户**:为特定所有者持有该铸造代币的余额。只有所有者可以减少余额(转移、销毁等),但任何人都可以向账户发送代币,从而增加其余额。
212+
- **铸造账户 (Mint Account)**:存储特定代币的元数据:供应量、小数位、铸造权限、冻结权限等。
213+
- **代币账户 (Token Account)**:为特定所有者持有该铸造代币的余额。只有所有者可以减少余额(转移、销毁等),但任何人都可以向账户发送代币,从而增加其余额。
164214

165215
### Anchor 中的 Token 账户
166216

@@ -351,7 +401,7 @@ pub struct InstructionAccounts<'info> {
351401
}
352402
```
353403

354-
<ArticleSection name="Custom Account Validation" id="custom-account-validation" level="h2" />
404+
<ArticleSection name="自定义账户验证" id="custom-account-validation" level="h2" />
355405

356406
Anchor 提供了一套强大的约束,可以直接应用在 `#[account]` 属性中。这些约束有助于确保账户的有效性,并在指令逻辑运行之前,在账户级别强制执行程序规则。以下是可用的约束:
357407

@@ -434,4 +484,88 @@ pub account: Account<'info, CustomAccountType>,
434484
pub account: Account<'info, CustomAccountType>,
435485
```
436486

437-
这些约束可以组合起来,为您的账户创建强大的验证规则。通过在账户级别进行验证,您可以将安全检查与账户定义紧密结合,避免在指令逻辑中散布 `require!()` 调用。
487+
这些约束可以组合起来,为您的账户创建强大的验证规则。通过在账户级别进行验证,您可以将安全检查与账户定义紧密结合,避免在指令逻辑中散布 `require!()` 调用。
488+
489+
<ArticleSection name="剩余账户" id="remaining-accounts" level="h2" />
490+
491+
在编写程序时,指令账户的固定结构有时无法提供程序所需的灵活性。
492+
493+
剩余账户通过允许您传递超出定义指令结构的额外账户,解决了这一问题,从而根据运行时条件实现动态行为。
494+
495+
### 实现指南
496+
497+
传统的指令定义要求您明确指定将使用哪些账户:
498+
499+
```rust
500+
#[derive(Accounts)]
501+
pub struct Transfer<'info> {
502+
pub from: Account<'info, TokenAccount>,
503+
pub to: Account<'info, TokenAccount>,
504+
pub authority: Signer<'info>,
505+
}
506+
```
507+
508+
这对于单一操作非常有效,但如果您希望在一个指令中执行多次代币转账,该怎么办?您需要多次调用指令,这会增加交易成本和复杂性。
509+
510+
剩余账户允许您传递不属于固定指令结构的额外账户,这意味着您的程序可以遍历这些账户并动态应用重复逻辑。
511+
512+
与其为每次转账单独定义指令,您可以设计一个指令来处理 "N" 次转账:
513+
514+
```rust
515+
#[derive(Accounts)]
516+
pub struct BatchTransfer<'info> {
517+
pub from: Account<'info, TokenAccount>,
518+
pub to: Account<'info, TokenAccount>,
519+
pub authority: Signer<'info>,
520+
}
521+
522+
pub fn batch_transfer(ctx: Context<BatchTransfer>, amounts: Vec<u64>) -> Result<()> {
523+
// Handle the first transfer using fixed accounts
524+
transfer_tokens(&ctx.accounts.from, &ctx.accounts.to, amounts[0])?;
525+
526+
let remaining_accounts = &ctx.remaining_accounts;
527+
528+
// CRITICAL: Validate remaining accounts schema
529+
// For batch transfers, we expect pairs of accounts
530+
require!(
531+
remaining_accounts.len() % 2 == 0,
532+
TransferError::InvalidRemainingAccountsSchema
533+
);
534+
535+
// Process remaining accounts in groups of 2 (from_account, to_account)
536+
for (i, chunk) in remaining_accounts.chunks(2).enumerate() {
537+
let from_account = &chunk[0];
538+
let to_account = &chunk[1];
539+
let amount = amounts[i + 1];
540+
541+
// Apply the same transfer logic to remaining accounts
542+
transfer_tokens(from_account, to_account, amount)?;
543+
}
544+
545+
Ok(())
546+
}
547+
```
548+
549+
批量处理指令意味着:
550+
- 更小的指令大小:重复的账户和数据不需要包含在内
551+
- 更高的效率:每次 CPI 消耗 1000 CU,这意味着使用您程序的用户如果需要执行批量指令,只需调用一次,而不是调用三次
552+
553+
> 剩余账户作为 `UncheckedAccount` 传递,这意味着 Anchor 不会对其进行任何验证。始终验证 `RemainingAccountSchema` 和底层账户。
554+
555+
### 客户端实现
556+
557+
我们可以通过 Anchor SDK 轻松传递剩余账户,一旦我们执行 `anchor build`。由于这些是“原始”账户,我们需要指定它们是否需要作为签名者和/或可变账户传递,如下所示:
558+
559+
```ts
560+
await program.methods.someMethod().accounts({
561+
// some accounts
562+
})
563+
.remainingAccounts([
564+
{
565+
isSigner: false,
566+
isWritable: true,
567+
pubkey: new Pubkey().default
568+
}
569+
])
570+
.rpc();
571+
```

0 commit comments

Comments
 (0)