跳转到内容

第一个 Coin

本教程介绍如何编译、部署和铸造自定义 coin(定义见此处),名为 MoonCoin

从以下列表安装首选 SDK:


安装 Aptos CLI 的预编译二进制文件


克隆 aptos-ts-sdk 仓库并构建:

Terminal window
git clone https://github.com/aptos-labs/aptos-ts-sdk.git
cd aptos-ts-sdk
pnpm install
pnpm build

进入 TypeScript 示例目录:

Terminal window
cd examples/typescript/

安装必要依赖:

Terminal window
pnpm install

运行 TypeScript your_coin 示例:

Terminal window
pnpm run your_coin

应用将完成并打印:

Terminal window
Bob's initial MoonCoin balance: 0.
Alice mints herself 100 MoonCoin.
Alice transfers 100 MoonCoin to Bob.
Bob's updated MoonCoin balance: 100.

第 4.1 步:构建和发布 MoonCoin 包

Section titled “第 4.1 步:构建和发布 MoonCoin 包”

Move 合约实际上是一组称为包的 Move 模块。部署或升级新包时,必须使用 --save-metadata 调用编译器以发布包。对于 MoonCoin,以下输出文件至关重要:

  • build/Examples/package-metadata.bcs:包含与包关联的元数据。
  • build/Examples/bytecode_modules/moon_coin.mv:包含 moon_coin.move 模块的字节码。

示例会读取这些文件并发布到 Aptos 区块链:

TypeScript 示例使用 aptos move build-publish-payload 命令编译和构建模块。该命令构建含 package-metadata.bcsmoon_coin.mv 字节码的 build 文件夹,也会构建发布交易载荷并存入 JSON 输出文件,随后可读取该文件获取 metadataBytesbyteCode 来发布合约。

编译包:

export function compilePackage(
packageDir: string,
outputFile: string,
namedAddresses: Array<{ name: string; address: AccountAddress }>,
) {
const addressArg = namedAddresses
.map(({ name, address }) => `${name}=${address}`)
.join(" ");
// Assume-yes automatically overwrites the previous compiled version, only do this if you are sure you want to overwrite the previous version.
const compileCommand = `aptos move build-publish-payload --json-output-file ${outputFile} --package-dir ${packageDir} --named-addresses ${addressArg} --assume-yes`;
execSync(compileCommand);
}
compilePackage("move/moonCoin", "move/moonCoin/moonCoin.json", [
{ name: "MoonCoin", address: alice.accountAddress },
]);

将包发布到链上:

export function getPackageBytesToPublish(filePath: string) {
// current working directory - the root folder of this repo
const cwd = process.cwd();
// target directory - current working directory + filePath (filePath JSON file is generated with the previous, compilePackage, CLI command)
const modulePath = path.join(cwd, filePath);
const jsonData = JSON.parse(fs.readFileSync(modulePath, "utf8"));
const metadataBytes = jsonData.args[0].value;
const byteCode = jsonData.args[1].value;
return { metadataBytes, byteCode };
}
const { metadataBytes, byteCode } = getPackageBytesToPublish(
"move/moonCoin/moonCoin.json",
);
// Publish MoonCoin package to chain
const transaction = await aptos.publishPackageTransaction({
account: alice.accountAddress,
metadataBytes,
moduleBytecode: byteCode,
});
const pendingTransaction = await aptos.signAndSubmitTransaction({
signer: alice,
transaction,
});
await aptos.waitForTransaction({ transactionHash: pendingTransaction.hash });

MoonCoin 模块定义 MoonCoin 结构体,即独特的 coin 类型。它还包含 init_module 函数。模块发布时会调用该函数。本例中,MoonCoin 将 MoonCoin coin 类型初始化为由账户所有者维护的 ManagedCoin

module MoonCoin::moon_coin {
struct MoonCoin {}
fun init_module(sender: &signer) {
aptos_framework::managed_coin::initialize<MoonCoin>(
sender,
b"Moon Coin",
b"MOON",
6,
false,
);
}
}

Coin 有多个原语:

  • 铸造:创建新 coin。
  • 销毁:删除 coin。
  • 冻结:阻止账户将 coin 存入 CoinStore
  • 注册:在账户上创建 CoinStore 资源以存储 coin。
  • 转移:从 CoinStore 提取并存入 coin。

Coin 类型发布到 Aptos 区块链后,发布该 coin 类型的实体可初始化它:

module 0x1::coin {
public fun initialize<CoinType>(
account: &signer,
name: string::String,
symbol: string::String,
decimals: u8,
monitor_supply: bool,
): (BurnCapability<CoinType>, FreezeCapability<CoinType>, MintCapability<CoinType>) {
let account_addr = signer::address_of(account);
assert!(
coin_address<CoinType>() == account_addr,
error::invalid_argument(ECOIN_INFO_ADDRESS_MISMATCH),
);
assert!(
!exists<CoinInfo<CoinType>>(account_addr),
error::already_exists(ECOIN_INFO_ALREADY_PUBLISHED),
);
let coin_info = CoinInfo<CoinType> {
name,
symbol,
decimals,
supply: if (monitor_supply) { option::some(optional_aggregator::new(MAX_U128, false)) } else { option::none() },
};
move_to(account, coin_info);
(BurnCapability<CoinType>{ }, FreezeCapability<CoinType>{ }, MintCapability<CoinType>{ })
}
}

这确保 coin 类型此前从未初始化。请注意第 10 和 15 行的检查,以确保 initialize 调用者就是实际发布模块的实体,且其账户中未存储 CoinInfo。两个条件都成立时,会存储 CoinInfo,调用者获得销毁、冻结和铸造能力。


要使用 coin,实体必须在账户上为它注册 CoinStore

public entry fun registerCoinType(account: &signer) {

MoonCoin 使用提供入口函数包装 managed_coin::registerManagedCoin。以下是注册示例脚本:

script {
fun register(account: &signer) {
aptos_framework::managed_coin::register<MoonCoin::moon_coin::MoonCoin>(account)
}
}

铸造 coin 需要初始化期间产生的铸造能力。下方 mint 函数接收该能力和数量,并返回包含该数量 coin 的 Coin<T> 结构体。如果 coin 跟踪供应量,也会更新供应量。

module 0x1::coin {
public fun mint<CoinType>(
amount: u64,
_cap: &MintCapability<CoinType>,
): Coin<CoinType> acquires CoinInfo {
if (amount == 0) {
return zero<CoinType>()
};
let maybe_supply = &mut borrow_global_mut<CoinInfo<CoinType>>(coin_address<CoinType>()).supply;
if (option::is_some(maybe_supply)) {
let supply = option::borrow_mut(maybe_supply);
optional_aggregator::add(supply, (amount as u128));
};
Coin<CoinType> { value: amount }
}
}

ManagedCoin 通过入口函数 managed_coin::mint 简化了此操作。


Aptos 提供多个构件支持 coin 转移:

  • coin::deposit<CoinType>:允许任何实体将 coin 存入已调用 coin::register<CoinType> 的账户。
  • coin::withdraw<CoinType>:允许任何实体从账户提取 coin 数量。
  • aptos_account::transfer_coins<CoinType>:将特定 CoinType 的 coin 转移给接收者。