您现在的位置是: 首页 >  焦点 焦点

EOS代币发行教程:别再犹豫!你的区块链项目就差它了!

时间:2025-03-16 54人已围观

柚子币代币创建步骤

前言

柚子币(EOS)作为一条高性能区块链平台,以其卓越的交易速度和可扩展性著称,为去中心化应用(DApps)的开发提供了坚实的基础。EOS的设计理念是简化DApp的开发流程,并为开发者提供类似于传统操作系统的功能,从而降低开发门槛。EOS允许开发者在其上发行和管理各种自定义代币,这些代币不仅限于简单的价值转移,还可以代表多种多样的资产、权益证明、会员资格、游戏内物品,甚至复杂金融工具的凭证,极大地拓展了EOS生态系统的应用广度和深度。通过自定义代币,开发者能够构建更具创新性和实用性的DApps,满足不同行业和用户的特定需求。本文将深入且详尽地介绍在EOS平台上创建自定义代币的完整步骤,包括智能合约的编写、部署、代币发行以及相关注意事项,旨在帮助开发者即使没有任何先前的区块链经验,也能快速上手并掌握EOS代币发行的关键技能,进而充分利用EOS平台的强大功能来构建创新的去中心化应用。

一、准备工作

在开始创建基于EOS区块链的代币之前,充分的准备工作至关重要。以下是您需要准备的工具、知识和先决条件,以确保流畅的开发和部署过程:

  1. EOS账户: 你需要一个已经激活且拥有足够资源的EOS账户。该账户将作为智能合约的部署者和代币的管理账户。没有EOS账户,你可以选择通过支持EOS交易的加密货币交易所购买,或者利用第三方钱包服务创建。注意,创建后务必进行激活,通常需要少量EOS抵押才能开始使用。激活后,请确保存有足够的CPU和NET资源,以便进行后续的合约部署和代币操作。
  2. EOS开发环境 (EOSIO SDK): 你需要安装EOSIO软件开发工具包 (SDK),它是开发和部署EOS智能合约的必要组件。EOSIO SDK包含一系列工具,其中最常用的包括 cleos (命令行界面工具),用于与EOS区块链交互,以及 nodeos (本地EOS节点),用于在本地模拟EOS区块链环境。你可以从EOSIO官方网站下载最新版本的EOSIO SDK。强烈建议配置本地开发环境,方便调试和测试。
  3. 智能合约开发工具 (C++ IDE): EOS智能合约通常使用C++编写。因此,你需要一个合适的C++集成开发环境 (IDE) 来编写、编译和调试你的智能合约代码。常用的选择包括Visual Studio Code(配合C++插件)、Eclipse CDT或Clion。选择一个你熟悉且 comfortable 的 IDE 可以提高开发效率。配置IDE时,确保已正确设置C++编译器和相关依赖。
  4. 智能合约基础知识: 深入理解智能合约的概念以及EOS智能合约的开发流程至关重要。你需要掌握EOS智能合约的基本结构,包括Actions(智能合约提供的可执行函数)、Tables(用于存储数据的持久化数据结构)、权限管理(控制对智能合约Actions和数据的访问)等核心概念。同时,了解EOSIO的系统合约,如`eosio.bios`和`eosio.token`,有助于更好地理解EOS生态系统。
  5. EOSIO.Token合约详解: 深入了解EOSIO.Token合约的结构、功能和实现原理。EOSIO.Token是EOS官方提供的标准代币合约,提供了一套完整的代币管理功能,包括代币的发行 (issue)、转移 (transfer)、销毁 (retire)、以及管理权限等。你可以直接使用EOSIO.Token合约创建你的代币,或者在此基础上进行二次开发,根据你的特定业务需求进行定制。理解EOSIO.Token合约的源码,可以帮助你更好地进行扩展和定制,并避免潜在的安全风险。研究合约中的`inline` actions 和权限控制机制。

