{"content":{"title":"Moveinscription 源码系列（二）","body":"## 0. 前言\r\n\r\n上一篇笔记分析了 2.0 和 3.0 版本的 Movescriptions 智能铭文合约。在 4.0 版本时合约有了很多重大的变化，本篇文章将分析 4.0 合约源码\r\n\r\n## 1. 文件结构\r\n\r\n新版本的文件结构：\r\n\r\n```bash\r\n.\r\n├── assert_util.move\r\n├── content_type.move\r\n├── epoch_bus_factory.move\r\n├── init.move\r\n├── metadata.move\r\n├── mint_get_factory.move\r\n├── movescription.move\r\n├── name_factory.move\r\n├── string_util.move\r\n├── svg.move\r\n├── tests\r\n│   ├── epoch_bus_factory_scenario_test.move\r\n│   ├── mint_get_factory_scenario_test.move\r\n│   ├── movescription_object_test.move\r\n│   ├── name_factory_scenario_test.move\r\n│   ├── scenario_test.move\r\n│   ├── tick_factory_scenario_test.move\r\n│   └── tick_record_df_test.move\r\n├── tick_factory.move\r\n├── tick_name.move\r\n├── type_util.move\r\n└── util.move\r\n```\r\n\r\n之前版本，都只有 `movescription.move `一个文件，在 4.0 版本中，合约项目做了工程化处理，多出了很多 `factory `合约文件。一起来看看这些都是做什么的吧\r\n\r\n## 2 结构体\r\n\r\n首先结构体有所变化，之前的 `TickRecord` 名字改成` TickRecordV2 `，分成了两个嵌套的结构体\r\n对比之前，少了 `epoch_records `字段。关于` epoch` 的功能，会使用添加动态字段的方式实现\r\n\r\n```rust\r\nstruct TickStat has store, copy, drop {\r\n  /// The remaining inscription amount not minted\r\n  remain: u64,\r\n  /// The current supply of the inscription, burn will decrease the current supply\r\n  current_supply: u64,\r\n  /// Total mint transactions\r\n  total_transactions: u64,\r\n}\r\n\r\nstruct TickRecordV2 has key, store {\r\n  id: UID,\r\n  version: u64,\r\n  tick: String,\r\n  total_supply: u64,\r\n  /// The movescription can be burned by the owner\r\n  burnable: bool,\r\n  /// The mint factory type name\r\n  mint_factory: String,\r\n  stat: TickStat,\r\n}\r\n```\r\n\r\n新增 `LockedBox`，用于实现铭文嵌套\r\n\r\n```rust\r\nstruct LockedBox has store{\r\n\tlocked_movescription: Movescription,\r\n}\r\n```\r\n\r\n`burn` 掉铭文后可以获取的凭据\r\n\r\n```rust\r\nstruct BurnReceipt has key, store {\r\n  id: UID,\r\n  tick: String,\r\n  amount: u64,\r\n}\r\n```\r\n\r\n## 3 函数\r\n\r\n### 3.1 `init` 函数\r\n\r\n`init` 函数中，不再发行 `$MOVE` 铭文，仅仅创建一个共享的 DeployRecord。\r\n发行 MOVE 铭文的功能在 `epoch_bus_factory.move` 合约中实现，逻辑上基本没有变化\r\n\r\n### 3.2 发行其他类型铭文\r\n\r\n```rust\r\npublic(friend) fun internal_deploy_with_witness<W: drop>(\r\n  deploy_record: &mut DeployRecord,\r\n  tick: String,\r\n  total_supply: u64,\r\n  burnable: bool,\r\n  _witness: W,\r\n  ctx: &mut TxContext\r\n) : TickRecordV2\r\n```\r\n\r\n这个函数是实现 `deploy` 功能的底层函数，上层逻辑在包内其他合约中实现，此函数只负责创建 `TickRecordV2` 对象并触发事件\r\n包内其他合约的 `deploy` 功能最终都是调用本函数实现。\r\n和之前版本的 `depoly` 函数相比，4.0 版本使用了一次性见证模式，其他合约调用的时候会使用 WITNESS{}做为参数，如\r\n`let tick_record = movescription::internal_deploy_with_witness(deploy_record, ascii::string(tick), total_supply, true, WITNESS{}, ctx);`\r\n由于工程化的原因，使用了`friend`关键字，仅供同一个包的函数调用\r\n\r\n### 3.3 铸造铭文\r\n\r\n和上面的 `internal_deploy_with_witness` 函数类似，这个函数是最终完成铸造铭文的底层函数。\r\n包内其他合约先完成铸造铭文的逻辑，如计算 epoch 等，最后会调用此函数完成铸造\r\n\r\n```rust\r\npublic fun do_mint_with_witness<W: drop>(\r\n  tick_record: &mut TickRecordV2,\r\n  init_locked_sui: Balance<SUI>,\r\n  amount: u64,\r\n  metadata: Option<Metadata>,\r\n  _witness: W,\r\n  ctx: &mut TxContext\r\n) : Movescription\r\n```\r\n\r\n### 3.4 销毁铭文\r\n\r\n销毁铭文的调用关系：`do_burn_v2` --> `do_burn_with_message_v2` --> `internal_burn`\r\n也许你注意到了，这个版本的 `TickRecordV2` 结构体中有 `burnable` 字段，意味着有些铭文是不能销毁的，这取决于发行方\r\n`do_burn_with_message_v2` 函数中验证铭文能否销毁\r\n\r\n大多数 `burn` 逻辑都在 `internal_burn` 函数中完成\r\n\r\n```rust\r\nfun internal_burn(\r\n        tick_record: &mut TickRecordV2,\r\n        inscription: Movescription,\r\n        message: vector<u8>,\r\n        ctx: &mut TxContext\r\n    ) : (Coin<SUI>, Option<Movescription>)\r\n```\r\n\r\n整个函数的功能和之前版本的 `do_burn` 相同，但是多了 `message` 参数\r\n这个参数用来在触发 `BurnTick` 事件的时候附加一段信息\r\n\r\n此外，添加了一次性见证模式的 `burn` 功能\r\n\r\n```rust\r\npublic fun do_burn_with_witness<W: drop>(\r\n  tick_record: &mut TickRecordV2,\r\n  inscription: Movescription,\r\n  message: vector<u8>,\r\n  _witness: W,\r\n  ctx: &mut TxContext\r\n) : (Coin<SUI>, Option<Movescription>)\r\n```\r\n\r\n函数中先验证了一习性见证模式，然后调用 `internal_burn` 函数\r\n\r\n如果想要 `burn` 铭文并获取一个记录的话，可以调用 `do_burn_for_receipt_v2`:\r\n\r\n```rust\r\npublic fun do_burn_for_receipt_v2(\r\n  tick_record: &mut TickRecordV2,\r\n  inscription: Movescription,\r\n  message: vector<u8>,\r\n  ctx: &mut TxContext\r\n) : (Coin<SUI>, Option<Movescription>, BurnReceipt)\r\n```\r\n\r\n这个函数会调用 `do_burn_with_message_v2` 销毁铭文，并在此之后创建并返回一个 `BurnReceipt` 对象\r\n这个 `BurnReceipt` 可以使用 `drop_receipt` 函数删除\r\n\r\n`public fun drop_receipt(receipt: BurnReceipt):(String, u64)`\r\n\r\n以上所有的 `burn` 方法，最终都会返还 `mint` 的时候付的钱。基本的铭文玩法并没有很大的变化\r\n\r\n### 3.5 向 `Movescription` 上添加动态字段\r\n\r\n新版本添加了新的接口，可以操作 `Movescription` 的字段，目前主要用于铭文嵌套\r\n\r\n```rust\r\n/// Add the `Value` type dynamic field to the movescription\r\nfun add_df<Value: store>(\r\n  movescription: &mut Movescription,\r\n  value: Value,\r\n) {\r\n  let name = type_to_name<Value>();\r\n  df::add(&mut movescription.id, name, value);\r\n}\r\n\r\n/// Borrow the `Value` type dynamic field of the movescription\r\nfun borrow_df<Value: store>(  movescription: &Movescription, ): &Value {\r\n  let name = type_to_name<Value>();\r\n  df::borrow<String, Value>(&movescription.id, name)\r\n}\r\n\r\n/// Borrow the `Value` type dynamic field of the movescription mutably\r\nfun borrow_df_mut<Value: store>(movescription: &mut Movescription, ): &mut Value {\r\n  let name = type_to_name<Value>();\r\n  df::borrow_mut<String, Value>(&mut movescription.id, name)\r\n}\r\n\r\n/// Returns the `Value` type dynamic field of the movescription\r\nfun remove_df<Value: store>( movescription: &mut Movescription,): Value {\r\n  let name = type_to_name<Value>();\r\n  let value: Value = df::remove<String, Value>(&mut movescription.id, name);\r\n  value\r\n}\r\n\r\n/// Returns if the movescription contains the `Value` type dynamic field\r\nfun exists_df<Value: store>(\r\n  movescription: &Movescription,\r\n): bool {\r\n  let name = type_to_name<Value>();\r\n  df::exists_with_type<String, Value>(&movescription.id, name)\r\n}\r\n```\r\n\r\n以上是一些功能性的函数\r\n\r\n接下来，通过调用 `lock_within` 函数，可以把一个铭文锁进另一个铭文中，实现铭文嵌套\r\n`public fun lock_within(movescription: &mut Movescription, locked_movescription: Movescription) `\r\n\r\n当然同样有读取和解锁的函数：\r\n\r\n```rust\r\npublic fun contains_locked(movescription: &Movescription): bool {\r\n  exists_df<LockedBox>(movescription)\r\n}\r\n\r\npublic fun borrow_locked(movescription: &Movescription): &Movescription {\r\n  let locked_box = borrow_df<LockedBox>(movescription);\r\n  &locked_box.locked_movescription\r\n}\r\n\r\nfun borrow_mut_locked(movescription: &mut Movescription): &mut Movescription {\r\n  let locked_box = borrow_df_mut<LockedBox>(movescription);\r\n  &mut locked_box.locked_movescription\r\n}\r\n\r\nfun unlock_box(movescription: &mut Movescription) : Movescription {\r\n  let LockedBox{ locked_movescription } = remove_df<LockedBox>(movescription);\r\n  locked_movescription\r\n}\r\n```\r\n\r\n### 3.6 向 `TickRecordV2` 添加动态字段\r\n\r\n```rust\r\npublic fun tick_record_add_df<V: store, W: drop>(tick_record: &mut TickRecordV2, value: V, _witness: W) {\r\n  type_util::assert_witness<W>(tick_record.mint_factory);\r\n  let name = type_util::type_to_name<V>();\r\n  df::add(&mut tick_record.id, name, value);\r\n}\r\n\r\npublic fun tick_record_remove_df<V: store, W: drop>(tick_record: &mut TickRecordV2, _witness: W) : V {\r\n  type_util::assert_witness<W>(tick_record.mint_factory);\r\n  let name = type_util::type_to_name<V>();\r\n  df::remove(&mut tick_record.id, name)\r\n}\r\n\r\npublic fun tick_record_borrow_mut_df<V: store, W: drop>(tick_record: &mut TickRecordV2, _witness: W) : &mut V {\r\n  type_util::assert_witness<W>(tick_record.mint_factory);\r\n  let name = type_util::type_to_name<V>();\r\n  df::borrow_mut(&mut tick_record.id, name)\r\n}\r\n\r\npublic fun tick_record_borrow_df<V: store>(tick_record: &TickRecordV2) : &V{\r\n  let name = type_util::type_to_name<V>();\r\n  df::borrow(&tick_record.id, name)\r\n}\r\n\r\npublic fun tick_record_exists_df<V: store>(tick_record: &TickRecordV2) : bool {\r\n  let name = type_util::type_to_name<V>();\r\n  df::exists_with_type<String, V>(&tick_record.id, name)\r\n}\r\n```\r\n\r\n这些动态字段主要用于向 `TickRecordV2` 上添加 `epoch` 信息\r\n设计上，`TickRecordV2` 取消了 `epoch_records` 字段，每次 `depoly `出一个新的铭文后，都需要向上面添加` epoch`信息。\r\n这些功能都是在 `epoch_bus_factory.move` 合约中完成的，我们下一篇文章会详细分析\r\n\r\n### 3.7 合约升级支持\r\n\r\n这个版本的升级，结构体发生了改变，所以合约中留了升级接口，用于处理之前版本的数据。如版本号升级。\r\n这里介绍 `migrate_tick_record_to_v2` 函数\r\n\r\n```rust\r\npublic(friend) fun migrate_tick_record_to_v2<W: drop>(\r\n  deploy_record: &mut DeployRecord,\r\n  tick_record: TickRecord,\r\n  _witness: W,\r\n  ctx: &mut TxContext) :\r\n(TickRecordV2, u64, u64, u64, u64, Table<u64, EpochRecord>)\r\n```\r\n这个函数接收一个 `TickRecord` 结构体。这是上个版本的数据结构。函数将这个结构体升级成 `TickRecordV2` 并返回。\r\n\r\n---\r\n\r\n本篇文章介绍的大多数是合约项目的底层函数，接下来的文章将介绍上层的业务逻辑。"},"author":{"user":"https://learnblockchain.cn/people/18893","address":null},"history":"bafkreibc2xr2456bbxnwtxio7ikgwzz7cgr7blyspomo4mkxksjeyt7tfa","timestamp":1711412592,"version":1}