记录一次Rust借用的生命周期问题

Unstake的结构体声明如下:

1
2
3
4
5
6
7
8
9

#[derive(Accounts)]
pub struct UnStake<'info> {
#[account(
mint::token_program = token_program,
)]
pub stake_token_mint: InterfaceAccount<'info, Mint>,
}

我想编写一个helper函数, 用来为 Unstake创建 CpiContext, 像这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
impl<'info> UnStake<'info> {
pub fn transfer_ctx(&self) -> CpiContext<'_, '_, '_, 'info, TransferChecked<'info>> {
let seeds: &[&[&[u8]]] =&[ &[
b"POOL_AUTH".as_ref(),
self.stake_token_mint.key().as_ref(),
]];
CpiContext::new_with_signer(
self.token_program.to_account_info(),
TransferChecked {
from: self.receive_stake_token_ata.to_account_info(),
mint: self.stake_token_mint.to_account_info(),
to: self.user_stake_token_ata.to_account_info(),
authority: self.pool_authority.to_account_info(),
},
seeds,
)
}
}

编译报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
error[E0515]: cannot return value referencing temporary value
--> programs/anchor-token-staking-yqq/src/instructions/unstake.rs:218:9
|
216 | self.stake_token_mint.key().as_ref(),
| --------------------------- temporary value created here
217 | ]];
218 | / CpiContext::new_with_signer(
219 | | self.token_program.to_account_info(),
220 | | TransferChecked {
221 | | from: self.receive_stake_token_ata.to_account_info(),
... |
226 | | seeds,
227 | | )
| |_________^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing temporary value
--> programs/anchor-token-staking-yqq/src/instructions/unstake.rs:218:9
|
214 | let seeds: &[&[&[u8]]] =&[ &[
| _____________________________________-
215 | | b"POOL_AUTH".as_ref(),
216 | | self.stake_token_mint.key().as_ref(),
217 | | ]];
| |_________- temporary value created here
218 | / CpiContext::new_with_signer(
219 | | self.token_program.to_account_info(),
220 | | TransferChecked {
221 | | from: self.receive_stake_token_ata.to_account_info(),
... |
226 | | seeds,
227 | | )
| |_________^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing temporary value
--> programs/anchor-token-staking-yqq/src/instructions/unstake.rs:218:9
|
214 | let seeds: &[&[&[u8]]] =&[ &[
| __________________________________-
215 | | b"POOL_AUTH".as_ref(),
216 | | self.stake_token_mint.key().as_ref(),
217 | | ]];
| |__________- temporary value created here
218 | / CpiContext::new_with_signer(
219 | | self.token_program.to_account_info(),
220 | | TransferChecked {
221 | | from: self.receive_stake_token_ata.to_account_info(),
... |
226 | | seeds,
227 | | )
| |_________^ returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.
warning: `anchor-token-staking-yqq` (lib) generated 1 warning
error: could not compile `anchor-token-staking-yqq` (lib) due to 3 previous errors; 1 warning emitted

原因: 返回局部变量的引用. 即返回临时变量seeds的引用, 而seeds在函数结束之后就被释放了。

  • 深层次的原因:seeds中包含了对 self.stake_token_mint.key().as_ref() 引用,而 self.stake_token_mint

下面这段代码是可以的, 因为 "POOL_AUTH"具有静态生命周期,在整个运行期间都有效,因此,seeds的生命周期静态生命周期。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
impl<'info> UnStake<'info> {
pub fn transfer_ctx(&self) -> CpiContext<'_, '_, '_, 'info, TransferChecked<'info>> {
let seeds: &[&[&[u8]]] = &[&[b"POOL_AUTH"]];
CpiContext::new_with_signer(
self.token_program.to_account_info(),
TransferChecked {
from: self.receive_stake_token_ata.to_account_info(),
mint: self.stake_token_mint.to_account_info(),
to: self.user_stake_token_ata.to_account_info(),
authority: self.pool_authority.to_account_info(),
},
seeds,
)
}
}

为什么? 看下面简化的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Example<'a> {
data: &'a str,
}

impl<'info> Example<'info> {
fn problematic(&self) -> &'info str {
let local = self.data; // local 的生命周期被限制在函数内
local // 错误:尝试返回一个生命周期比函数更短的引用
}

fn works(&self) -> &'info str {
self.data // 直接返回,没问题
}
}

