Facebook Libra币开发指南---Move语言开发代币智能合约
  oCSbu1MUxU1k 2023年11月02日 27 0


由于Libra Core基本还处于试验阶段,还没有API和SDK,因此除了命令行之外,没有其他接口。虽然文档中有Move语言介绍,但是在目前的testnet上,还不支持部署基于Move语言的智能合约,因此使我们这种特别喜欢动手尝试的人,非常抓狂。不过还是有牛人,发现可以使用Libra的功能测试框架,来编写和运行Move语言编写的智能合约,所以我们也可以基于这种方法来尝试一把。

代币智能合约

我们可以使用Move语言来开发一个代币的智能合约,代码如下所示:

modules:

module Capability {

// Capability is responsible for declaring an account's permissions.
// We define the notion of an owner, minter and blacklisted.
// Only the owner can assign blacklisted and minter capabilities.

// The owner is defined by hardcoding its account before publishing the module.

// -----------------------------------------------------------------

// Declare owner as a resource. It's only meant to be published once
resource Owner { }

// Declare a resource that declares an address's capabilities.
// Every address using EToken will need to publish a resource themselves.
// However, only the owner can change its content.
resource T {
minter: bool,
blacklisted: bool,
}

// Every account should execute this once before using Capability and EToken module.
// If the sender is the hardcoded owner, then owner capability is published.
// Reverts if already published
public publish() {
let sender: address;
sender = get_txn_sender();

// Publish owner capability if sender is the privileged account
// Uncomment the following line in production and use a real owner account: if (move(sender) == 0x0) {
if (true) {
// Always branch to here when testing, otherwise the test can't complete as the sender address is randomly chosen.
Self.grant_owner_capability();
}

// Publish a new capability with no permissions.
move_to_sender<T>(T{ minter: false, blacklisted: false });

return;
}

// Internal function that grants owner capability
grant_owner_capability() {
move_to_sender<Owner>(Owner {});
return;
}

// Grants minter capability to receiver, but can only succeed if sender owns the owner capability.
public grant_minter_capability(receiver: address, owner_capability: &R#Self.Owner) {
let capability_ref: &mut R#Self.T;

release(move(owner_capability));

// Pull a mutable reference to the receiver's capability, and change its permission.
capability_ref = borrow_global<T>(move(receiver));
*(&mut move(capability_ref).minter) = true;

return;
}

// Grants blacklist capability to receiver, but can only succeed if sender owns the owner capability.
public grant_blacklisted_capability(receiver: address, owner_capability: &R#Self.Owner) {
let capability_ref: &mut R#Self.T;

release(move(owner_capability));

// Pull a mutable reference to the receiver's capability, and change its permission.
capability_ref = borrow_global<T>(move(receiver));
*(&mut move(capability_ref).blacklisted) = true;

return;
}

// This returns an immutable reference to the owner capability if it exists.
// Is used by the owner to show ownership to privileged functions.
// Reverts if owner capability does not exist.
public borrow_owner_capability(): &R#Self.Owner {
let sender: address;
let owner_capability_ref: &mut R#Self.Owner;
let owner_capability_immut_ref: &R#Self.Owner;

sender = get_txn_sender();
owner_capability_ref = borrow_global<Owner>(move(sender));
owner_capability_immut_ref = freeze(move(owner_capability_ref));

return move(owner_capability_immut_ref);
}

// This returns an immutable reference to the general capability if it exists.
// Should be used by every account to prove capabilities.
// Reverts if capability does not exist.
public borrow_capability(): &R#Self.T {
let sender: address;
let capability_ref: &mut R#Self.T;
let capability_immut_ref: &R#Self.T;

sender = get_txn_sender();
capability_ref = borrow_global<T>(move(sender));
capability_immut_ref = freeze(move(capability_ref));

return move(capability_immut_ref);
}

// Return whether the capability allows minting.
public is_minter(capability: &R#Self.T): bool {
let is_minter: bool;
is_minter = *(&move(capability).minter);
return move(is_minter);
}

// Return true the capability is not blacklisted.
public is_not_blacklisted(capability: &R#Self.T): bool {
let is_blacklisted: bool;
is_blacklisted = *(&move(capability).blacklisted);
return !move(is_blacklisted);
}

