{"author":{"address":null,"user":"https://learnblockchain.cn/people/13838"},"content":{"body":"# sui-move进阶：coin.move源码分析\r\n\r\n`coin.move` 是 Sui Move 中实现可替代代币（fungible tokens）的核心模块（实际上，因为sui\"一切皆对象\"和所有权的设计，也自然而然地可以用来实现NFT）。它提供了创建、管理和操作代币的基础工具，包括代币的生成、分割、合并、供应量管理以及监管功能。\r\n\r\n在本教程中，我将部分解析 `coin.move` 的设计与功能。\r\n\r\n---\r\n\r\n## 模块概述\r\n\r\n`coin.move` 旨在提供一套通用的代币操作接口，并支持高级功能如：\r\n- 代币分割与合并\r\n- 总供应量管理\r\n- 元数据存储与更新\r\n- 受监管的代币功能（如地址限制、全局暂停）\r\n\r\n---\r\n\r\n## 核心结构体\r\n\r\n### Coin\r\n`Coin` 是代币的核心结构，用于表示某种类型 `T` 的代币及其余额。\r\n\r\n```move\r\npublic struct Coin\u003cphantom T\u003e has key, store {\r\n    id: UID,\r\n    balance: Balance\u003cT\u003e,\r\n}\r\n```\r\n\r\n字段解析:\r\n\r\n- id: 每个 Coin 实例的唯一标识符。\r\n- balance: 表示代币的余额，使用 Balance\u003cT\u003e 类型。\r\n功能解析\r\n- phantom T 确保 Coin\u003cT\u003e 类型之间互不干扰。\r\n- key, store 能力允许 Coin 存储在全局状态中。\r\n\r\n### CoinMetadata\r\n\r\n`CoinMetadata` 用于存储代币的元数据信息，如名称、符号、小数位数等。\r\n\r\n```move\r\npublic struct CoinMetadata\u003cphantom T\u003e has key, store {\r\n    id: UID,\r\n    decimals: u8,\r\n    name: string::String,\r\n    symbol: ascii::String,\r\n    description: string::String,\r\n    icon_url: Option\u003cUrl\u003e,\r\n}\r\n```\r\n\r\n字段解析:\r\n\r\n- decimals: 小数位数，用于显示格式化的代币余额。\r\n- name: 代币的名称，如 \"US Dollar\"。\r\n- symbol: 代币符号，如 \"USD\"。\r\n- description: 对代币的描述。\r\n- icon_url: 可选的图标 URL。\r\n\r\n### TreasuryCap\r\n\r\n`TreasuryCap` 用于管理代币的总供应量。\r\n\r\n```move\r\npublic struct TreasuryCap\u003cphantom T\u003e has key, store {\r\n    id: UID,\r\n    total_supply: Supply\u003cT\u003e,\r\n}\r\n```\r\n\r\n功能解析:\r\n\r\n- 提供安全的供应量管理。\r\n- 确保某种类型的代币只能由其唯一的 TreasuryCap 控制。\r\n\r\n### DenyCapV2\r\n\r\n`DenyCapV2` 为受监管代币提供高级功能，如地址限制和全局暂停。\r\n\r\n```move\r\npublic struct DenyCapV2\u003cphantom T\u003e has key, store {\r\n    id: UID,\r\n    allow_global_pause: bool,\r\n}\r\n```\r\n\r\n功能解析:\r\n\r\n- allow_global_pause: 允许启用全局暂停，禁止所有地址操作该代币。\r\n\r\n## 核心功能解析\r\n\r\n### 基础代币操作\r\n\r\n#### 查询余额\r\n\r\n通过 value 获取 Coin 的余额：\r\n\r\n```move\r\npublic fun value\u003cT\u003e(self: \u0026Coin\u003cT\u003e): u64 {\r\n    self.balance.value()\r\n}\r\n```\r\n\r\n#### 分割代币\r\n\r\n将一个代币分割为两部分：\r\n\r\n```move\r\npublic fun split\u003cT\u003e(self: \u0026mut Coin\u003cT\u003e, split_amount: u64, ctx: \u0026mut TxContext): Coin\u003cT\u003e {\r\n    take(\u0026mut self.balance, split_amount, ctx)\r\n}\r\n```\r\n\r\n#### 合并代币\r\n\r\n将两个代币合并：\r\n\r\n```move\r\npublic entry fun join\u003cT\u003e(self: \u0026mut Coin\u003cT\u003e, c: Coin\u003cT\u003e) {\r\n    let Coin { id, balance } = c;\r\n    id.delete();\r\n    self.balance.join(balance);\r\n}\r\n```\r\n\r\n#### 生成零余额代币\r\n\r\n创建一个余额为 0 的占位代币：\r\n\r\n```move\r\npublic fun zero\u003cT\u003e(ctx: \u0026mut TxContext): Coin\u003cT\u003e {\r\n    Coin { id: object::new(ctx), balance: balance::zero() }\r\n}\r\n```\r\n### 总供应量管理\r\n\r\n#### 查询总供应量\r\n\r\n```move\r\npublic fun total_supply\u003cT\u003e(cap: \u0026TreasuryCap\u003cT\u003e): u64 {\r\n    balance::supply_value(\u0026cap.total_supply)\r\n}\r\n```\r\n\r\n#### 铸造代币\r\n\r\n通过 TreasuryCap 创建新的代币：\r\n\r\n```move\r\npublic fun mint\u003cT\u003e(cap: \u0026mut TreasuryCap\u003cT\u003e, value: u64, ctx: \u0026mut TxContext): Coin\u003cT\u003e {\r\n    Coin {\r\n        id: object::new(ctx),\r\n        balance: cap.total_supply.increase_supply(value),\r\n    }\r\n}\r\n```\r\n\r\n#### 销毁代币\r\n\r\n减少代币的总供应量：\r\n\r\n```move\r\npublic entry fun burn\u003cT\u003e(cap: \u0026mut TreasuryCap\u003cT\u003e, c: Coin\u003cT\u003e): u64 {\r\n    let Coin { id, balance } = c;\r\n    id.delete();\r\n    cap.total_supply.decrease_supply(balance)\r\n}\r\n```\r\n### 监管代币\r\n\r\n#### 地址限制\r\n\r\n添加地址到禁止列表：\r\n\r\n```move\r\npublic fun deny_list_v2_add\u003cT\u003e(\r\n    deny_list: \u0026mut DenyList,\r\n    _deny_cap: \u0026mut DenyCapV2\u003cT\u003e,\r\n    addr: address,\r\n    ctx: \u0026mut TxContext,\r\n) {\r\n    let ty = type_name::get_with_original_ids\u003cT\u003e().into_string().into_bytes();\r\n    deny_list.v2_add(DENY_LIST_COIN_INDEX, ty, addr, ctx)\r\n}\r\n```\r\n\r\n#### 启用全局暂停\r\n\r\n立即禁止所有地址使用代币：\r\n\r\n```move\r\npublic fun deny_list_v2_enable_global_pause\u003cT\u003e(\r\n    deny_list: \u0026mut DenyList,\r\n    deny_cap: \u0026mut DenyCapV2\u003cT\u003e,\r\n    ctx: \u0026mut TxContext,\r\n) {\r\n    assert!(deny_cap.allow_global_pause, EGlobalPauseNotAllowed);\r\n    let ty = type_name::get_with_original_ids\u003cT\u003e().into_string().into_bytes();\r\n    deny_list.v2_enable_global_pause(DENY_LIST_COIN_INDEX, ty, ctx)\r\n}\r\n```\r\n\r\n### 元数据管理\r\n\r\n#### 更新元数据\r\n\r\n支持动态更新代币的元数据：\r\n\r\n```move\r\npublic entry fun update_name\u003cT\u003e(\r\n    _treasury: \u0026TreasuryCap\u003cT\u003e,\r\n    metadata: \u0026mut CoinMetadata\u003cT\u003e,\r\n    name: string::String,\r\n) {\r\n    metadata.name = name;\r\n}\r\n```\r\n\r\n## 示例：创建和管理代币\r\n\r\n```move\r\npublic fun create_currency\u003cT: drop\u003e(\r\n    witness: T,\r\n    decimals: u8,\r\n    symbol: vector\u003cu8\u003e,\r\n    name: vector\u003cu8\u003e,\r\n    description: vector\u003cu8\u003e,\r\n    icon_url: Option\u003cUrl\u003e,\r\n    ctx: \u0026mut TxContext,\r\n): (TreasuryCap\u003cT\u003e, CoinMetadata\u003cT\u003e) {\r\n    (\r\n        TreasuryCap {\r\n            id: object::new(ctx),\r\n            total_supply: balance::create_supply(witness),\r\n        },\r\n        CoinMetadata {\r\n            id: object::new(ctx),\r\n            decimals,\r\n            name: string::utf8(name),\r\n            symbol: ascii::string(symbol),\r\n            description: string::utf8(description),\r\n            icon_url,\r\n        },\r\n    )\r\n}\r\n```\r\n\r\n注意这个`witness`，这里使用了`一次性见证者`的设计模式，我将在下一篇教程中进行讲述与探讨。\r\n\r\n而我们同样可以创建被管理的代币：\r\n\r\n```move\r\npublic fun create_regulated_currency_v2\u003cT: drop\u003e(\r\n    witness: T,\r\n    decimals: u8,\r\n    symbol: vector\u003cu8\u003e,\r\n    name: vector\u003cu8\u003e,\r\n    description: vector\u003cu8\u003e,\r\n    icon_url: Option\u003cUrl\u003e,\r\n    allow_global_pause: bool,\r\n    ctx: \u0026mut TxContext,\r\n): (TreasuryCap\u003cT\u003e, DenyCapV2\u003cT\u003e, CoinMetadata\u003cT\u003e) {\r\n    let (treasury_cap, metadata) = create_currency(\r\n        witness,\r\n        decimals,\r\n        symbol,\r\n        name,\r\n        description,\r\n        icon_url,\r\n        ctx,\r\n    );\r\n    let deny_cap = DenyCapV2 {\r\n        id: object::new(ctx),\r\n        allow_global_pause,\r\n    };\r\n    transfer::freeze_object(RegulatedCoinMetadata\u003cT\u003e {\r\n        id: object::new(ctx),\r\n        coin_metadata_object: object::id(\u0026metadata),\r\n        deny_cap_object: object::id(\u0026deny_cap),\r\n    });\r\n    (treasury_cap, deny_cap, metadata)\r\n}\r\n```\r\n\r\n源代码对其的解释是：\r\n\r\n\u003e通过调用 `create_currency` 创建一种新的货币类型，并附加了一项额外功能，\r\n\u003e\r\n\u003e允许将特定地址的代币冻结。当一个地址被添加到禁止列表时，\r\n它将立即无法将该货币的代币用作交易的输入对象。\r\n\u003e\r\n\u003e此外，从下一个纪元开始，这些地址将无法接收该货币的代币。\r\n`allow_global_pause` 标志启用了一个额外的 API，可禁止所有地址操作该货币的代币。\r\n\u003e\r\n\u003e需要注意的是，这不会影响禁止列表中针对单个地址的条目，\r\n也不会更改 \"contains\" API 的返回结果。\r\n\r\n另外，如果需要使用上述代币管理的相关功能，在创建代币时，我们必须调用`create_regulated_currency_v2`创建可被管理的代币。\r\n\r\n## 总结\r\n\r\nCoin是sui链的基本资产，因此，理解和使用Coin也是sui move学习最重要的一部分。\r\n\r\n还有一些额外的功能与特性我在本篇文章没有提到，希望大家主动去阅读源码，甚至编写用例进行调试。","title":"sui-move进阶：coin.move源码分析"},"history":null,"timestamp":1733842632,"version":1}