局部变量的生命周期:

  • 在Rust中,局部变量的生命周期默认仅限于它们被定义的作用域内。即使这个局部变量包含了对生命周期更长的数据的引用,变量本身的生命周期仍然被限制在函数内。
  • 引用的生命周期 vs 变量的生命周期: 虽然 self.stake_token_mint 的生命周期是 'info,但当我们创建一个包含这个引用的新局部变量时,这个新变量的生命周期被限制在函数内。
  • 生命周期的传播: 生命周期并不会自动从被引用的数据传播到包含引用的新数据结构。
  • 编译器的保守处理: 编译器会保守地处理生命周期,除非明确指定,否则它不会假设局部变量的生命周期比函数更长。

那么,不使用局部变量 seeds , 而是直接传参, 同样报错:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
impl<'info> UnStake<'info> {
pub fn transfer_ctx(&self) -> CpiContext<'_, '_, '_, 'info, TransferChecked<'info>> {
// let seeds: &[&[&[u8]]] =;
CpiContext::new_with_signer(
self.token_program.to_account_info(),
TransferChecked {
from: self.receive_stake_token_ata.to_account_info(),
mint: self.stake_token_mint.to_account_info(),
to: self.user_stake_token_ata.to_account_info(),
authority: self.pool_authority.to_account_info(),
},
&[&[b"POOL_AUTH", self.stake_token_mint.key().as_ref()]],
)
}
}

self.stake_token_mint.key() 会创建应该临时变量, 这个临时变量的生命周期也是仅限函数内部,因此参数中包含对于一个 临时变量的引用,导致生命周期不匹配的问题

最终的解决方案:

  • seeds作为参数从外部传入
  • 使用生命周期注解'a, 注解selfseeds ,确保seeds在函数执行期间有效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
impl<'info> UnStake<'info> {
pub fn transfer_ctx<'a>(
&'a self,
seeds: &'a [&[&[u8]]],
) -> CpiContext<'_, '_, '_, 'info, TransferChecked<'info>> {
CpiContext::new_with_signer(
self.token_program.to_account_info(),
TransferChecked {
from: self.receive_stake_token_ata.to_account_info(),
mint: self.stake_token_mint.to_account_info(),
to: self.user_stake_token_ata.to_account_info(),
authority: self.pool_authority.to_account_info(),
},
seeds,
)
}
}

16_Solana_程序安全实践指南

官方列出的安全例子:

汇总如下:

  • 0-signer-authorization: 非法权限调用攻击,调用者不是交易签名者
    • 使用 Anchor的Signer账户类型检查交易签名者
  • 1-account-data-matching: 账户&数据不一致,伪造攻击
    • 使用 Anchor的约束,检查权限是否一致
  • 2-owner-check: 权限, owner不一致
  • 3-type-cosplay: 数据类型伪造
  • 4-initialization: 重复初始化攻击 + 初始化抢跑攻击
    • 重新初始化攻击: 使用Anchor的init,
    • 初始化抢跑攻击: 使用 Anchor的init, 重复初始化会报错,因为就会发现是否被抢跑
  • 5-arbitrary-cpi: CPI乱调用(programId不一致, PDA的owner不是该程序), 可以进行伪造PDA攻击
    • 使用Anchor的CpiContext进行CPI调用
  • 6-duplicate-mutable-accounts: 重复修改账户(2个账户数据结构相同,传入相同的值)
    • 注意账户&指令中包含2个相同的数据结构的账户,要做检查key检查
  • 7-bump-seed-canonicalization: PDA碰撞攻击(通过传入 seeds和bump)
  • 8-pda-sharing: (常见)伪造PDA攻击, PDA权限不清晰(共享的PDA),攻击者可以伪造一个PDA
    • 原因: seeds 中字段不唯一,没有跟账户关联起来
  • 9-closing-accounts: 账户关闭攻击(重入攻击),
    • 要在指令执行结束后,关闭一个(临时)账户, 直接使用Anchor的 close=destination 约束即可
  • 10-sysvar-address-checking: 系统变量地址检查(PDA伪造)
    • 使用Anchor的Sysvar获取系统变量