二、创建智能合约

  1. 创建合约目录: 为了组织清晰且便于管理,首先需要创建一个专门的目录来存放你的代币智能合约的所有相关代码文件。这个目录将作为你项目的根目录,确保所有文件都井然有序地存放其中。例如,你可以选择一个具有描述性的名称,比如 mytoken ,来创建一个目录。

    mkdir mytoken 命令将在当前目录下创建一个名为 mytoken 的新目录。 cd mytoken 命令则会将你的终端工作目录切换到新创建的 mytoken 目录中,以便后续操作都在该目录下进行。

    bash

    mkdir mytoken
    cd mytoken
  2. 创建合约文件: 在成功创建并进入 mytoken 目录后,下一步是创建构成智能合约的三个关键文件。这三个文件分别负责合约的定义、实现和接口描述,它们协同工作,共同构成一个完整的 EOSIO 智能合约。
    • mytoken.hpp :这是一个头文件,用于定义合约的结构和接口。它包含合约类的声明、Action 的声明和 Table 的声明。简单来说,它定义了合约的蓝图,描述了合约能做什么,以及如何与合约进行交互。

    • mytoken.cpp :这是一个源文件,用于实现 mytoken.hpp 中声明的合约接口。它包含了 Action 的具体实现代码和 Table 的数据存储逻辑。该文件是合约的核心,定义了合约的具体行为。

    • mytoken.abi :这是一个 ABI (Application Binary Interface) 文件,它描述了合约的接口,包括 Action 的参数类型、Table 的数据结构等信息。 cleos 等工具使用 ABI 文件与合约进行交互,将人类可读的命令转换为合约可以理解的二进制数据。该文件是连接外部世界与合约的桥梁。

  3. 编写合约代码: 现在,需要编写实际的 C++ 代码来定义你的代币合约。这涉及到编辑 mytoken.hpp mytoken.cpp 文件,以定义代币的属性、功能和逻辑。
    • mytoken.hpp : 头文件定义了合约的结构。 这通常包括定义合约类,以及声明将用于与合约交互的 actions 和数据表 (tables)。actions 是合约可以执行的函数,而 tables 用于在区块链上持久化存储数据。

      cpp

include

include

using namespace eosio;

CONTRACT mytoken : public contract { public: using contract::contract;

   ACTION create( const name& issuer, const asset& maximum_supply ) {
       // 权限验证:只有合约自身可以调用此action
       require_auth( _self );

       // 符号有效性检查:最大供给量必须是有效符号
       check( maximum_supply.is_valid(), "无效的 maximum_supply 符号" );

       // 符号非负检查:最大供给量必须是非负数
       check( maximum_supply.amount > 0, "maximum_supply 必须为正数" );

       // 符号是否已存在检查:使用 symbol_code 获取符号,如果已存在则抛出异常
       symbol symbol = maximum_supply.symbol;
       stats statstable( _self, symbol.code().raw() );
       auto existing = statstable.find( symbol.code().raw() );
       check( existing == statstable.end(), "token 符号已经存在" );

       // 创建 token 统计信息
       statstable.emplace( _self, [&]( auto& s ) {
          s.supply.symbol = maximum_supply.symbol;
          s.max_supply    = maximum_supply;
          s.issuer        = issuer;
       });
   }

   ACTION issue( const name& to, const asset& quantity, const string& memo ) {
       // 权限验证:只有 token 的发行者可以调用此action
       auto sym = quantity.symbol;
       check( sym.is_valid(), "无效的符号" );
       check( memo.size() <= 256, "memo 不能超过 256 个字符" );

       stats statstable( _self, sym.code().raw() );
       auto existing = statstable.find( sym.code().raw() );
       check( existing != statstable.end(), "token 符号不存在,请先创建" );
       const auto& st = *existing;

       require_auth( st.issuer );
       check( quantity.is_valid(), "无效的 quantity 符号" );
       check( quantity.amount > 0, "必须发行正数数量的 token" );

       check( quantity.symbol == st.supply.symbol, "符号不匹配" );
       check( quantity.amount <= st.max_supply.amount - st.supply.amount, "超过最大供给量" );

       statstable.modify( st, _self, [&]( 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 }
          );
       }
   }

   ACTION transfer( const name& from, const name& to, const asset& quantity, const string& memo ) {
       check( from != to, "不能给自己转账" );
       require_auth( from );
       check( is_account( to ), "目标账户不存在" );
       auto sym = quantity.symbol.code().raw();
       stats statstable( _self, sym );
       const auto& st = statstable.get( sym );

       require_recipient( from );
       require_recipient( to );

       check( quantity.is_valid(), "无效的 quantity 符号" );
       check( quantity.amount > 0, "必须转账正数数量的 token" );
       check( quantity.symbol == st.supply.symbol, "符号不匹配" );
       check( memo.size() <= 256, "memo 不能超过 256 个字符" );

       sub_balance( from, quantity );
       add_balance( to, quantity, from );
   }

   ACTION retire( const asset& quantity, const string& memo ) {
        auto sym = quantity.symbol;
        check( sym.is_valid(), "无效的符号" );
        check( memo.size() <= 256, "memo 不能超过 256 个字符" );

        stats statstable( _self, sym.code().raw() );
        auto existing = statstable.find( sym.code().raw() );
        check( existing != statstable.end(), "token 符号不存在" );
        const auto& st = *existing;

        require_auth( st.issuer );
        check( quantity.is_valid(), "无效的 quantity 符号" );
        check( quantity.amount > 0, "必须销毁正数数量的 token" );
        check( quantity.symbol == st.supply.symbol, "符号不匹配" );

        statstable.modify( st, _self, [&]( auto& s ) {
           s.supply -= quantity;
        });

        sub_balance( st.issuer, quantity );
   }

private: TABLE accounts { asset balance;

       uint64_t primary_key() const { return balance.symbol.raw(); }
  };

   typedef multi_index<"accounts"_n, accounts> account_table;

    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;

    void add_balance( const name& owner, const asset& value, const name& ram_payer ) {
      account_table accounts(_self, owner.value);
      auto sym = value.symbol.code().raw();
      auto it = accounts.find(sym);
      if( it == accounts.end() ) {
         accounts.emplace(ram_payer, [&]( auto& a ){
           a.balance = value;
         });
      } else {
         accounts.modify(it, ram_payer, [&]( auto& a ) {
           a.balance += value;
         });
      }
   }

   void sub_balance( const name& owner, const asset& value ) {
      account_table accounts(_self, owner.value);

      auto sym = value.symbol.code().raw();
      const auto& from = accounts.get(sym, "没有找到余额对象");
      check( from.balance.amount >= value.amount, "overdrawn balance" );

      accounts.modify( from, _self, [&]( auto& a ) {
          a.balance -= value;
      });
   }

};

  • mytoken.cpp :
  • cpp

    include "mytoken.hpp"

    ACTION mytoken::create(const name& issuer, const asset& maximum_supply)

    此Action用于创建新的代币。只有部署合约的账户才能调用此Action。 issuer 参数指定了有权发行代币的账户。 maximum_supply 参数定义了该代币的最大发行量,包含代币符号和精度。

    require_auth(get_self());
    
    auto sym = maximum_supply.symbol;
    check(sym.is_valid(), "invalid symbol name");
    check(maximum_supply.is_amount_within_range(), "invalid supply");
    
    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;
    });
    

    require_auth(get_self()); 验证调用此Action的账户是否为合约部署账户,确保只有合约拥有者才能创建代币。

    auto sym = maximum_supply.symbol; maximum_supply 变量中提取代币符号。

    check(sym.is_valid(), "invalid symbol name"); 检查代币符号是否有效。无效的符号会导致后续操作失败。

    check(maximum_supply.is_amount_within_range(), "invalid supply"); 检查提供的最大供应量是否在允许的范围内。 这个范围通常由底层EOSIO平台的限制决定。

    stats statstable(get_self(), sym.code().raw()); 创建一个用于存储代币统计信息的表 statstable 。 表的作用域是合约自身,主键是代币符号的哈希值。

    auto existing = statstable.find(sym.code().raw()); 尝试在 statstable 表中查找是否已存在具有相同符号的代币。

    check(existing == statstable.end(), "token with symbol already exists"); 检查是否找到了现有的代币。 如果找到了,则说明该符号的代币已经存在,操作将失败。

    statstable.emplace(get_self(), [&](auto& s) { ... }); 如果不存在具有相同符号的代币,则在 statstable 表中创建一个新的记录。 get_self() 作为 ram_payer ,表示合约本身支付RAM费用。Lambda表达式用于初始化新记录的各个字段: supply.symbol , max_supply issuer

    }

    ACTION mytoken::issue(const name& to, const asset& quantity, const string& memo)

    此Action用于发行代币到指定的账户。只有代币的发行者(在create Action中指定的账户)才能调用此Action。 to 参数指定接收代币的账户。 quantity 参数定义了要发行的代币数量,包含代币符号和数量。 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, create token first");
    const auto& st = *existing;
    
    require_auth(st.issuer);
    check(quantity.is_amount_within_range(), "quantity magnitude is too large");
    check(quantity.symbol == st.supply.symbol, "symbol precision mismatch");
    
    statstable.modify(st, get_self(), [&](auto& s) {
        s.supply += quantity;
    });
    
    add_balance(st.issuer, quantity, st.issuer);
    

    auto sym = quantity.symbol; quantity 变量中提取代币符号。

    check(sym.is_valid(), "invalid symbol name"); 验证代币符号的有效性。

    check(memo.size() <= 256, "memo has more than 256 bytes"); 检查memo的大小是否超过256字节的限制。

    stats statstable(get_self(), sym.code().raw()); 创建一个用于存储代币统计信息的表 statstable 。 表的作用域是合约自身,主键是代币符号的哈希值。

    auto existing = statstable.find(sym.code().raw()); 尝试在 statstable 表中查找具有相同符号的代币。

    check(existing != statstable.end(), "token with symbol does not exist, create token first"); 确保该符号的代币已经存在。 如果不存在,则操作失败,因为必须先创建代币。

    const auto& st = *existing; 获取找到的代币的统计信息。

    require_auth(st.issuer); 验证调用此Action的账户是否为代币的发行者。

    check(quantity.is_amount_within_range(), "quantity magnitude is too large"); 检查要发行的代币数量是否在允许的范围内。

    check(quantity.symbol == st.supply.symbol, "symbol precision mismatch"); 确保要发行的代币符号与已创建的代币符号一致。

    statstable.modify(st, get_self(), [&](auto& s) { s.supply += quantity; }); 修改 statstable 表中的记录,增加代币的发行量。 get_self() 作为 ram_payer ,表示合约本身支付RAM费用。

    add_balance(st.issuer, quantity, st.issuer); 调用 add_balance 函数,将代币发行到指定账户。发行者的账户余额也会增加。

    }

    ACTION mytoken::transfer(const name& from, const name& to, const asset& quantity, const string& memo)

    此Action用于从一个账户转移代币到另一个账户。 from 参数指定发送代币的账户。 to 参数指定接收代币的账户。 quantity 参数定义了要转移的代币数量。 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_amount_within_range(), "quantity magnitude is too large");
    check(quantity.symbol == st.supply.symbol, "symbol precision mismatch");
    check(memo.size() <= 256, "memo has more than 256 bytes");
    
    sub_balance(from, quantity);
    add_balance(to, quantity, from);
    

    check(from != to, "cannot transfer to self"); 验证发送方和接收方是否为同一账户。不允许向自己转账。

    require_auth(from); 验证调用此Action的账户是否为发送方账户。只有账户所有者才能转移代币。

    check(is_account(to), "to account does not exist"); 验证接收方账户是否存在。

    auto sym = quantity.symbol.code(); quantity 变量中提取代币符号。

    stats statstable(get_self(), sym.raw()); 创建一个用于存储代币统计信息的表 statstable

    const auto& st = statstable.get(sym.raw()); 获取代币的统计信息。 如果代币不存在,会抛出异常。

    require_recipient(from); 通知发送方账户,已发生转账事件。

    require_recipient(to); 通知接收方账户,已发生转账事件。

    check(quantity.is_amount_within_range(), "quantity magnitude is too large"); 检查转账数量是否在允许的范围内。

    check(quantity.symbol == st.supply.symbol, "symbol precision mismatch"); 验证转账的代币符号与已创建的代币符号是否一致。

    check(memo.size() <= 256, "memo has more than 256 bytes"); 检查memo的大小是否超过256字节的限制。

    sub_balance(from, quantity); 调用 sub_balance 函数,从发送方账户扣除代币。

    add_balance(to, quantity, from); 调用 add_balance 函数,将代币添加到接收方账户。 from 作为 ram_payer ,表示发送方为接收方支付RAM费用(如果接收方是首次接收此代币)。

    }

    void mytoken::sub_balance(const name& owner, const asset& value)

    此函数用于从指定账户扣除代币余额。

    accounts 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, get_self(), [&](auto& a) {
        a.balance -= value;
    });
    

    accounts from_acnts(get_self(), owner.value); 创建一个用于存储账户余额的表 from_acnts 。 表的作用域是账户所有者,主键是代币符号的哈希值。

    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, get_self(), [&](auto& a) { a.balance -= value; }); 修改 from_acnts 表中的记录,减少账户余额。 get_self() 作为 ram_payer ,表示合约本身支付RAM费用。

    void mytoken::add_balance(const name& owner, const asset& value, const name& ram_payer)

    此函数用于向指定账户增加代币余额。

    accounts 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, ram_payer, [&](auto& a) {
            a.balance += value;
        });
    }
    

    accounts to_acnts(get_self(), owner.value); 创建一个用于存储账户余额的表 to_acnts 。 表的作用域是账户所有者,主键是代币符号的哈希值。

    auto to = to_acnts.find(value.symbol.code().raw()); 尝试在 to_acnts 表中查找账户的代币余额记录。

    if (to == to_acnts.end()) { ... } else { ... } 检查是否找到了现有的余额记录。 如果没有找到,则说明该账户是首次接收此代币,需要创建一个新的记录。 如果找到了,则直接修改现有记录,增加余额。

    to_acnts.emplace(ram_payer, [&](auto& a) { a.balance = value; }); 如果账户是首次接收此代币,则在 to_acnts 表中创建一个新的记录。 ram_payer 指定支付RAM费用的账户。 Lambda表达式用于初始化新记录的 balance 字段。

    to_acnts.modify(to, ram_payer, [&](auto& a) { a.balance += value; }); 如果账户已经存在余额记录,则修改 to_acnts 表中的记录,增加账户余额。 ram_payer 指定支付RAM费用的账户。

    ACTION mytoken::retire(const asset& quantity, const string& memo)

    此Action用于销毁代币,从流通量中移除。 只有代币的发行者才能调用此Action。 quantity 参数定义了要销毁的代币数量。 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");
    const auto& st = *existing;
    
    require_auth(st.issuer);
    check(quantity.is_amount_within_range(), "quantity magnitude is too large");
    check(quantity.symbol == st.supply.symbol, "symbol precision mismatch");
    
    statstable.modify(st, get_self(), [&](auto& s) {
        s.supply -= quantity;
    });
    
    sub_balance(st.issuer, quantity);
    

    auto sym = quantity.symbol; quantity 变量中提取代币符号。

    check(sym.is_valid(), "invalid symbol name"); 验证代币符号的有效性。

    check(memo.size() <= 256, "memo has more than 256 bytes"); 检查memo的大小是否超过256字节的限制。

    stats statstable(get_self(), sym.code().raw()); 创建一个用于存储代币统计信息的表 statstable

    auto existing = statstable.find(sym.code().raw()); 尝试在 statstable 表中查找具有相同符号的代币。

    check(existing != statstable.end(), "token with symbol does not exist"); 确保该符号的代币已经存在。

    const auto& st = *existing; 获取代币的统计信息。

    require_auth(st.issuer); 验证调用此Action的账户是否为代币的发行者。

    check(quantity.is_amount_within_range(), "quantity magnitude is too large"); 检查要销毁的代币数量是否在允许的范围内。

    check(quantity.symbol == st.supply.symbol, "symbol precision mismatch"); 验证要销毁的代币符号与已创建的代币符号是否一致。

    statstable.modify(st, get_self(), [&](auto& s) { s.supply -= quantity; }); 修改 statstable 表中的记录,减少代币的发行量。 get_self() 作为 ram_payer ,表示合约本身支付RAM费用。

    sub_balance(st.issuer, quantity); 调用 sub_balance 函数,从发行者账户扣除代币。 代币从发行者账户销毁。

    }

    EOSIO_DISPATCH(mytoken, (create)(issue)(transfer)(retire))

    此宏用于将Action名称映射到对应的处理函数。 它将 create , issue , transfer retire Actions分别映射到 mytoken::create , mytoken::issue , mytoken::transfer mytoken::retire 函数。

    • mytoken.abi :
    • ABI文件定义了合约的接口,包括数据结构、Action和Table的定义。它用于客户端应用程序与合约进行交互。

      { "version": "eosio::abi/1.0", "types": [], "structs": [ { "name": "accounts", "base": "", "fields": [ { "name": "balance", "type": "asset" } ] }, { "name": "create", "base": "", "fields": [ { "name": "issuer", "type": "name" }, { "name": "maximum_supply", "type": "asset" } ] }, { "name": "issue", "base": "", "fields": [ { "name": "to", "type": "name" }, { "name": "quantity", "type": "asset" }, { "name": "memo", "type": "string" } ] }, { "name": "transfer", "base": "", "fields": [ { "name": "from", "type": "name" }, { "name": "to", "type": "name" }, { "name": "quantity", "type": "asset" }, { "name": "memo", "type": "string" } ] }, { "name": "retire", "base": "", "fields": [ { "name": "quantity", "type": "asset" }, { "name": "memo", "type": "string" } ] } ], "actions": [ { "name": "create", "type": "create", "ricardian_contract": "" }, { "name": "issue", "type": "issue", "ricardian_contract": "" }, { "name": "transfer", "type": "transfer", "ricardian_contract": "" }, { "name": "retire", "type": "retire", "ricardian_contract": "" } ], "tables": [ { "name": "accounts", "index_type": "i64", "key_names": [ "balance" ], "type": "accounts" } ], "ricardian_clauses": [], "error_messages": [], "abi_extensions": [] }

  • 编译合约: 使用 eosio-cpp 命令编译合约。
  • bash eosio-cpp -abigen mytoken.cpp -o mytoken.wasm

    这会生成 mytoken.wasm (WebAssembly代码)和 mytoken.abi 文件。

    三、部署合约

    1. 部署合约到链上: 部署智能合约到EOS区块链是项目启动的关键步骤。此过程涉及使用 cleos 命令行工具,该工具是EOSIO工具链的核心组成部分。

      执行以下 cleos set contract 命令来完成部署:

      cleos set contract <你的EOS账户名> ./mytoken ./mytoken.wasm ./mytoken.abi -p <你的EOS账户名>@active

      务必将 <你的EOS账户名> 替换为你拥有的、用于部署合约的实际EOS账户名。 ./mytoken 代表合约名称, ./mytoken.wasm 是编译后的WebAssembly代码,而 ./mytoken.abi 是应用程序二进制接口文件,定义了合约的接口。

      -p <你的EOS账户名>@active 指定了授权账户和权限级别,通常使用active权限。此步骤需要消耗账户一定的资源(如CPU、RAM、NET),确保账户有足够的资源可供使用。部署成功后,合约代码和ABI文件将被上传到区块链,合约账户就可以开始执行合约定义的动作。

    2. 创建代币: 成功部署合约后,下一步是创建代币。这通过调用合约中的 create Action来实现,该Action定义了代币的初始属性,例如总供应量和符号。

      使用以下 cleos push action 命令来创建代币:

      cleos push action <你的EOS账户名> create '[ "<你的EOS账户名>", "1000000.0000 SYS"]' -p <你的EOS账户名>@active

      • <你的EOS账户名> :再次强调,替换为你的EOS账户名,它将成为此代币的发行者,拥有控制权。
      • 1000000.0000 SYS :定义了代币的最大发行量,即代币的总供应量。 SYS 是代币符号,你可以根据项目需求自定义,例如 TOKEN 或任何其他合规的符号。 .0000 指定精度,表示小数点后保留四位。精度影响了代币的可分割性。 修改最大发行量会影响EOS账户所需的RAM资源,请合理设置。

      此操作会在链上创建一个新的代币类型,定义了其基本属性。创建完成后,才能进行后续的发行和转移操作。

    3. 发行代币: 创建代币后,需要实际发行代币到特定的账户。通过调用合约中的 issue Action完成此操作。

      使用以下 cleos push action 命令来发行代币:

      cleos push action <你的EOS账户名> issue '[ "<你的EOS账户名>", "100.0000 SYS", "memo"]' -p <你的EOS账户名>@active

      • <你的EOS账户名> :指定接收代币的账户。通常,首次发行会将代币发送到发行者自己的账户。
      • 100.0000 SYS :指定要发行的代币数量。这部分代币将从合约的总供应量中转移到接收账户。
      • memo :这是一个可选的备注信息,可以包含任何相关的说明或交易详情。例如,可以记录发行原因或目的。

      发行代币会从代币的总供应量中减去指定数量,并将其添加到接收账户的余额中。发行者需要有足够的权限才能执行此操作。

    4. 转移代币: 代币发行后,可以在不同账户之间转移。通过调用合约中的 transfer Action实现。

      使用以下 cleos push action 命令来转移代币:

      cleos push action <你的EOS账户名> transfer '[ "<你的EOS账户名>", "<接收代币的账户名>", "10.0000 SYS", "memo"]' -p <你的EOS账户名>@active

      • <你的EOS账户名> :指定发送代币的账户。此账户必须拥有足够的代币余额才能完成转移。
      • <接收代币的账户名> :指定接收代币的账户。
      • 10.0000 SYS :指定要转移的代币数量。
      • memo :同样是可选的备注信息,可以包含交易的说明。

      转移代币会从发送账户的余额中减去指定数量,并将其添加到接收账户的余额中。发送和接收账户都需要存在于区块链上。

    四、常见问题及注意事项

    1. 账户权限: 确保执行合约的账户具备足够的权限。例如,发行新的代币需要发行者的权限,转移代币需要持有者的授权。权限不足会导致交易失败。 可以使用EOS权限管理系统,例如使用`active`权限或者自定义权限进行操作。
    2. 精度(Precision): 代币的精度至关重要,它决定了代币可以被分割的最小单位。精度越高,代币的可分割性越好,但也会增加存储和计算的负担。务必在创建代币时仔细考虑,并根据代币的使用场景选择合适的精度。 常见的精度包括0、2、4、6、8等。精度为0表示代币只能是整数,精度为2表示代币可以精确到小数点后两位。
    3. RAM消耗: 在EOS区块链上,部署智能合约和存储数据需要消耗RAM资源。每个账户的RAM资源是有限的。确保你的账户有足够的RAM,否则可能会导致交易失败。 在部署合约之前,可以使用`cleos get account `命令查看账户的RAM使用情况。 可以通过购买RAM或者优化合约代码来减少RAM的消耗。
    4. 错误处理: 智能合约的错误处理至关重要。编写健壮的合约代码,要充分考虑各种可能出现的错误情况,并进行适当的处理,以防止恶意攻击和意外情况。 使用`eosio::check`、`eosio::require_auth`等函数进行错误检查。 对于可能失败的操作,添加相应的错误处理逻辑,例如回滚交易或者记录错误日志。
    5. 安全性: EOS智能合约的安全性至关重要。由于智能合约一旦部署就无法更改,因此必须在部署之前进行彻底的代码审计和测试,确保合约不存在漏洞,如重入攻击、整数溢出等。 强烈建议使用专业的智能合约审计服务,由安全专家对代码进行全面的审查。 还可以使用形式化验证等技术来提高合约的安全性。
    6. Symbol的选择: Symbol是代币的唯一标识符,用于在区块链上区分不同的代币。建议选择一个独一无二且易于识别的Symbol,避免与其他已存在的代币发生冲突,导致混淆或交易错误。 Symbol的长度通常为3-7个大写字母。 在选择Symbol之前,可以在EOS区块链浏览器上搜索,确认该Symbol是否已被使用。

    五、进阶应用

    在掌握了基本代币创建后,为了更深入地应用EOS代币,你可以进一步开发代币合约,添加更多高级功能,例如:

    • 代币增发: 精细化管理代币的供应量。你可以通过设置合约参数,选择限制或开放代币增发功能。 限制增发可以确保代币的稀缺性,防止通货膨胀;而开放增发则允许在特定条件下(例如,通过社区投票或特定事件触发)增加代币供应,以满足项目发展需求。
    • 投票治理: 利用代币持有者的力量,实现去中心化社区治理。代币持有者可以使用其持有的代币数量作为投票权重的依据,参与对提案的投票,例如协议升级、参数调整、社区资金使用等。这种方式能够提升社区的参与度和透明度,让代币持有者共同决定项目的未来发展方向。
    • 抵押挖矿: 设计激励机制,鼓励用户长期持有和使用代币。允许用户将代币抵押到特定的智能合约中,参与挖矿活动,并根据抵押的时间和数量获得相应的奖励,例如更多的代币、NFT或者其他权益。这种方式可以有效提升代币的价值和流动性,吸引更多用户参与生态建设。
    • NFT集成: 将代币与非同质化代币(NFT)相结合,创造独特的数字资产应用。可以将代币作为NFT的支付手段、价值支撑或治理凭证。例如,用户可以使用代币购买或交易NFT,或者通过持有特定数量的代币来解锁NFT的特殊功能或权益。 这种结合可以极大地丰富数字资产的应用场景,为用户提供更多创新体验。
    • DeFi应用集成: 将代币无缝集成到去中心化金融(DeFi)协议中,扩展其应用场景。例如,可以将代币作为抵押品参与借贷,用于在去中心化交易所(DEX)中进行交易,或者参与流动性挖矿获得收益。这种集成可以提升代币的流动性和实用性,使其成为DeFi生态系统中不可或缺的一部分。