// Reverts if capability does not allow minting
public require_minter(capability: &R#Self.T) {
let is_minter: bool;
is_minter = Self.is_minter(move(capability));
assert(move(is_minter), 0);
return;
}

// Reverts if capability is blacklisted
public require_not_blacklisted(capability: &R#Self.T) {
let is_not_blacklisted: bool;
is_not_blacklisted = Self.is_not_blacklisted(move(capability));
assert(move(is_not_blacklisted), 0);
return;
}
}

module EToken {

// This module is responsible for an actual eToken.
// For it to be useful a capability has to be published by using the Capability module above.

// -----------------------------------------------------------------

import Transaction.Capability;

// Declare the eToken resource, storing an account's total balance.
resource T {
value: u64,
}

// Publishes an initial zero eToken to the sender.
// Should be called once before using this module.
public publish() {
move_to_sender<T>(T{ value: 0 });
return;
}

// Mint new eTokens.
// Reverts if capability does not allow it.
public mint(value: u64, capability: &R#Capability.T): R#Self.T {
Capability.require_minter(move(capability));
return T{value: move(value)};
}

// Returns an account's eToken balance.
// Reverts if an initial eToken hasn't been published.
public balance(): u64 {
let sender: address;
let token_ref: &mut R#Self.T;
let token_value: u64;

sender = get_txn_sender();
token_ref = borrow_global<T>(move(sender));
token_value = *(&move(token_ref).value);

return move(token_value);
}

// Deposit owned tokens to an payee's address, and destroy the tokens to deposit,
// Reverts if user is blacklisted.
public deposit(payee: address, to_deposit: R#Self.T, capability: &R#Capability.T) {
let payee_token_ref: &mut R#Self.T;
let payee_token_value: u64;
let to_deposit_value: u64;

Capability.require_not_blacklisted(move(capability));

payee_token_ref = borrow_global<T>(move(payee));
payee_token_value = *(©(payee_token_ref).value);

// Unpack and destroy to_deposit tokens
T{ value: to_deposit_value } = move(to_deposit);

// Increase the payees balance with the destroyed token amount
*(&mut move(payee_token_ref).value) = move(payee_token_value) + move(to_deposit_value);

return;
}

// Withdraw an amount of tokens of the sender and return it.
// This works by splitting the token published and returning the specified amount as tokens.
public withdraw(amount: u64, capability: &R#Capability.T): R#Self.T {
let sender: address;
let sender_token_ref: &mut R#Self.T;
let value: u64;

Capability.require_not_blacklisted(move(capability));

sender = get_txn_sender();
sender_token_ref = borrow_global<T>(move(sender));
value = *(©(sender_token_ref).value);

// Make sure that sender has enough tokens
assert(copy(value) >= copy(amount), 1);

// Split the senders token and return the amount specified
*(&mut move(sender_token_ref).value) = move(value) - copy(amount);
return T{ value: move(amount) };
}
}

script:

// Performs simple testing to crudely verify the published modules above.

import Transaction.Capability;
import Transaction.EToken;

main() {
let sender: address;
let owner_capability: &R#Capability.Owner;
let capability: &R#Capability.T;
let minted_tokens: R#EToken.T;
let balance: u64;

sender = get_txn_sender();

// Publish initial capability
Capability.publish();

// Borrow owner_capability for minter delegation
owner_capability = Capability.borrow_owner_capability();

// Delegate itself as a minter
Capability.grant_minter_capability(copy(sender), move(owner_capability));

// Borrow general capability for proof of minting capability
capability = Capability.borrow_capability();

// Publish an eToken account
EToken.publish();

// Mint 100 eTokens and prove minter capability
minted_tokens = EToken.mint(100, copy(capability));

// Deposit the freshly minted tokens to itself
EToken.deposit(move(sender), move(minted_tokens), move(capability));

// Test that the balance corresponds with the intended behaviour
balance = EToken.balance();
assert(move(balance) == 100, 3);

return;
}

将该文件保存到language/functional_tests/tests/testsuite/modules/目录下,然后运行如下命令:

cargo test -p functional_tests eToken

运行结果如下所示:

Facebook Libra币开发指南---Move语言开发代币智能合约_ERC20


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
oCSbu1MUxU1k