15_Solana_Token Extension

https://www.soldev.app/course/intro-to-token-extensions-program

  • Token Extension Program 是 Token Program 的超集

  • Token Program 和 Token Extension Program 是2个程序

    • 两个程序的地址,不可以互换(not interchangeable)
  • Token Extension 16种功能:

    https://spl.solana.com/token-2022/extensions

    • Account:
      • Memo: 转账时需要增加备注
      • Immutable ownership: ATA权限不可以转移
        • Token 2022的ATA权限默认是不可转移的
      • Default account state: 设置默认的账户状态,如:默认冻结
      • CPI guard: 对CPI做一些限制操作
    • Mint
      • Transfer fees: 项目方可以加入抽水功能
      • Closing mint: 关闭mint , 方便跑路
        • 需要supply为0, 即,需要销毁所有token之后才能关闭mint
      • Interest-bearing tokens: 生息, 非常适合staking项目
      • Non-transferable tokens: 不可转移, 适合做灵魂绑定(Soul-Bound)
      • Permanent delegate: 永久代理,项目方可以控制一切账户,非常适合做中心化集权场景
      • Transfer hook: token转账的钩子, 可以增加自定义相关回调
      • Metadata pointer: 为token增加metadata
        • 一般与Metadata Extension一起用, 也可以与外部账户(Metaplex)联合使用
      • Metadata: 为token增加metadata ,一般和 metadta pointer一起用
      • Group pointer: 群组,适合做合集, 如NFT合集
      • Group: 同上
      • Member pointer : 成员
      • Member: 同上
      • Confidential transfers: 私密交易

使用命令行spl-token使用 Token 2022

spl-token –create-token –help

创建 close authority token

获取solana配置信息,设置 devnet

1
2
3
4
5
6
$ solana config get
Config File: /home/yqq/.config/solana/cli/config.yml
RPC URL: https://api.devnet.solana.com
WebSocket URL: wss://api.devnet.solana.com/ (computed)
Keypair Path: /home/yqq/.config/solana/id.json
Commitment: confirmed

创建 close authority token

注意: spl-token 默认使用的 Token Program的program id, 如需使用Token 2022,则需要制定program id

  • TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb 是 Token 2022的program id
  • TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA 是 Token Program的program id
1
spl-token create-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --enable-close

创建ATA账户

1
2
spl-token create-account --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

mint token, 注意必须指定program id, 因为 spl-token 默认使用旧版Token Program作为program id

1
2
3
4
5
6
7
8
spl-token mint --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb CisqmfLH8R2JnSYSA8tgW8LSG5hQPogYZKxxHv6H5aMq 1000000000  4tQwDVgmNYPxrdhmqVAza9qefkmPPhBrF1h75oMrfd2Q

Minting 1000000000 tokens
Token: CisqmfLH8R2JnSYSA8tgW8LSG5hQPogYZKxxHv6H5aMq
Recipient: 4tQwDVgmNYPxrdhmqVAza9qefkmPPhBrF1h75oMrfd2Q

Signature: YdAcA3VJ9ehFRKzZCbkwMvEMxVAMPVB7X7Cuu4pS7pXcUKjGWSB7siCywFG1wJGXqQVXK3HuTSje2dytrmHKFJg

直接关闭会报错,因为此时 supply不是0, 必须先销毁,然后才能close

1
2
$ spl-token close-mint CisqmfLH8R2JnSYSA8tgW8LSG5hQPogYZKxxHv6H5aMq
Error: "Mint CisqmfLH8R2JnSYSA8tgW8LSG5hQPogYZKxxHv6H5aMq still has 1000000000000000000 outstanding tokens; these must be burned before closing the mint."

