一、知识点一次性见证,发布者权限,所有权的转移结构体和key、copy、store、drop能力错误码和断言基本数据类型的认识和使用display、table、transfer、event、object函数的认识和使用TxContent交易上下文对象公开函数的调用二、代码创建
使用终端命令sui move mynft创建一个新项目,创建项目后找到Move.toml文件中的git地址,将github替换为gitee。
打开mynft.move文件,放开文件中的module注释。
代码书写时,相关标准库会自动引入。
const MAX_SUPPLY: u64 = 10; // 允许铸造的最大NFT数量
const ENOTENOUGHSUPPLY: u64 = 101; // 当铸造的nft数量超过最大供应量时报错
const ENOTMINTAGAIN: u64 = 102; // 当同一个地址重复接收时报错
结构体首字母必须大写。
NFT结构体:记录NFT元数据,有key,store能力
MintRecord结构体:记录铸造NFT的地址,有key能力
NFTMinted结构体:记录用户铸造NFT时需要传递给链下的地址,可共享,有copy,drop能力
MYNFT结构体:用于一次性见证,配合init函数使用,命名必须是module的大写,有drop能力,
// 结构体名称必须首字母大写
// 定义nft结构体,包含nft的基本信息,拥有key、store能力
public struct NFT has key, store {
id: UID, // nft的唯一标识符,确保每个nft实例在区块链上都是唯一的
nft_id: u64, // 铸造的nft编码,表示铸造的第几个nft
name: String, // nft的名称
image_url: String, // nft的图片地址
creator: address, // nft的铸造者地址
recipient: address // nft的接收者地址
}
// MintRecord结构体,用于记录NFT铸造的地址和编码的映射
public struct MintRecord has key {
id: UID, // 结构体唯一标识符
record: Table<address, u64> // 用户铸造NFT的映射,Table是一种映射集合,其中所有键值对的类型必须一致
}
// NFTMinted结构体,用于发送用户铸造 NFT 事件时需要传递给链下的数据
public struct NFTMinted has copy, drop {
object_id: ID, // 记录铸造的NFT的UID
creator: address, // 记录铸造者的地址
name: String // 记录铸造的NFT的名称
}
// 用于一次性见证otw,命名必须是module的大写,而且要有drop能力
public struct MYNFT has drop {}
// 初始化函数,otw在init函数出现一般在创建NFT、Coin等资产时
fun init(otw: MYNFT, ctx: &mut TxContext) {
// 获取发布者权限,为了确保合约和数据的合法性、安全性、版本控制、元数据一致性以及透明性
let publisher = package::claim(otw, ctx);
// 创建一个vector,用来创建需要展示的NFT元数据的key值,用于display::new_with_fields方法的第二个参数
let keys = vector[utf8(b"name"), utf8(b"image_url"), utf8(b"creator")];
// 创建另一个vertor,用来创建需要展示的NFT元数据的value值,用于display::new_with_fields方法的第三个参数
let values= vector[utf8(b"{name} #{nft_id}"), utf8(b"{image_url}"), utf8(b"{creator}")];
// display对象用来定义每个 NFT 对象展示的关键元数据,传递泛型NFT结构体,是可变对象,需要传递NFT数据
let mut display = display::new_with_fields<NFT>(&publisher, keys, values, ctx);
// 创建MintRecord对象,用来追踪记录用户铸造NFT的情况
let mintRecord = MintRecord{
id: object::new(ctx),// 使用object::new(ctx)是唯一可以获取object UID的方法
record: table::new<address, u64>(ctx)
};
// 设置display对象版本号
display::update_version(&mut display);
// 转移所有权
// 将 Publisher 和 Display 对象转移给合约发布地址
transfer::public_transfer(publisher, ctx.sender());
transfer::public_transfer(display, ctx.sender());
// MintRecord 对象需要共享所有权
transfer::share_object(mintRecord);
}
定义一个名为 mint 的公开函数,该函数需要5个参数:被铸造 NFT 的 name、image_url、recipient、MintRecord 对象和 TxContext交易上下文对象。
铸造过程中需要判断是否超出最大供应量、是否重复为同一个地址铸造。这里需要注意assert!(bool,code)方法的使用。
// 定义一个公开的mint函数,铸造方法,包含5个参数:name、image_url、recipient、mintRecord、TxContext
public entry fun mint (name: String, image_url: String, recipient: address, mint_record: &mut MintRecord, ctx: &mut TxContext) {
// 断言 assert!(<bool expression>, <code>),bool 必须判断为真,否者(即bool为false)会报错误码并中断
// 如果给同一个地址重复铸造就报错
assert!(!table::contains(&mint_record.record, recipient), ENOTMINTAGAIN);
// 记录NFT编号
let nft_id = table::length(&mint_record.record) + 1;
// 验证NFT是否超过最大供应量
assert!(nft_id <= MAX_SUPPLY, ENOTENOUGHSUPPLY);
// 完成铸造记录并映射
table::add(&mut mint_record.record, recipient, nft_id);
// 铸造NFT
let nft = NFT{
id: object::new(ctx), // nft的唯一标识符,确保每个nft实例在区块链上都是唯一的
nft_id: nft_id, // 铸造的nft编码,表示铸造的第几个nft
name: name, // nft的名称
image_url: image_url, // nft的图片地址
creator: ctx.sender(), // nft的铸造者地址
recipient: recipient // nft的接收者地址
};
// 记录事件,在转移之前记录
let nft_minted = NFTMinted{
object_id: object::id(&nft), // 记录铸造的NFT的UID,object::id(obj)获取obj的UID
creator: ctx.sender(), // 记录铸造者的地址
name: nft.name // 记录铸造的NFT的名称
};
// 转移nft
transfer::public_transfer(nft, recipient);
// 发送事件通知
event::emit(nft_minted);
}
销毁NFT需要将NFT MintRecord中的记录删除,并将拥有的NFT对象删除。
定义一个公开函数burn,传入铸造记录MintRecord、NFT两个参数。
// 燃烧已铸造nft,传入两个参数:铸造记录MintRecord、自己拥有的NFT
public entry fun burn(mint_record: &mut MintRecord, nft: NFT) {
// 删除记录中的映射关系
table::remove(&mut mint_record.record, nft.recipient);
// 解包Object(解构?)
let NFT {id, nft_id, name, image_url, creator, recipient} = nft;
object::delete(id);
}
module mynft::mynft;
use std::string::{String, utf8};
use sui::display;
use sui::event;
use sui::object;
use sui::package;
use sui::table;
use sui::table::Table;
use sui::transfer;
const MAX_SUPPLY: u64 = 10; // 允许铸造的最大NFT数量
// 错误码
const ENOTENOUGHSUPPLY: u64 = 101; // 当铸造的nft数量超过最大供应量时报错
const ENOTMINTAGAIN: u64 = 102; // 当同一个地址重复接收时报错
// 结构体名称必须首字母大写
// 定义nft结构体,包含nft的基本信息,拥有key、store能力
public struct NFT has key, store {
id: UID, // nft的唯一标识符,确保每个nft实例在区块链上都是唯一的
nft_id: u64, // 铸造的nft编码,表示铸造的第几个nft
name: String, // nft的名称
image_url: String, // nft的图片地址
creator: address, // nft的铸造者地址
recipient: address // nft的接收者地址
}
// MintRecord结构体,用于记录NFT铸造的地址和编码的映射
public struct MintRecord has key {
id: UID, // 结构体唯一标识符
record: Table<address, u64> // 用户铸造NFT的映射,Table是一种映射集合,其中所有键值对的类型必须一致
}
// NFTMinted结构体,用于发送用户铸造 NFT 事件时需要传递给链下的数据
public struct NFTMinted has copy, drop {
object_id: ID, // 记录铸造的NFT的UID
creator: address, // 记录铸造者的地址
name: String // 记录铸造的NFT的名称
}
// 用于一次性见证otw,命名必须是module的大写,而且要有drop能力
public struct MYNFT has drop {}
// 初始化函数,otw在init函数出现一般在创建NFT、Coin等资产时
fun init(otw: MYNFT, ctx: &mut TxContext) {
// 获取发布者权限,为了确保合约和数据的合法性、安全性、版本控制、元数据一致性以及透明性
let publisher = package::claim(otw, ctx);
// 创建一个vector,用来创建需要展示的NFT元数据的key值,用于display::new_with_fields方法的第二个参数
let keys = vector[utf8(b"name"), utf8(b"image_url"), utf8(b"creator")];
// 创建另一个vertor,用来创建需要展示的NFT元数据的value值,用于display::new_with_fields方法的第三个参数
let values= vector[utf8(b"{name} #{nft_id}"), utf8(b"{image_url}"), utf8(b"{creator}")];
// display对象用来定义每个 NFT 对象展示的关键元数据,传递泛型NFT结构体,是可变对象,需要传递NFT数据
let mut display = display::new_with_fields<NFT>(&publisher, keys, values, ctx);
// 创建MintRecord对象,用来追踪记录用户铸造NFT的情况
let mintRecord = MintRecord{
id: object::new(ctx),// 使用object::new(ctx)是唯一可以获取object UID的方法
record: table::new<address, u64>(ctx)
};
// 设置display对象版本号
display::update_version(&mut display);
// 转移所有权
// 将 Publisher 和 Display 对象转移给合约发布地址
transfer::public_transfer(publisher, ctx.sender());
transfer::public_transfer(display, ctx.sender());
// MintRecord 对象需要共享所有权
transfer::share_object(mintRecord);
}
// 定义一个公开的mint函数,铸造方法,包含5个参数:name、image_url、recipient、mintRecord、TxContext
public entry fun mint (name: String, image_url: String, recipient: address, mint_record: &mut MintRecord, ctx: &mut TxContext) {
// 断言 assert!(<bool expression>, <code>),bool 必须判断为真,否者(即bool为false)会报错误码并中断
// 如果给同一个地址重复铸造就报错
assert!(!table::contains(&mint_record.record, recipient), ENOTMINTAGAIN);
// 记录NFT编号
let nft_id = table::length(&mint_record.record) + 1;
// 验证NFT是否超过最大供应量
assert!(nft_id <= MAX_SUPPLY, ENOTENOUGHSUPPLY);
// 完成铸造记录并映射
table::add(&mut mint_record.record, recipient, nft_id);
// 铸造NFT
let nft = NFT{
id: object::new(ctx), // nft的唯一标识符,确保每个nft实例在区块链上都是唯一的
nft_id: nft_id, // 铸造的nft编码,表示铸造的第几个nft
name: name, // nft的名称
image_url: image_url, // nft的图片地址
creator: ctx.sender(), // nft的铸造者地址
recipient: recipient // nft的接收者地址
};
// 记录事件,在转移之前记录
let nft_minted = NFTMinted{
object_id: object::id(&nft), // 记录铸造的NFT的UID,object::id(obj)获取obj的UID
creator: ctx.sender(), // 记录铸造者的地址
name: nft.name // 记录铸造的NFT的名称
};
// 转移nft
transfer::public_transfer(nft, recipient);
// 发送事件通知
event::emit(nft_minted);
}
// 燃烧已铸造nft,传入两个参数:铸造记录MintRecord、自己拥有的NFT
public entry fun burn(mint_record: &mut MintRecord, nft: NFT) {
// 删除记录中的映射关系
table::remove(&mut mint_record.record, nft.recipient);
// 解包Object(解构?)
let NFT {id, nft_id, name, image_url, creator, recipient} = nft;
object::delete(id);
}
合约调用有两种方法:前端引入SDK使用代码调用;在终端使用命令调用。
这里先讲使用命令调用,调用命令:
sui client call --package <package ID> --module <moduleName> --function <function> --args <args1 args2 ...>
<>中的内容根据调用合约方法填写
package ID:合约发布后,产生的合约 package ID
moduleName:项目中module 的命名
function:需要调用的合约方法
args:合约方法中的参数,如果参数中包含TxContent交易上下文对象,不需要填写,一般放在方法参数的最后
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!