跳到主要内容

入门指南

入门手册#

简介#

本教程主要是指导用户在Alaya上使用solidity语言创建简单的HelloWorld智能合约,通过alaya-truffle编译,部署,调用此合约。如果您想使用更加丰富的API可以参考Java SDK开发指南 或者 JS SDK开发指南

alaya-truffle开发工具介绍#

alaya-truffle是PlatON提供的一款能够在本地编译、部署、调用智能合约的工具,具体的安装及使用手册参见

创建HelloWorld合约#

pragma solidity ^0.5.17;
contract HelloWorld {        string name;        function setName(string memory _name) public returns(string memory){        name = _name;        return name;    }        function getName() public view returns(string memory){        return name;    }}

合约文件说明

  • pragma solidity ^0.5.17
    • pragma solidity:是solidity版本声明 0.5.17:代表solidity版本 ^ :表示向上兼容,即可以用0.5.17以上版本编译器进行编译
  • contract HelloWorld
    • contract:合约声明的关键字 HelloWorld:当前合约的名称
  • string name
    • name:合约的状态变量 string:指明此状态变量的类型
  • function setName(string memory _name) public returns(string memory)
    • function:合约中函数声明关键字 setName:此函数的名称 memory:声明_name参数的存储位置(字符串类型的函数输入参数与输出参数必须声明为memory) _name:为此函数的局部变量 public:声明此函数的可见性 name = _name:此操作将外部传进来的局部变量赋值给状态变量
  • function getName() public view returns(string memory)
    • view:如果一个函数带有view关键字,此函数将不会改变合约中状态变量的值(主要用于查询)

编译HelloWorld合约#

step1. 为HelloWorld项目创建新目录

mkdir HelloWorld && cd HelloWorld
  • 以下命令如果没有特殊说明都在HelloWorld目录下进行

step2. 使用alaya-truffle初始化一个工程

alaya-truffle init

在操作完成之后,就有如下项目结构:

  • contracts/: Solidity合约目录

  • migrations/: 部署脚本文件目录

  • test/: 测试脚本目录

  • truffle-config.js: alaya-truffle 配置文件

step3. 将之前编写好的HelloWorld合约放至HelloWorld/contracts/目录下

ls contracts/
  • 将看到HelloWorld.sol

step4. 修改alaya-truffle 配置文件truffle-config.js,将编译器版本修改对应的solidity合约中的版本号

vim truffle-config.js

truffle-config.js 修改部分内容如下:

compilers: {      solc: {            version: "^0.5.17",    // 此版本号与HelloWorld.sol中声明的版本号保持一致      }}

step5. 编译合约

alaya-truffle compile

在操作完成之后,生成如下目录结构:

  • build/: Solidity合约编译后的目录

  • build/contracts/HelloWorld.json HelloWorld.sol对应的编译文件

部署HelloWorld合约#

step1. 新增合约部署脚本文件

cd migrations/ && touch 2_initial_helloworld.js

部署脚本文件名建议使用合约名称便于后面维护,如HelloWorld合约对应的部署脚本文件为2_initial_helloworld.js,内容如下所示:

const helloWorld = artifacts.require("HelloWorld"); //artifacts.require告诉alaya-truffle需要部署哪个合约,HelloWorld即之前写的合约类名    module.exports = function(deployer) {       deployer.deploy(helloWorld); //helloWorld即之前定义的合约抽象(部署带参数的合约失败,请参考FAQ部署带参数合约失败说明)};

step2. 修改truffle-config.js中链的配置信息

vim truffle-config.js

将truffle-config.js中的区块链相关配置修改成您真实连接的链配置

networks: {    development: {       host: "10.1.1.6",     // 区块链所在服务器主机       port: 8806,            // 链端口号       network_id: "*",       // Any network (default: none)       from: "atp1jtfqqqr6436ppj6ccnrh8xjg7qals3ctnnmurp", //部署合约账号的钱包地址       gas: 999999,       gasPrice: 50000000004,    },}

step3. 解锁钱包账户

进入alaya-truffle控制台

alaya-truffle console

导入私钥(如果之前已导入可以跳过此步骤)

web3.platon.personal.importRawKey("您的钱包私钥","您的钱包密码");

导入成功将看到私钥对应的地址:

'atp1jtfqqqr6436ppj6ccnrh8xjg7qals3ctnnmurp'

解锁钱包账户

 web3.platon.personal.unlockAccount('您的钱包地址','您的钱包密码',999999);

解锁成功将看到如下信息:

ture

step4. 部署合约

alaya-truffle migrate

部署成功后,将看到类似如下信息:

2_initial_helloworld.js======================
   Deploying 'HelloWorld'   ----------------------   > transaction hash:    0x87cd48cc467f9bc943fd096c57c8a7e7b7fa941380538d9e59797800c6c4292c   > Blocks: 0            Seconds: 0   > contract address:    atp1c5xxup4au4pqkgkm6a3p6hj3x0vvekdj52z2la   > block number:        282520   > block timestamp:     1585535169200   > account:             atp1jtfqqqr6436ppj6ccnrh8xjg7qals3ctnnmurp   > balance:             16447231233352977496646259638377769924557918764752765436645.336513079692286014   > gas used:            145569   > gas price:           1 gvon   > value sent:          0 ATP   > total cost:          0.000145569 ATP

   > Saving migration to chain.   > Saving artifacts   -------------------------------------   > Total cost:          0.000145569 ATP
Summary=======> Total deployments:   2> Final cost:          0.000259892 ATP

调用HelloWorld合约#

step1. 进入alaya-truffle控制台

alaya-truffle console
  • 以下调用查询将在truffle控制台中进行

step2. 构建合约对象

var abi = [{"constant":false,"inputs":[{"internalType":"string","name":"_name","type":"string"}],"name":"setName","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getName","outputs":[{"internalType":"string","name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}]; //可以从HelloWorld/build/contracts/HelloWorld.json文件中获取到
var contractAddr = 'atp1c5xxup4au4pqkgkm6a3p6hj3x0vvekdj52z2la';//部署合约时的获取的地址var helloWorld = new web3.platon.Contract(abi,contractAddr); 

说明:

  • abi 是合约提供给外部调用时的接口,每个合约对应的abi在编译后的文件中,如:HelloWorld/build/contracts/HelloWorld.json 中可以找到
  • contractAddr 在部署合约成功后有一个contract address
  • helloWorld 就是构建出来与链上合约交互的合约对象抽象

step3. 调用合约

helloWorld.methods.setName("hello world").send({from: 'atp1jtfqqqr6436ppj6ccnrh8xjg7qals3ctnnmurp'}).on('receipt', function(receipt) {console.log(receipt);}).on('error', console.error); 

调用合约命令说明:

  • helloWorld 是之前构建的合约对象
  • methods 固定语法,指量后面紧跟合约的方法名
  • setName 是我们HelloWorld合约中的一个方法,有一个String类型的入参,此处入参为hello world
  • from 调用者的钱包地址
  • on 是监听合约处理结果事件,此处如果成功将打印回执,失败输出错误日志

函数调用成功,将会看到如下信息:

{ blockHash:  '0xe592a4f203ed058df7515205717f167848b1a56b8bb143f9eba512facae22aa1',  blockNumber: 283911,  contractAddress: null,  cumulativeGasUsed: 44820,  from: 'atp1jtfqqqr6436ppj6ccnrh8xjg7qals3ctnnmurp',  gasUsed: 44820,  logsBloom:'0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',  status: true,  to: 'atp1c5xxup4au4pqkgkm6a3p6hj3x0vvekdj52z2la',//交易调用的合约地址  transactionHash:   '0x2b381a8efab4774ae029fdf2e2585b48c03c033c64d543c9c606c925689fca31',//交易hash  transactionIndex: 0,  events: {} }

step4. 合约查询

helloWorld.methods.getName().call(null,function(error,result){console.log("name is:" + result);})  

查询合约命令说明:

  • helloWorld 是之前构建的合约对象
  • methods 指定将获取合约中的方法
  • getName 是我们HelloWorld合约中的一个方法,该方法没有入参,故入参为空
  • call 指明是查询方法
  • function 是一个回调函数,将处理调用后的结果,此处我们通过console.log打印出执行结果

众筹合约#

简介#

​ 在下面的例子中,我们将使用合约进行一次众筹。合约创建者发起众筹,并初始化众筹的代币数量及众筹持续的时间。如果在指定时间内众筹完成则此次众筹成功。并关闭众筹开关,根据一个固定兑换比率得到的一定数量的token会被铸造出来,并且会被计入在买方名下。否则众筹失败,把众筹的金额返还给投资者。

在合约中设置了两个角色

  • 众筹者
  • 投资者

众筹的流程#

  • 1.创建众筹合约指受益人
  • 2.部署合约初始化众筹代币数量及持续时间
  • 3.投资者进行投资
  • 4.判断众筹是否结束
  • 如果众筹时间未到,众筹代币数量已完成,关闭众筹开关。给投资者按比例分配token。众筹成功
  • 如果众筹时间已到,众筹代币数量已完成,给投资者按比例分配token。众筹成功
  • 如果众筹时间已到,众筹代币数量未完成,返还投资者代币。众筹失败

众筹合约#

pragma solidity ^0.5.17;
contract CrowdFunding {    address payable public beneficiaryAddress = address(uint160(0)); //受益人地址,设置为合约创建者    uint256 public fundingGoal = 100 atp;  //众筹目标,单位是ATP    uint256 public amountRaised = 0; //已筹集金额数量, 单位是VON    uint256 public deadline; //截止时间    uint256 public price;  //代币价格    bool public fundingGoalReached = false;  //达成众筹目标    bool public crowdsaleClosed = false; //众筹关闭
    mapping(address => uint256) public balance; //保存众筹者对捐赈的金额        mapping(address => uint256) public tokenMap; //保存众筹者所拥有的代币数量
    //记录已接收的ATP通知    event GoalReached(address _beneficiaryAddress, uint _amountRaised);
    //转帐通知    event FundTransfer(address _backer, uint _amount, bool _isContribution);        //校验地址是否为空    modifier validAddress(address _address) {        require(_address != address(uint160(0)));        _;    }
    /**     * 初始化构造函数     *     * @param _fundingGoalInlats 众筹ATP币总量     * @param _durationInMinutes 众筹截止,单位是分钟     */    constructor (        uint _fundingGoalInlats,        uint _durationInMinutes    )public {        beneficiaryAddress = msg.sender;        fundingGoal = _fundingGoalInlats * 1 atp;        deadline = now + _durationInMinutes * 1 minutes;        price = 500 finney; //1个ATP币可以买 2 个代币    }

    /**     * 默认函数     *     * 默认函数,可以向合约直接打款     */    function () payable external {
        //判断是否关闭众筹        require(!crowdsaleClosed);        uint amount = msg.value;
        //捐款人的金额累加        balance[msg.sender] += amount;
        //捐款总额累加        amountRaised += amount;
        //转帐操作,转多少代币给捐款人        tokenMap[msg.sender]  += amount / price;                emit FundTransfer(msg.sender, amount, true);    }
    /**     * 判断是否已经过了众筹截止限期     */    modifier afterDeadline() { if (now >= deadline) _; }
    /**     * 检测众筹目标是否已经达到     */    function checkGoalReached() public afterDeadline payable{        if (amountRaised >= fundingGoal){            //达成众筹目标            fundingGoalReached = true;            emit GoalReached(beneficiaryAddress, amountRaised);        }
        //关闭众筹        crowdsaleClosed = true;    }

    /**     * 收回资金     *     * 检查是否达到了目标或时间限制,如果有,并且达到了资金目标,     * 将全部金额发送给受益人。如果没有达到目标,每个贡献者都可以退回     * 他们贡献的金额     */    function safeWithdrawal() public afterDeadline {
        //如果没有达成众筹目标        if (!fundingGoalReached) {            //获取合约调用者已捐款余额            uint amount = balance[msg.sender];
            if (amount > 0) {                //返回合约发起者所有余额                msg.sender.transfer(amount);                emit FundTransfer(msg.sender, amount, false);                balance[msg.sender] = 0;            }        }
        //如果达成众筹目标,并且合约调用者是受益人        if (fundingGoalReached && beneficiaryAddress == msg.sender) {
            //将所有捐款从合约中给受益人            beneficiaryAddress.transfer(amountRaised);                        emit FundTransfer(beneficiaryAddress, amountRaised, false);        }    }}

编译众筹合约:

step1. 为众筹合约创建新目录

mkdir myCrowdFunding && cd myCrowdFunding
  • 以下命令如果没有特殊说明都在myCrowdFunding目录下进行

step2. 使用alaya-truffle初始化一个工程

alaya-truffle init

在操作完成之后,就有这样的一个项目结构:

  • contracts/: Solidity合约目录
  • migrations/: 部署脚本文件目录
  • test/: 测试脚本目录
  • truffle-config.js: alaya-truffle 配置文件

step3. 将编写好的众筹合约放至myCrowdFunding/contracts/目录下

ls myCrowdFunding/contracts/
  • 将看到 CrowdFunding.sol

step4. 修改alaya-truffle 配置文件truffle-config.js,将编译器版本修改对应的solidity合约中的版本号

vim truffle-config.js

truffle-config.js 修改部分内容如下

compilers: {     solc: {        version: "0.5.17",    // 此版本号与CrowdFunding.sol中声明的版本号保持一致    }}

step5. 编译合约

alaya-truffle compile

在操作完成之后,就有这样的一个目录结构:

  • build/: Solidity合约编译后的目录
  • build/contracts/CrowdFunding.json CrowdFunding.sol对应的编译文件

部署众筹合约:

step1. 添加部署脚本文件

cd migrations/ && touch 2_initial_CrowdFunding.js

部署脚本文件为:2_initial_crowdFunding.js,内容如下所示:

const CrowdFunding = artifacts.require("CrowdFunding"); //需要部署的合约名称 module.exports = function(deployer) {      deployer.deploy(CrowdFunding,1000000,100);};

step2. 修改truffle-config.js中链的配置信息

vim truffle-config.js

将truffle-config.js中的区块链相关配置修改成您真实连接的链配置

networks: {    development: {       host: "10.1.1.6",     // 区块链所在服务器主机       port: 8806,            // 链端口号       network_id: "*",       // Any network (default: none)       from: "atp1jtfqqqr6436ppj6ccnrh8xjg7qals3ctnnmurp", //部署合约账号的钱包地址       gas: 999999,       gasPrice: 50000000004,    },}

step3. 部署合约

alaya-truffle migrate

部署成功将输出如下信息:

Compiling your contracts... Everything is up to date, there is nothing to compile. 3_initial_CrowdFunding.js     Deploying 'CrowdFunding'     transaction hash:    0x3a6419cd4169d7cfb430a1fc5632239ac4a01845827f20df9b3229a334c5488b     Blocks: 0            Seconds: 0     contract address:    atp1crcjuu9uwa9aukmf5dr5tq4ym6cv2kre0042ya //部署后的合约地址     block number:        280532     block timestamp:     1581751224032     account:             atp1jtfqqqr6436ppj6ccnrh8xjg7qals3ctnnmurp     balance:             90000000.806077629992489796     gas used:            379154     gas price:           50.000000004 gVON     value sent:          0 ATP     total cost:          0.018957700001516616 ATP      Saving migration to chain.     Saving artifacts     Total cost:     0.018957700001516616 ATP

众筹者查询众筹情况:

step1. 进入alaya-truffle控制台

alaya-truffle console
  • 以下调用查询将在truffle控制台中进行

step2. 构建的众筹合约对象

var abi = [...]; //众筹合约的ABI,从编译后的文件获取var contractAddr = 'atp1crcjuu9uwa9aukmf5dr5tq4ym6cv2kre0042ya'; //众筹合约地址var crowdFunding = new web3.platon.Contract(abi,contractAddr);  

step3. 查询已筹集金额

crowdFunding.methods.amountRaised().call(null,function(error,result){console.log("result:" + result);}); //查询已筹集金额

step4. 众筹者判断众筹是否成功

crowdFunding.methods.safeWithdrawal().send({from:'atp1jtfqqqr6436ppj6ccnrh8xjg7qals3ctnnmurp'}).on('data', function(event){ console.log(event);}).on('error', console.error); 

调用合约命令说明:

  • crowdFunding 是我们之前构建的合约对象
  • methods 固定语法,指定将获取合约中的方法
  • safeWithdrawal 是我们众筹合约中的一个方法,用于收回资金
  • from 调用者的钱包地址
  • on 是监听合约处理结果事件,失败输出错误日志

FAQ#

编译相关#

  1. alaya-truffle有哪些命令如何使用?

    alaya-truffle开发使用手册参考这里

  2. 合约为什么语法校验通不过?

    solidity合约0.4.x版本与0.5.x版本有重大变更,具体语法参考这里

  3. alaya-truffle执行alaya-truffle compile 失败?

    1.确认编译的合约文件中的版本号与truffle-config.js中指定的版本号是否一致。 2.可能语法有误,可以根据命令行提示修复后再进行编译。

  4. alaya-truffle执行alaya-truffle migrate部署合约失败?

1.确认truffle-config.js中连接的链的配置信息及用户的钱包地址是否正确。

  1. alaya-truffle migrate部署带参数的构造函数合约失败?

以合约A.sol为例,在migrations/2_initial_A.js文件中,确认是否添加构造参数信息如: A.sol构造函数格式如下:

constructor(uint256 a, string memory b, string memory c) public {}

2_initial_A.js文件配置如下:

 const A = artifacts.require("A");   module.exports = function(deployer) {      deployer.deploy(A,100,'PLA','PLAT');//需要传入对应构造函数参数 };