销毁token

1
2
3
4
5
6
7
spl-token burn --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb 4tQwDVgmNYPxrdhmqVAza9qefkmPPhBrF1h75oMrfd2Q 1000000000

Burn 1000000000 tokens
Source: 4tQwDVgmNYPxrdhmqVAza9qefkmPPhBrF1h75oMrfd2Q

Signature: K3v2mkrrdyqym9RHFWT2yo2RQ89zcZQaR6V6RW37ie44ob2Du4arTTym1FimpHLQ9FTHPd8zhdXxnjqU8tGWzZp

查看mint的信息, 此时 supply 为0, 可以进行close

1
2
3
4
5
6
7
8
9
10
11
12
$ spl-token display CisqmfLH8R2JnSYSA8tgW8LSG5hQPogYZKxxHv6H5aMq

SPL Token Mint
Address: CisqmfLH8R2JnSYSA8tgW8LSG5hQPogYZKxxHv6H5aMq
Program: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Supply: 0
Decimals: 9
Mint authority: 7DxeAgFoxk9Ha3sdciWE4G4hsR9CUjPxsHAxTmuCJrop
Freeze authority: (not set)
Extensions
Close authority: 7DxeAgFoxk9Ha3sdciWE4G4hsR9CUjPxsHAxTmuCJrop

进行close

1
2
3
4
$ spl-token close-mint CisqmfLH8R2JnSYSA8tgW8LSG5hQPogYZKxxHv6H5aMq

Signature: 2h9bLrRCcK1bSbavdtKHrHLV2FVJVtEQiCUXBDKZXwZwnGFSZMhiYLDpRCJ7pxgb4KKxc75zUbCgeo9SeM7sjheH

创建ATA权限不可转移的token 2022

创建 token, token 2022 的ATA默认都是不可转移的,因此不需要制定额外参数

注意: spl-token 默认使用的 Token Program的program id, 如需使用Token 2022,则需要制定program id

  • TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb 是 Token 2022的program id
  • TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA 是 Token Program的program id
1
2
3
4
5
6
7
8
$ spl-token create-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Creating token HcGkiji8KimiZPTBf3SFCapAoR9NP63LdZtpv3719wdw under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

Address: HcGkiji8KimiZPTBf3SFCapAoR9NP63LdZtpv3719wdw
Decimals: 9

Signature: 411LLtiDPB6Xgpq4kqsqp4K3atJCsTuWAHnAYaV6YaaMPDCYVTkRi6PWra7ixxMtWTbwyGeBxiZmfBfLjfeyZ6Q5

创建 ATA 账户

1
2
3
4
$ spl-token create-account --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb  HcGkiji8KimiZPTBf3SFCapAoR9NP63LdZtpv3719wdw
Creating account GfuBt164MUSThb3ZnhLfra8bbHzzrCvruXs5p7rC23LW

Signature: 3eRBFR52dhVBtad7sZs9s2i2h9Lw6W9TGvDdRQJ9DAiuEZPYoaHPEnqfCuoGzX6mDfTcPiP4wW7bdLzSQHtEzZLT

mint token

1
2
3
4
5
6
7
$ spl-token mint --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb HcGkiji8KimiZPTBf3SFCapAoR9NP63LdZtpv3719wdw 10000000 GfuBt164MUSThb3ZnhLfra8bbHzzrCvruXs5p7rC23LW
Minting 10000000 tokens
Token: HcGkiji8KimiZPTBf3SFCapAoR9NP63LdZtpv3719wdw
Recipient: GfuBt164MUSThb3ZnhLfra8bbHzzrCvruXs5p7rC23LW

Signature: 2ffKaX7XsK71GbpehqCgqsx9qVdGXSA28umQQ7guXN7Pv9ZzviRpCNyRuoPmFGwAtHANGzTs8TkWDMXJUT4m6vhP

查看余额

