0%

StudyRecord-Solana上的代币

代币是代表对各种资产所有权的数字资产。 代币化使得财产权的数字化成为可能,是管理 可替代和不可替代资产的基本组成部分。

  • 可替代代币: 代表同类型和同价值的可互换和可分割资产(例如 USDC)。
  • 不可替代代币(NFT): 代表不可分割资产的所有权(例如艺术品)。

本节将介绍代币在 Solana 上的基本表示方式。这些代币被称为 SPL (Solana Program Library) 代币。

  • 代币程序(Token Program): 包含与网络上的代币(包括可替代和不可替代)交互的所有指令逻辑。

  • 铸造账户(Mint Account): 代表一种特定类型的代币,并存储关于代币的全局元数据,如总供应量和铸造权限(有权创建新代币单位的地址)。

  • 代币账户(Token Account): 跟踪特定地址拥有的特定类型代币(铸造账户)的单位数量。

关键点:

  • 代币代表对可替代(可互换)或不可替代(唯一)资产的所有权。

  • 代币程序包含与网络上可替代和不可替代代币交互的所有指令。

  • 代币扩展程序是代币程序的新版本,包含额外功能,同时保持相同的核心功能。

  • 铸造账户代表网络上的唯一代币,并存储全局元数据,如总供应量。

  • 代币账户跟踪特定铸造账户的代币的个人所有权。

  • 关联代币账户( Associated Token Account)是使用所有者和铸造账户地址派生的地址 创建的代币账户。

代币程序:

代币程序包含与网络上的代币(包括可替代和不可替代)交互的所有指令逻辑。
Solana 上的所有代币实际上都是由代币程序拥有的数据账户。

可以在这里找到代币程序指令的完整列表

20241209180449

一些常用的指令包括:

  • InitializeMint: 创建一个新的铸造账户以代表一种新的代币类型。

  • InitializeAccount: 创建一个新的代币账户以持有特定类型代币(铸造)。

  • MintTo: 创建特定类型代币的新单位并将其添加到代币账户中。这会增加代币的供应量,并且只能由铸造账户的铸造权限执行。

  • Transfer: 将特定类型代币的单位从一个代币账户转移到另一个代币账户。

铸造账户:

Solana 上的代币通过由代币程序拥有的铸造账户的地址唯一标识。该账户实际上是特定代币的全局计数器,并存储以下数据:

  • 供应量:代币的总供应量

  • 小数位:代币的小数精度

  • 铸造权限:有权创建新代币单位的账户,从而增加供应量

  • 冻结权限:有权冻结代币账户中代币转移的账户

20241209180819
每个铸造账户存储的完整详细信息包括以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pub struct Mint {
/// Optional authority used to mint new tokens. The mint authority may only
/// be provided during mint creation. If no mint authority is present
/// then the mint has a fixed supply and no further tokens may be
/// minted.
pub mint_authority: COption<Pubkey>,
/// Total supply of tokens.
pub supply: u64,
/// Number of base 10 digits to the right of the decimal place.
pub decimals: u8,
/// Is `true` if this structure has been initialized
pub is_initialized: bool,
/// Optional authority to freeze token accounts.
pub freeze_authority: COption<Pubkey>,
}

代币账户:

为了跟踪每个特定代币单位的个人所有权,必须创建另一种由代币程序拥有的数据账户。该 账户被称 为代币账户。

代币账户中最常引用的数据包括以下内容:

  • 铸造:代币账户持有的代币类型

  • 所有者:有权从代币账户转移代币的账户

  • 数量:代币账户当前持有的代币单位

20241209182241
每个代币账户存储的完整详细信息包括以下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pub struct Account {
/// The mint associated with this account
pub mint: Pubkey,
/// The owner of this account.
pub owner: Pubkey,
/// The amount of tokens this account holds.
pub amount: u64,
/// If `delegate` is `Some` then `delegated_amount` represents
/// the amount authorized by the delegate
pub delegate: COption<Pubkey>,
/// The account's state
pub state: AccountState,
/// If is_native.is_some, this is a native token, and the value logs the
/// rent-exempt reserve. An Account is required to be rent-exempt, so
/// the value is used by the Processor to ensure that wrapped SOL
/// accounts do not drop below this threshold.
pub is_native: COption<u64>,
/// The amount delegated
pub delegated_amount: u64,
/// Optional authority to close the account.
pub close_authority: COption<Pubkey>,
}

请注意,每个代币账户的数据包括一个owner字段,用于标识谁对该特定代币账户拥有权限。这与 AccountInfo 中指定的程序所有者是 分开的,所有代币账户的程序所有者都是代币程序。

一个钱包可以为同一种代币创建多个代币账户,但每个代币账户只能由一个钱包拥有,并且只能持有一种类型的代币。

账户关系图:
20241209182453

关联代币账户

为了简化查找特定铸造和所有者的代币账户地址的过程,我们通常使用关联代币账户。

关联代币账户是使用所有者地址铸造账户地址确定性派生的代币账户。你可以将关联代币账户视为特定铸造和所有者的“默认”代币账户

重要的是要理解,关联代币账户并不是一种不同类型的代币账户。它只是一个具有特定地址的代币账户。

20241209182644

这介绍了 Solana 开发中的一个关键概念: 程序派生地址 (PDA)。 从概念上讲,PDA 提供了一种使用一些预定义输入生成地址的确定性方法。 这使我们能够在以后轻松找到账户的地址。

这里有一个 Solana Playground 示例,它派生了 USDC 关联代币账户地址和所有者。 对于相同的铸造和所有者,它将始终生成相同的地址 。

1
2
3
4
5
6
import { getAssociatedTokenAddressSync } from "@solana/spl-token";

const associatedTokenAccountAddress = getAssociatedTokenAddressSync(
USDC_MINT_ADDRESS,
OWNER_ADDRESS,
);

具体来说,关联代币账户的地址是使用以下输入派生的。 这里有一个 Solana Playground 示例,它生成 了与前一个示例相同的地址。

1
2
3
4
5
6
7
8
9
10
import { PublicKey } from "@solana/web3.js";

const [PDA, bump] = PublicKey.findProgramAddressSync(
[
OWNER_ADDRESS.toBuffer(),
TOKEN_PROGRAM_ID.toBuffer(),
USDC_MINT_ADDRESS.toBuffer(),
],
ASSOCIATED_TOKEN_PROGRAM_ID,
);

为了让两个钱包持有相同类型的代币,每个钱包需要为特定的铸造账户拥有自己的代币账户。下图展示了这种账户关系的样子。

20241209182935

refs: