请选择 进入手机版 | 继续访问电脑版
 找回密码
 立即注册
本帖最后由 爱微凉 于 2018-12-3 15:14 编辑

  • TRON 概况

    TRON,2017年九月份, 做了千万美元级别的ICO,2018年六月tron主网上线。据官方数据,截至目前,主网上已经有69万个账户注册,每天新增账户量超过2万个。同时TRON每秒支持千量级tps,ETH现在主网还在十几tps量级。不可否认eth生态聚集了很多有天赋的开发者和researcher,但eth的PoW共识机制决定了其短时间内无法解决交易手续费高,交易确认速度,和并发交易的限制。目前从合约和交易体验上来讲,TRON相比ETH有比较大的优势。同时,从用户进入门槛和合约开发门槛上,由于TRON新用户几乎不需要成本(对比EOS复杂的邀请机制),和其支持solidtiy的TVM(相比EOS采用C++进行合约开发),TRON相比EOS也有显著优势。

    对于dapp开发者,TRON一定是现阶段非常值得关注的一条公链,建立在其性能和生态的基础上, 已经出现了很多用户量和交易量都很大的dapp,爆品dapp也是完全有足够的机会诞生在这条公链之上。不过现在TRON公链生态中,对于开发者的引导性文章很少,高质量的也几乎没有看到,这也是我们写作这篇入门教程的初衷。下一章我们将正式开始介绍基于TRON的dapp开发基本概念,和一个样例来帮助大家快速入门。

    TRON 开发工具链

    TRON 提供了完整的工具链为开发者提供合约开发,测试,部署,接口。尤其是对于熟悉以太坊智能合约的开发者,从语言到工具一定都非常熟悉。



    • tron-web:JavaScript接口,用于提供常用的账户,地址,转账,合约相关操作。相当于Ethereum的web3js。
    • tron-box:提供合约编译,部署,测试的命令行工具。作用相当于Ethereum的truffle工具链。
    • tronLinktronPay: 提供浏览器环境的钱包插件,为dapp提供便利和安全的执行环境,相当于Ethereum的MetaMask,Scatter。
    • tron-grid:社区维护的主网和测试网HTTP API接口,相当于Ethereum社区中的Infura。
    • tron-studio: TRON集成开发环境,相当于以太坊中的Remix,不过现在功能和稳定性都有待提高,不推荐使用。

    全流程Tutorial

    本章节我们通过一个Token合约,来讲解基本的合约开发和dapp开发流程。本例中的所有代码都可以在该Repo中找到。

    环境搭建

    如果希望自己搭建一条本地私链进行调试。根据官方文档,TRON的私链可以通过官方提供的docker镜像来部署,以下为部署的指令:

    1. docker run -it \
    2. -p 8091:8091 -p 8092:8092 -p 8090:8090 -p 50051:50051 -p 50052:50052 \
    3. --rm --name tron trontools/quickstart:latest
    复制代码

    运行结果如下,可以看到,本地已经生成了10个可用用来测试的TRX的账户地址。

    认真的读者可能已经发现了,我们在启动镜像的时候,一共要设置5个端口,这相比以太坊和比特币的RPC接口多了很多。这其中设计到TRON本身节点的一个区分,作为开发者我们有必要理解。TRON network中,一共有4种节点分别为:

    • Witness:作为超级节点,负责收集网络中的交易,负责出块,不为客户端暴露API。
    • FullNode:Full Node广播交易和区块,同时为提供的API进行账户,交易和区块的查询和操作。在本例中,端口8090为其HTTP接口,端口50051为其GRPC接口。
    • SolidityNode:SolidityNode只负责从FullNodo中拉取区块数据,不会主动发送数据,其区块数据落后于FullNode几个区块,故其暴露的API适合已经确认的交易。客户端需要同时和FullNode和SolidityNode想链接,以获得不同的API功能。其具体API列表在这里。在本例中,端口8091为其HTTP接口,端口50052为其GRPC接口。
    • EventSever: 在TRON的合约中,像Ehereum一样支持事件,不同的是,TRON中的事件是从EventServer中来监听。在本例中,端口8092为其HTTP接口。

    同时,社区提供了Shasta测试网,用户也可以方便的输入自己地址来获取测试代币,同时提供测试网的区块链浏览器,非常方便开发调试,由于TRON测试网速度比以太坊的快非常多,我们在开发测试中,本教程直接使用测试网进行调试和部署,以简化读者的操作和学习路径。

    合约编写

    TRON中的合约采用Solidity来编写,故以太坊开发者在合约开发层,可以零学习成本迁移至TRON网络。下面我们编写一个标准的ERC20的代币Transfer合约,并将其部署至TRON。

    1. pragma solidity ^0.4.18;


    2. contract Token {

    3.     uint256 constant private MAX_UINT256 = 2**256 - 1;
    4.     mapping (address => uint256) public balances;
    5.     mapping (address => mapping (address => uint256)) public allowed;
    6.     /*
    7.     NOTE:
    8.     The following variables are OPTIONAL vanities. One does not have to include them.
    9.     They allow one to customise the token contract & in no way influences the core functionality.
    10.     Some wallets/interfaces might not even bother to look at this information.
    11.     */
    12.     string public name;                   //fancy name: eg Simon Bucks
    13.     uint8 public decimals;                //How many decimals to show.
    14.     string public symbol;                 //An identifier: eg SBX

    15.     function Token(
    16.         uint256 _initialAmount,
    17.         string _tokenName,
    18.         uint8 _decimalUnits,
    19.         string _tokenSymbol
    20.     ) public {
    21.         balances[msg.sender] = _initialAmount;               // Give the creator all initial tokens
    22.         totalSupply = _initialAmount;                        // Update total supply
    23.         name = _tokenName;                                   // Set the name for display purposes
    24.         decimals = _decimalUnits;                            // Amount of decimals for display purposes
    25.         symbol = _tokenSymbol;                               // Set the symbol for display purposes
    26.     }

    27.     function transfer(address _to, uint256 _value) public returns (bool success) {
    28.         require(balances[msg.sender] >= _value);
    29.         balances[msg.sender] -= _value;
    30.         balances[_to] += _value;
    31.         emit Transfer(msg.sender, _to, _value); //solhint-disable-line indent, no-unused-vars
    32.         return true;
    33.     }

    34.     function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
    35.         uint256 allowance = allowed[_from][msg.sender];
    36.         require(balances[_from] >= _value && allowance >= _value);
    37.         balances[_to] += _value;
    38.         balances[_from] -= _value;
    39.         if (allowance < MAX_UINT256) {
    40.             allowed[_from][msg.sender] -= _value;
    41.         }
    42.         emit Transfer(_from, _to, _value); //solhint-disable-line indent, no-unused-vars
    43.         return true;
    44.     }

    45.     function balanceOf(address _owner) public view returns (uint256 balance) {
    46.         return balances[_owner];
    47.     }

    48.     function approve(address _spender, uint256 _value) public returns (bool success) {
    49.         allowed[msg.sender][_spender] = _value;
    50.         emit Approval(msg.sender, _spender, _value); //solhint-disable-line indent, no-unused-vars
    51.         return true;
    52.     }

    53.     function allowance(address _owner, address _spender) public view returns (uint256 remaining) {
    54.         return allowed[_owner][_spender];
    55.     }
    56. }
    复制代码

    合约部署

    合约代码完成后, 我们新建一个目录,利用tronbox来初始化一个基础的dapp工程。

    1. npm install -g tronbox
    2. tronbox init
    3. ls
    复制代码

    我们可以看到,路径下已经生成了合约样板工程。

    为了部署合约,我们首先下载一个tronPay插件来创建我们的TRON账户地址(要求Chrome浏览器),之后导出私钥。

    打开路径下的tronbox.js,粘贴将私钥替换为自己的私钥, 其余的参数如上对不同节点功能的描述,我们需要分别配置节点API路径:

    1. module.exports = {
    2.   networks: {
    3.     shasta: {
    4.       privateKey: '你的私钥',
    5.       consume_user_resource_percent: 30,
    6.       fee_limit: 100000000,
    7.       fullNode: "https://api.shasta.trongrid.io",
    8.       solidityNode: "https://api.shasta.trongrid.io",
    9.       eventServer: "https://api.shasta.trongrid.io",
    10.       network_id: "*"
    11.     },
    12.    
    13.   }
    14. };
    复制代码

    最后一步,我们在migrations文件夹中,创建名为2_deploy_contracts.js的部署脚本,其作用与truffle中的 migration scripts完全相同。

    1. <pre style="box-sizing: border-box; word-wrap: normal; border-radius: 3px; line-height: 1.45; overflow: auto; padding: 16px; word-break: normal; font-variant-numeric: normal; font-variant-east-asian: normal;"><font face="微软雅黑"><span class="pl-k" style="box-sizing: border-box;">var</span> token <span class="pl-k" style="box-sizing: border-box;">=</span> <span class="pl-smi" style="box-sizing: border-box;">artifacts</span>.<span class="pl-en" style="box-sizing: border-box;">require</span>(<span class="pl-s" style="box-sizing: border-box;"><span class="pl-pds" style="box-sizing: border-box;">"</span>./Token.sol<span class="pl-pds" style="box-sizing: border-box;">"</span></span>);

    2. <span class="pl-c1" style="box-sizing: border-box;">module</span>.<span class="pl-en" style="box-sizing: border-box;">exports</span> <span class="pl-k" style="box-sizing: border-box;">=</span> <span class="pl-k" style="box-sizing: border-box;">function</span>(<span class="pl-smi" style="box-sizing: border-box;">deployer</span>) {
    3.   <span class="pl-smi" style="box-sizing: border-box;">deployer</span>.<span class="pl-en" style="box-sizing: border-box;">deploy</span>(token, <span class="pl-c1" style="box-sizing: border-box;">1000000</span>, <span class="pl-s" style="box-sizing: border-box;"><span class="pl-pds" style="box-sizing: border-box;">"</span>GUIDE<span class="pl-pds" style="box-sizing: border-box;">"</span></span>, <span class="pl-c1" style="box-sizing: border-box;">6</span>, <span class="pl-s" style="box-sizing: border-box;"><span class="pl-pds" style="box-sizing: border-box;">"</span>GD<span class="pl-pds" style="box-sizing: border-box;">"</span></span>);
    4. };</font></pre>
    复制代码

    现在,我们可以完成合约部署。

    1. tronbox migrate --network shasta
    复制代码

    至此,合约已经可以在区块浏览器中找到。下面的章节我们讲解如何卡发dapp与合约进行交互。

    集成TronLink,TronPay
    1. import React from 'react';
    2. import ReactDOM from 'react-dom';
    3. import './index.css';
    4. import App from './App';

    5. var waitForGlobal = async () =>{
    6.     // 1. check variable, 检查tronweb是否已经加载
    7.     if (window.tronWeb) {
    8.         let tronWeb = window.tronWeb;
    9.         // 2. check node connection,检查所需要的API是否都可以连通
    10.         const nodes = await tronWeb.isConnected();
    11.         const connected = !Object.entries(nodes).map(([name, connected]) => {
    12.             if (!connected) {
    13.                 console.error(`Error: ${name} is not connected`);
    14.             }
    15.             return connected;
    16.         }).includes(false);
    17.         if (connected){
    18.             // 3. 如果一切正常,启动react应用。
    19.             ReactDOM.render(<App />, document.getElementById('root'));
    20.         } else {
    21.             console.error(`Error: TRON node is not connected`);
    22.             console.error('wait for tronLink');
    23.             setTimeout(async () => {
    24.                 await waitForGlobal();
    25.             }, 100);
    26.         }

    27.     } else {
    28.         // 如果检测到没有注入tronWeb对象,则等待100ms后重新检测
    29.         console.error('wait for tronLink');
    30.         setTimeout(async () => {
    31.             await waitForGlobal();
    32.         }, 100);
    33.     }
    34. };

    35. waitForGlobal().then();
    复制代码

    合约和交易

    合约调用在tronWeb中略有不同,下面我们分几个场景来分别说明:

    • 查询当前用户trx余额:

    1. let tronWeb = window.tronWeb;
    2.         this.state.address && (this.setState({balance : await tronWeb.trx.getBalance(this.address)}));
    复制代码

  • 发起交易,转账trx
    1. let tronWeb = window.tronWeb;
    2.         const sendTransaction = await tronWeb.trx.sendTransaction("TKPzfsXRaDmdKh2GuouXw2eyK2HNH9FNQS", 1000);
    复制代码

  • 构造合约并查询当前的token余额
          
    1. import * as artifact from './contracts/Token'
    2.         let tronWeb = window.tronWeb;
    3.         let address = tronWeb.address.fromHex(artifact.networks['*'].address);
    4.         this.contract = tronWeb.contract(artifact.abi, address);
    5.         this.state.address &&  this.contract.balances(this.state.address).call().then(output => {
    6.             console.group('Contract "call" result');
    7.             console.log('- Output:', output, '\n');
    8.             this.setState({tokenBalance: output.toString()});
    9.             console.groupEnd();
    10.         });
    复制代码

  • 对token进行转账
            
    1.         // 2. send token
    2.          let tx = this.contract.transfer("TKPzfsXRaDmdKh2GuouXw2eyK2HNH9FNQS", 100).send().then(output => {
    3.             console.group('Contract "getLast" result');
    4.             console.log('- Output:', output, '\n');
    5.             console.groupEnd();
    6.         });
    复制代码

  • 监听链上Transfer事件
            
    1. this.contract && this.contract.Transfer().watch((err, event) => {
    2.             if(err)
    3.                 return console.error('Error with "Message" event:', err);

    4.             console.group('New event received');
    5.             console.log('- Contract Address:', event.contract);
    6.             console.log('- Event Name:', event.name);
    7.             console.log('- Transaction:', event.transaction);
    8.             console.log('- Block number:', event.block);
    9.             console.log('- Result:', event.result, '\n');
    10.             console.groupEnd();
    11.         });
    复制代码

    以上几个case包含了所有主要的dapp与智能合约的交互逻辑,在实际使用中,基本也可以涵盖大部分场景,更多的用法由于TronWeb文档的暂时不完善,读者可以尝试看一点源码,并不是非常复杂,更多的问题也期待与作者交流讨论。

    总结

    以上通过讲解一个Token合约的dapp开发全流程,我们讲解了合约编写,合约部署,dapp集成,dapp与合约交互,本文章所有代码均开源,欢迎读者star。并在每一个章节突出了从开发者角度需要关注的重点,并与以太坊开发进行了类比和关联。总体上来说,如果dapp开发者熟悉以太坊生态的开发,迁移至TRON生态的学习成本和现有代码的迁移成本都非常低。欢迎大家拥抱TRON生态,也请读者期待下一期教程《TRON开发者入门2:带宽,能量为什么对Dapp开发者如此重要?》。

    最后推荐一个技术社区,在波场开发社区中他们做的非常不错。感谢阅读。

    参考

    本例中的所有参考链接已经附加在文档中,推荐两个官方文档:



分享至 : QQ空间
收藏

0 个回复