1
2
3
$ spl-token balance HcGkiji8KimiZPTBf3SFCapAoR9NP63LdZtpv3719wdw
10000000

查看ATA账户信息, 可以看到 Immutable owner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ spl-token display GfuBt164MUSThb3ZnhLfra8bbHzzrCvruXs5p7rC23LW

SPL Token Account
Address: GfuBt164MUSThb3ZnhLfra8bbHzzrCvruXs5p7rC23LW
Program: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Balance: 10000000
Decimals: 9
Mint: HcGkiji8KimiZPTBf3SFCapAoR9NP63LdZtpv3719wdw
Owner: 7DxeAgFoxk9Ha3sdciWE4G4hsR9CUjPxsHAxTmuCJrop
State: Initialized
Delegation: (not set)
Close authority: (not set)
Extensions:
Immutable owner

创建 灵魂绑定代币

创建 token,

  • --decimals 0
  • --enable-metadata
  • --enable-non-transferable
1
2
3
4
5
6
7
8
9
$ spl-token create-token --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb --decimals 0 --enable-metadata --enable-non-transferable
Creating token 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K under program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
To initialize metadata inside the mint, please run `spl-token initialize-metadata 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K <YOUR_TOKEN_NAME> <YOUR_TOKEN_SYMBOL> <YOUR_TOKEN_URI>`, and sign with the mint authority.

Address: 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K
Decimals: 0

Signature: 4z1gNiFfLAkoe9RQD84N3vHh88ZNegZs2zL81ve1uKG8ijT6VGK4yuTYwjmLLCLgAXxRvCSUoMChneQZRCZXR7Wz

初始化metadata

1
2
3
4
$ spl-token initialize-metadata 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K YQQTOKEN YT https://arweave.net/7q03FecPFE5JBPDakJFDS7xvdKqw5NSlNPUFZOYVVlk

Signature: 3zxSfkAg32KavFJ3UNXAMs4KyogZwRNDjDWmD1z8PmmHLHsDaYinwpQwrsC7EnqFNSSTc5e6ohMaUvAT7N5UowbZ

查看token信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
$ spl-token display 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K

SPL Token Mint
Address: 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K
Program: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
Supply: 0
Decimals: 0
Mint authority: 7DxeAgFoxk9Ha3sdciWE4G4hsR9CUjPxsHAxTmuCJrop
Freeze authority: (not set)
Extensions
Non-transferable
Metadata Pointer:
Authority: 7DxeAgFoxk9Ha3sdciWE4G4hsR9CUjPxsHAxTmuCJrop
Metadata address: 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K
Metadata:
Update Authority: 7DxeAgFoxk9Ha3sdciWE4G4hsR9CUjPxsHAxTmuCJrop
Mint: 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K
Name: YQQTOKEN
Symbol: YT
URI: https://arweave.net/7q03FecPFE5JBPDakJFDS7xvdKqw5NSlNPUFZOYVVlk

更新metadata

1
2
3
4
$ spl-token update-metadata 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K name YQQNFT

Signature: 2xRQhsQiyrG8FVe3JHSFsDYz4gy1RPD16ECZWBpfdCwRpCgnTUzEhDJJ9QNRmR4Qnga2xTua2jrYhy6PFizigun3

创建 ATA

1
2
3
4
5
$ spl-token create-account --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K
Creating account 64Sxa26sViFh9JKFM6tm7dEib3hLTxbRXvARjjLTCmeG

Signature: 3HvNKDiPa6QcihbgUB4pVsojtmq7khwqCDdTb1iwfwdbuexe3s3PNCipEL8MMQMCwFYEGFNnmdfohXJ6weUCZ4tw

mint token

1
2
3
4
5
6
7
$ spl-token mint --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K 1 64Sxa26sViFh9JKFM6tm7dEib3hLTxbRXvARjjLTCmeG
Minting 1 tokens
Token: 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K
Recipient: 64Sxa26sViFh9JKFM6tm7dEib3hLTxbRXvARjjLTCmeG

