揭秘柚子币(EOS)代币创建:从原理到实战
柚子币(EOS)作为曾经风靡一时的区块链平台,其代币创建机制相对独特。理解EOS代币的创建过程,不仅能帮助我们深入理解EOS区块链的技术架构,也能为我们设计和发行自己的区块链代币提供宝贵的参考。本文将深入探讨EOS代币的创建原理和步骤,希望能帮助读者对EOS代币有更全面的认识。
EOS代币创建的基石:智能合约
EOS代币的创建和管理,从根本上来说,是依赖于在EOS区块链上部署和执行智能合约实现的。 这些智能合约定义了代币的行为规则,如发行、转移和销毁等。 EOS智能合约通常使用C++编程语言编写,随后通过EOSIO工具链编译成WebAssembly (WASM) 字节码。WASM的设计目标是提供接近原生性能的执行效率和跨平台的可移植性,这使得EOS智能合约能够在EOS虚拟机上高效、安全地运行,从而保证了代币交易的快速处理和区块链状态的一致性。
创建EOS代币的核心智能合约通常需要实现以下几个关键的功能模块:
-
创建 (create):
此函数允许代币的发行者定义代币的关键属性和元数据,例如代币符号 (symbol,例如:EOS)、精度 (precision,小数点后的位数,决定了代币的最小可分割单位)、以及代币的最大供应量 (max_supply,代币发行总量的上限)。 通常,
create
函数只允许代币的发行者调用一次,以确保代币属性的唯一性和不可篡改性。 这也是代币创建的初始化阶段。 -
发行 (issue):
issue
函数的功能是允许代币的发行者按照预先设定的规则和策略,向指定的账户增发新的代币。 这个函数通常也仅限于代币发行者调用,并且需要极其谨慎地控制每次发行的数量,以避免因过度发行导致通货膨胀,从而损害代币的价值和生态系统的稳定。 发行通常需要记录发行memo,说明发行的原因。 -
转移 (transfer):
transfer
函数是代币智能合约中最基本和最核心的功能之一,它允许用户在不同的EOS账户之间进行代币转移操作。 用户通过调用transfer
函数,可以将自己账户中的代币转移到其他用户的账户中。 这是用户参与代币经济活动,进行交易、支付、奖励等操作的基础。transfer
函数通常包含转账金额、接收方账户、以及可选的memo信息,用于记录转账的原因或目的。 -
退休 (retire):
retire
函数允许持有代币的用户主动销毁自己账户中的代币。 通过调用retire
函数,用户可以减少代币的流通总量。 这个函数通常用于实现一些特殊的代币销毁机制,例如,根据某种规则定期销毁一定数量的代币,或者通过销毁代币来换取其他权益等。销毁通常需要记录销毁memo,说明销毁的原因。
EOS代币创建的详细步骤
创建EOS代币是一个涉及多个环节的过程,从环境配置到智能合约的部署和测试,每个步骤都至关重要。以下是创建EOS代币的详细步骤:
-
环境搭建: 搭建EOS开发环境是首要步骤。这涉及安装EOSIO软件开发工具包(SDK),它包含了构建、测试和部署智能合约所需的所有组件。配置
cleos
命令行界面是关键,它充当与EOS区块链交互的桥梁。nodeos
是EOSIO区块链节点软件,用于模拟或连接到真实的EOS区块链网络。正确配置环境包括设置环境变量,确保cleos
能够连接到本地或远程的nodeos
实例,并同步区块链数据。 -
编写智能合约: 智能合约是代币的核心逻辑。使用C++编写,需要定义代币的数据结构,例如使用
eosio::multi_index
定义账户余额表,用于存储每个账户的代币余额。实现关键功能至关重要,包括:create
(创建代币)、issue
(发行代币)、transfer
(转移代币)和retire
(销毁代币)。合约必须遵循EOSIO的智能合约开发规范,并利用其提供的API进行数据存储和权限管理。参考EOSIO官方提供的示例代码或使用现有的开源代币合约模板可以加速开发过程,例如eosio.token
合约。根据自身需求修改合约,包括调整代币的属性和功能。 -
编译智能合约: 使用EOSIO提供的
eosio-cpp
工具链将C++代码编译成WebAssembly (WASM)代码和应用程序二进制接口(ABI)文件。WASM代码是智能合约在EOS虚拟机上执行的字节码,ABI文件描述了智能合约的接口,定义了合约中可调用的函数及其参数类型,方便客户端程序或cleos
与智能合约进行交互。编译过程需要指定合约的入口点和目标平台。 -
部署智能合约: 将编译好的WASM代码和ABI文件部署到EOS区块链上。需要在EOS区块链上创建一个新的EOS账户,该账户将作为智能合约的owner,拥有管理和控制智能合约的权限。使用
cleos create account
命令创建账户。然后,使用cleos set contract
命令将WASM代码和ABI文件部署到该账户。部署过程中,需要提供合约的账户名、WASM文件路径和ABI文件路径。部署完成后,智能合约即可在EOS区块链上执行。 -
调用
create
函数: 使用cleos push action
命令行工具调用智能合约的create
函数,定义代币的基本信息。这些信息包括代币符号(例如"MYTOKEN")、精度(小数点后的位数,例如4)和最大供应量(例如10000000.0000 MYTOKEN)。create
函数通常由合约的owner调用,用于初始化代币的属性。调用时需要指定合约账户名、函数名和函数参数,并使用owner账户的私钥进行签名授权。 -
调用
issue
函数: 使用cleos push action
命令行工具调用智能合约的issue
函数,发行代币到指定的账户。issue
函数允许合约owner创建新的代币并将其分配给特定的账户。需要指定接收代币的账户名和发行的代币数量,例如将1000.0000 MYTOKEN发行给user.account
。同样,调用需要使用owner账户的私钥进行签名授权。 -
测试和验证: 完成代币的部署和发行后,进行全面的测试和验证至关重要。测试内容包括:使用
transfer
函数转移代币,验证交易的正确性和账户余额的更新;使用自定义的查询函数或cleos get currency balance
命令查询账户余额,确保余额显示正确;测试其他可能的功能,例如销毁代币或冻结账户。通过充分的测试,可以确保代币的功能正常,符合预期,并且没有安全漏洞。可以使用EOSIO提供的测试框架进行自动化测试。
EOS代币创建的注意事项
创建EOS代币涉及多个关键方面,必须认真考虑以确保代币的安全性、功能性和长期可持续性:
-
安全性:
智能合约的安全性是重中之重。漏洞可能导致资金损失或合约功能异常。
- 代码审计: 对智能合约代码进行全面细致的审查,识别潜在的安全隐患,如整数溢出(Integer Overflow/Underflow)、重入攻击(Reentrancy Attack)、拒绝服务(DoS)攻击等。
- 形式化验证: 考虑使用形式化验证方法,从数学层面证明智能合约的正确性,减少逻辑错误。
- 安全审计工具: 利用专业的智能合约安全审计工具,例如Mythril、Slither等,辅助代码审计,自动化发现常见漏洞。
- Bug赏金计划: 启动Bug赏金计划,鼓励社区成员参与安全测试,提交潜在漏洞,并提供奖励。
- 持续监控: 部署智能合约监控系统,实时检测异常行为,例如非授权交易、gas消耗异常等,及时采取应对措施。
-
权限控制:
精细化的权限控制机制对于防止未经授权的操作至关重要。
- 角色管理: 采用基于角色的访问控制(RBAC),为不同用户分配不同的角色,并赋予相应的权限。
- 访问控制列表(ACL): 使用访问控制列表,精确控制每个账户对智能合约函数的访问权限。
- 多重签名: 对于关键操作,例如代币发行,实施多重签名机制,需要多个授权账户的批准才能执行。
- 时间锁: 引入时间锁机制,限制某些操作只能在特定时间段内执行,防止紧急情况下的恶意操作。
- 紧急停止: 设计紧急停止功能,在发现严重漏洞或遭受攻击时,可以暂停合约功能,防止进一步损失。
-
代币模型:
根据应用场景选择合适的代币模型,影响代币的经济特性和使用方式。
- 固定供应量: 限制代币的总量,适用于价值存储或稀缺资源代表等场景。
- 通货膨胀: 每年增发一定比例的代币,奖励节点或参与者,激励网络维护和发展。
- 通货紧缩: 通过回购销毁等机制减少代币总量,提升代币的稀缺性。
- 弹性供应: 根据市场需求动态调整代币供应量,维持代币价格稳定。
- 治理代币: 用于参与社区治理,投票决定协议升级和参数调整等。
-
Gas成本:
在EOS上,资源(CPU和NET)消耗直接影响交易执行的成本。优化智能合约代码以降低资源消耗,提高效率。
- 代码优化: 编写高效的代码,避免不必要的循环和计算,减少资源消耗。
- 数据存储优化: 选择合适的数据类型和存储方式,减少存储空间和读取操作。
- 批量处理: 将多个操作合并为一个交易执行,减少交易次数,降低资源消耗。
- RAM优化: 合理使用RAM资源,避免不必要的RAM消耗。
- 资源租赁策略: 了解EOS资源租赁模型,根据实际需求合理租赁CPU和NET资源。
-
社区参与:
积极发展和维护社区,对于代币的长期发展至关重要。
- 社区论坛: 建立社区论坛,方便社区成员交流讨论,提出建议和反馈。
- 社交媒体: 利用社交媒体平台,例如Twitter、Telegram等,进行宣传和推广,扩大社区影响力。
- 线上活动: 定期举办线上活动,例如AMA(Ask Me Anything)、技术讲座等,增强社区互动。
- 开发者社区: 鼓励开发者参与代币的开发和维护,贡献代码和创意。
- 治理机制: 建立完善的治理机制,允许社区成员参与代币的决策和管理。
EOS代币智能合约示例代码片段
以下是一个简化的EOS代币智能合约的C++代码片段,旨在展示基本概念。请注意,这仅为示例,实际应用中需要进行安全审计和更全面的错误处理。此代码片段不包含所有必要的功能,例如权限管理和代币符号的注册。
C++代码:
#include <eosio/eosio.hpp>
#include <eosio/asset.hpp>
using namespace eosio;
CONTRACT token : public contract {
public:
using contract::contract;
ACTION create( const name& issuer,
const asset& maximum_supply);
ACTION issue( const name& to, const asset& quantity, const std::string& memo );
ACTION transfer( const name& from,
const name& to,
const asset& quantity,
const std::string& memo );
private:
TABLE accounts {
asset balance;
uint64_t primary_key() const { return balance.symbol.code().raw(); }
};
typedef eosio::multi_index< user_code("accounts"_n), accounts > account_table;
};
[[eosio::action]]
void token::create( const name& issuer,
const asset& maximum_supply) {
require_auth( get_self() );
check( maximum_supply.is_valid(), "无效的 maximum_supply");
check( maximum_supply.amount > 0, "maximum_supply 必须是正数");
symbol supply_symbol = maximum_supply.symbol;
check( supply_symbol.is_valid(), "符号无效" );
stats statstable( get_self(), supply_symbol.code().raw() );
auto existing = statstable.find( supply_symbol.code().raw() );
check( existing == statstable.end(), "代币已经存在" );
statstable.emplace( get_self(), [&]( auto& s ) {
s.supply.symbol = maximum_supply.symbol;
s.max_supply = maximum_supply;
s.issuer = issuer;
});
}
[[eosio::action]]
void token::issue( const name& to, const asset& quantity, const std::string& memo ) {
auto sym = quantity.symbol;
check( sym.is_valid(), "无效的符号" );
check( memo.size() <= 256, "memo 不能超过 256 字节" );
stats statstable( get_self(), sym.code().raw() );
auto existing = statstable.find( sym.code().raw() );
check( existing != statstable.end(), "代币不存在,请先创建" );
const auto& st = *existing;
require_auth( st.issuer );
check( quantity.is_valid(), "无效的 quantity" );
check( quantity.amount > 0, "必须 issue 正数的数量" );
check( quantity.symbol == st.supply.symbol, "符号不匹配" );
check( quantity.amount <= st.max_supply.amount - st.supply.amount, "超过最大供应量" );
statstable.modify( st, get_self(), [&]( auto& s ) {
s.supply += quantity;
});
add_balance( st.issuer, quantity, st.issuer );
if( to != st.issuer ) {
SEND_INLINE_ACTION( eosio::permission_level{st.issuer, "active"_n},
"eosio.token"_n, "transfer"_n,
std::make_tuple(st.issuer, to, quantity, memo)
);
}
}
[[eosio::action]]
void token::transfer( const name& from,
const name& to,
const asset& quantity,
const std::string& memo ) {
check( from != to, "不能转账给自己" );
require_auth( from );
check( is_account( to ), "账户不存在 to" );
auto sym = quantity.symbol.code().raw();
stats statstable( get_self(), sym );
const auto& st = statstable.get( sym, "代币不存在" );
require_recipient( from );
require_recipient( to );
check( quantity.is_valid(), "无效的 quantity" );
check( quantity.amount > 0, "必须转账正数的数量" );
check( quantity.symbol == st.supply.symbol, "符号不匹配" );
check( memo.size() <= 256, "memo 不能超过 256 字节" );
sub_balance( from, quantity );
add_balance( to, quantity, from );
}
void token::sub_balance( const name& owner, const asset& value ) {
account_table accounts( get_self(), owner.value );
const auto& from = accounts.get( value.symbol.code().raw(), "账户中没有这个符号的余额" );
check( from.balance.amount >= value.amount, "余额不足" );
if( from.balance.amount == value.amount ) {
accounts.erase( from );
} else {
accounts.modify( from, get_self(), [&]( auto& a ) {
a.balance -= value;
});
}
}
void token::add_balance( const name& owner, const asset& value, const name& ram_payer )
{
account_table accounts( get_self(), owner.value );
auto to = accounts.find( value.symbol.code().raw() );
if( to == accounts.end() ) {
accounts.emplace( ram_payer, [&]( auto& a ){
a.balance = value;
});
} else {
accounts.modify( to, ram_payer, [&]( auto& a ) {
a.balance += value;
});
}
}
EOSIO_DISPATCH( token, (create)(issue)(transfer) )
这段代码定义了一个名为
token
的合约,它继承自
eosio::contract
。它包含了以下几个关键Action:
-
create
: 用于创建新的代币,需要指定发行者和最大供应量。 -
issue
: 用于发行代币到指定账户,需要指定接收者、数量和备注。 -
transfer
: 用于从一个账户转账代币到另一个账户,需要指定发送者、接收者、数量和备注。
代码还包含了
accounts
表,用于存储每个账户的代币余额。
stats
表用于存储代币的统计信息,例如总供应量和最大供应量。
重要提示: 这段代码只是一个基本示例,不应该直接用于生产环境。在实际应用中,你需要进行充分的安全审计,并添加更多的功能和错误处理机制。例如,你应该添加权限控制,防止未经授权的账户发行代币。你应该处理溢出错误,并确保代码的效率和可扩展性。
include
#include <eosio/asset.hpp>
using namespace eosio;
这段代码声明使用
eosio
命名空间,避免在代码中重复书写
eosio::
前缀,简化代码。
CONTRACT mytoken : public contract {
定义一个名为
mytoken
的合约,该合约继承自
eosio::contract
。继承
eosio::contract
是创建智能合约的必要步骤,它提供了智能合约所需的基础设施和接口。
public:
using contract::contract;
使用
using contract::contract;
继承父类
contract
的构造函数。这允许子类
mytoken
使用父类的构造函数,无需显式重新定义。
ACTION create( const name& issuer,
const asset& maximum_supply ) {
require_auth( get_self() );
auto sym = maximum_supply.symbol;
check( sym.is_valid(), "invalid symbol name" );
check( maximum_supply.is_type(sym), "symbol precision mismatch" );
check( maximum_supply.amount > 0, "max-supply must be positive" );
stats statstable( get_self(), sym.code().raw() );
auto existing = statstable.find( sym.code().raw() );
check( existing == statstable.end(), "token with symbol already exists" );
statstable.emplace( get_self(), [&]( auto& s ) {
s.supply.symbol = maximum_supply.symbol;
s.max_supply = maximum_supply;
s.issuer = issuer;
});
}
create
action 用于创建新的代币。参数
issuer
是代币的发行者账户名,
maximum_supply
是代币的最大供应量。使用
require_auth(get_self())
验证调用者是否为合约自身,只有合约自身才能创建代币。然后,从
maximum_supply
中提取代币符号,并进行一系列检查:
-
sym.is_valid()
检查代币符号是否有效。 -
maximum_supply.is_type(sym)
检查供应量的精度是否与代币符号匹配。 -
maximum_supply.amount > 0
检查最大供应量是否为正数。
接着,创建一个名为
statstable
的表,用于存储代币的统计信息。使用
statstable.find(sym.code().raw())
检查是否已存在具有相同符号的代币。如果不存在,则使用
statstable.emplace
创建一个新的代币记录,并将发行者、最大供应量等信息存储到表中。
ACTION issue( const name& to, const asset& quantity, const std::string& memo ) {
auto sym = quantity.symbol;
check( sym.is_valid(), "invalid symbol name" );
check( memo.size() <= 256, "memo has more than 256 bytes" );
stats statstable( get_self(), sym.code().raw() );
auto existing = statstable.find( sym.code().raw() );
check( existing != statstable.end(), "token with symbol does not exist, use create action first" );
const auto& st = *existing;
require_auth( st.issuer );
check( quantity.is_type(st.supply.symbol), "symbol precision mismatch" );
check( quantity.amount > 0, "must issue positive quantity" );
check( quantity.amount <= st.max_supply.amount - st.supply.amount, "quantity exceeds available supply");
statstable.modify( st, same_payer, [&]( auto& s ) {
s.supply += quantity;
});
add_balance( st.issuer, quantity, st.issuer );
if( to != st.issuer ) {
SEND_INLINE_ACTION( *this, transfer, {st.issuer, "active"_n},
{st.issuer, to, quantity, memo}
);
}
}
issue
action 用于发行代币。参数
to
是接收代币的账户名,
quantity
是要发行的代币数量,
memo
是备注信息。检查代币符号是否有效,以及备注信息是否超过 256 字节。然后,检查是否已存在具有相同符号的代币。只有代币的发行者才能发行代币,因此使用
require_auth(st.issuer)
进行验证。接着,检查发行的数量是否有效,包括精度是否匹配、数量是否为正数,以及发行量是否超过剩余供应量。更新
statstable
中的代币供应量,并使用
add_balance
将代币添加到接收者的账户余额中。如果接收者不是发行者自身,则调用
transfer
action 将代币从发行者转移到接收者。
ACTION transfer( const name& from,
const name& to,
const asset& quantity,
const string& memo ) {
check( from != to, "cannot transfer to self" );
require_auth( from );
check( is_account( to ), "to account does not exist");
auto sym = quantity.symbol.code();
stats statstable( get_self(), sym.raw() );
const auto& st = statstable.get( sym.raw() );
require_recipient( from );
require_recipient( to );
check( quantity.is_type(st.supply.symbol), "symbol precision mismatch" );
check( quantity.amount > 0, "must transfer positive quantity" );
sub_balance( from, quantity );
add_balance( to, quantity, from );
}
transfer
action 用于转移代币。参数
from
是发送者的账户名,
to
是接收者的账户名,
quantity
是要转移的代币数量,
memo
是备注信息。检查发送者和接收者是否为同一账户,使用
require_auth(from)
验证发送者是否授权交易。使用
is_account(to)
确保接收者账户存在。然后,检查转移的数量是否有效,包括精度是否匹配、数量是否为正数。使用
sub_balance
从发送者的账户余额中扣除代币,并使用
add_balance
将代币添加到接收者的账户余额中。
require_recipient
用于通知相关账户,触发相应的操作(例如,更新账户余额)。
private:
struct stats {
asset supply;
asset max_supply;
name issuer;
uint64_t primary_key() const { return supply.symbol.code().raw(); }
};
typedef eosio::multi_index< "stat"_n, stats > stats_table;
stats
结构体用于存储代币的统计信息,包括总供应量 (
supply
)、最大供应量 (
max_supply
) 和发行者 (
issuer
)。
primary_key()
函数返回代币符号的代码,作为
stats_table
的主键。
stats_table
是一个多索引表,使用
"stat"_n
作为表名,存储
stats
结构体的数据。多索引表允许通过不同的索引访问数据,提高查询效率。
struct account {
asset balance;
uint64_t primary_key() const { return balance.symbol.code().raw(); }
};
typedef eosio::multi_index< "accounts"_n, account > accounts_table;
account
结构体用于存储账户的代币余额 (
balance
)。
primary_key()
函数返回代币符号的代码,作为
accounts_table
的主键。
accounts_table
是一个多索引表,使用
"accounts"_n
作为表名,存储
account
结构体的数据。该表用于跟踪每个账户的代币余额。
void add_balance( const name& owner, const asset& value, const name& ram_payer )
{
accounts_table to_acnts( get_self(), owner.value );
auto to = to_acnts.find( value.symbol.code().raw() );
if( to == to_acnts.end() ) {
to_acnts.emplace( ram_payer, [&]( auto& a ){
a.balance = value;
});
} else {
to_acnts.modify( to, same_payer, [&]( auto& a ) {
a.balance += value;
});
}
}
void sub_balance( const name& owner, const asset& value ) {
accounts_table from_acnts( get_self(), owner.value );
const auto& from = from_acnts.get( value.symbol.code().raw(), "no balance object found" );
check( from.balance.amount >= value.amount, "overdrawn balance" );
from_acnts.modify( from, same_payer, [&]( auto& a ) {
a.balance -= value;
});
}
add_balance
函数用于增加账户的代币余额。创建一个
accounts_table
对象,并使用
owner
的账户名作为作用域。然后,使用
to_acnts.find
查找该账户是否已存在该代币的余额记录。如果不存在,则使用
to_acnts.emplace
创建一个新的余额记录,并将余额设置为
value
。如果已存在,则使用
to_acnts.modify
修改现有余额,将
value
加到现有余额上。
ram_payer
指定为新记录支付 RAM 的账户。
sub_balance
函数用于减少账户的代币余额。创建一个
accounts_table
对象,并使用
owner
的账户名作为作用域。然后,使用
from_acnts.get
查找该账户的余额记录。如果不存在,则抛出异常。如果存在,则检查余额是否足够扣除
value
。如果余额不足,则抛出异常。如果余额足够,则使用
from_acnts.modify
修改现有余额,将
value
从现有余额中扣除。
};
EOSIO_DISPATCH( mytoken, (create)(issue)(transfer) )
EOSIO_DISPATCH
宏用于将合约的 actions 暴露给 EOSIO 系统。它将
mytoken
合约的
create
,
issue
和
transfer
actions 注册到合约中,使得外部用户可以调用这些 actions 与合约进行交互。 这是EOSIO智能合约的标准写法。
这段代码展示了创建、发行和转移代币的基本流程。实际应用中,需要根据具体需求进行修改和完善,例如增加权限管理、费用收取等功能。同时,需要注意代码的安全性,防止出现漏洞,确保代币的安全。
创建EOS代币是一个相对复杂的过程,需要你具备一定的编程基础和区块链知识。通过本文的介绍,相信你对EOS代币的创建原理和步骤有了更深入的了解。希望你能利用这些知识,创建出属于你自己的区块链代币。