Signature: FijHaPESJUG4PgMWjxFcPwvH7GkvL1feQeg8KKHRsS2WmfDEWkutWniEFMyDNg76acU6bUeaQ97ywtWWW1Y8SbF

尝试转移 Token, 报错Transfer is disabled for this mint

1
2
3
4
5
6
7
8
9

$ spl-token transfer --program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb 7R6uaZgMZgmfBRLJXXYHPRV8oysDokv8FHrZ7Td1xo5K 1 38jEaxphBTa3NEg4K6nG8Zgs6eVsSsr9AoSZCfax2pH8 --fund-recipient
Transfer 1 tokens
Sender: 64Sxa26sViFh9JKFM6tm7dEib3hLTxbRXvARjjLTCmeG
Recipient: 38jEaxphBTa3NEg4K6nG8Zgs6eVsSsr9AoSZCfax2pH8
Recipient associated token account: 3XX7DysVrERAeTYFczEoKtwxqH6QqxWfUBcUBaZy1GZ4
Funding recipient: 3XX7DysVrERAeTYFczEoKtwxqH6QqxWfUBcUBaZy1GZ4
Error: Client(Error { request: Some(SendTransaction), kind: RpcError(RpcResponseError { code: -32002, message: "Transaction simulation failed: Error processing Instruction 1: custom program error: 0x25", data: SendTransactionPreflightFailure(RpcSimulateTransactionResult { err: Some(InstructionError(1, Custom(37))), logs: Some(["Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL invoke [1]", "Program log: CreateIdempotent", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]", "Program log: Instruction: GetAccountDataSize", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 3064 of 22071 compute units", "Program return: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb rgAAAAAAAAA=", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", "Program 11111111111111111111111111111111 invoke [2]", "Program 11111111111111111111111111111111 success", "Program log: Initialize the associated token account", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]", "Program log: Instruction: InitializeImmutableOwner", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 1924 of 14077 compute units", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [2]", "Program log: Instruction: InitializeAccount3", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 5815 of 9763 compute units", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb success", "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL consumed 29823 of 33467 compute units", "Program ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL success", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb invoke [1]", "Program log: Instruction: TransferChecked", "Program log: Transfer is disabled for this mint", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb consumed 3644 of 3644 compute units", "Program TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb failed: custom program error: 0x25"]), accounts: None, units_consumed: Some(33467), return_data: None, inner_instructions: None }) }) })


在客户端中使用 Token 2022

https://www.soldev.app/course/token-extensions-in-the-client

  • spl-token默认使用 Token Program, 除非明确指定使用Token Programs Extension
    • Token Program: TOKEN_PROGRAM_ID
    • Token 2022: TOKEN_2022_PROGRAM_ID

示例代码

1
2
3
4
5
6
7
8
9
10
const mint = await createMint(
connection,
payer,
payer.publicKey,
payer.publicKey,
decimals,
undefined,
{ commitment: connection.commitment },
tokenProgramId // 指定 Program Id 即可
);

在Anchor使用 Token2022

在Anchor中使用 interface 类型来将 Token ProgramToken 2022 融合到一起

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
use {
anchor_lang::prelude::*,
anchor_spl::{token_interface},
};

#[derive(Accounts)]
pub struct Example<'info>{
// Token account
#[account(
token::token_program = token_program
)]
pub token_account: InterfaceAccount<'info, token_interface::TokenAccount>,
// Mint account
#[account(
mut,
mint::token_program = token_program
)]
pub mint_account: InterfaceAccount<'info, token_interface::Mint>,
pub token_program: Interface<'info, token_interface::TokenInterface>,
}
  • Interface: 是 Program 的wrapper支持多种Program
  • TokenInterface: 支持 Token ProgramToken 2022, 且仅支持这2种,如果传入其他的程序id会报错
  • InterfaceAccount: 和 Interface 类似,也是一个wrapper, 用于 AccountInfo. InterfaceAccount
  • Copyrights © 2021-2024 youngqqcn

请我喝杯咖啡吧~