Solidity入门系列:基于Truffle搭建本地开发环境

1,377 阅读4分钟

怎样开始Solidity的学习

最近在学习区块链、智能合约相关知识,发现Solidity权威的学习资料很匮乏,很奇怪Solidity这么火竟然没有官方的详细学习资料,比如《Solidity Cookbook》这类书籍。

这篇文章是Solidity入门第一篇,关于如何搭建本地环境搭建。我个人也是新手,多年的语言学习体验还是习惯先有个本地的开发环境,Ethereum官方推荐的学习工具列举在下面:ethereum.org/zh/develope… ,其中Remix我看到很多人在推荐,如果喜欢在线编码的话可以直接使用。

另外,Solidity的入门学习材料推荐以下两个:

  1. CryptoZombie 讲的很泛但是方方面面都介绍到了,很适合建立初步的体感。
  2. Solidity By Example 我正在看,例子很多,但是貌似也没有介绍的很深入。
  3. 需要更加Advanced的学习资料。

Solidity本地环境我个人选择Truffle,我也是新手,如果大家有更好的推荐可以在评论区推荐给我,谢谢~

Truffle环境搭建

主要参考Truffle官方Quick Start:trufflesuite.com/docs/truffl…

安装很简单:

$ npm install -g truffle

强烈推荐安装Ganache,Ganache可以在本地启动一个带UI的eth链,带10个测试账号,每个账号有100个eth供测试用。

截屏2022-06-02 下午11.34.13.png

虽然Truffle也会启动本地的eth测试链,但是有个UI界面的Ganache还是很直观的,方便学习。

Ganache建议安装支持Filecoin的最新版本,虽然我们还没用到Filecoin,但是提前准备总是没错的。

参考:trufflesuite.com/docs/ganach…

所有可用的版本列表:github.com/trufflesuit…

手动安装最新版本,我用的版本是2.6.0-beta.3.1390,下载地址

Truffle一些操作技巧及常用命令

参考:trufflesuite.com/docs/truffl…

Truffle Quick Start里面的例子使用的MetaCoin,我们这里从truffle init开始建一个新的truffle工程。

1. 创建工程

$ mkdir test
$ cd test
$ truffle init

查看目录树:

$ tree .
.
├── contracts
│   └── Migrations.sol
├── migrations
│   └── 1_initial_migration.js
├── test
└── truffle-config.js
\

3 directories, 3 files

2. 启动Ganache测试链

截屏2022-06-03 上午12.22.24.png

截屏2022-06-03 上午12.24.52.png

截屏2022-06-03 上午12.26.28.png

3. 配置Truffle Project连接到Ganache UI

图片里可以看到,Ganache的监听地址为http://127.0.0.1:7545 ,我们把这个地址配置到truffle-config.js里。

module.exports = {
  ...
  networks: {
    development: {
      host: "127.0.0.1",     // Localhost (default: none)
      port: 7545,            // Standard Ethereum port (default: none)
      network_id: "*",       // Any network (default: none)
    }
  },
  ...
}

启动truffle console测试下是否成功:

$ truffle console
truffle(development)> let accounts = await web3.eth.getAccounts()
undefined
truffle(development)> accounts
'0xf0Ac7625E3D35B2eed3b6D96E3aeebeCBC5af091''0x44CEfACE09E51dadAcaf153CE4f6Da90873D176c''0x5E7DFEEDA81e3734B756c429dBE291Ad0a773Ef5''0x357FAd25d64B1f66C5A1BD69bd57ce7f1846D203''0xaca05A86BEB9D9D91a73B092E747eAbc0b629c1f''0x10159311037dECc9aBCc32EEB1F5F93d8Db5fc02''0x2B8D12e9A5E44C64673bDA343F5d5Db66752bcE0''0x9581F92090bf2985e105d6Eb90cC1afFE05e6a34''0xAB3DF6FEA353D55c64c5aC206D40f9eFa951DACf''0x6587A514e15e59c7F8fb2Bfc0824707ba77004c2']
truffle(development)>.exit

注意用await关键字保障等待异步操作能正确返回。因为账户的生成是随机的,所以上面你的10个账户和我的不会相同。

4. 开发和部署新合约的一个示例

我们以solidity-by-example.org/payable 这里的一个example做示范,这个example里面包含了合约付款,是个不错的演示程序。

  1. 编写新合约

contracts/Payable.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

contract Payable {
    // Payable address can receive Ether
    address payable public owner;

    // Payable constructor can receive Ether
    constructor() payable {
        owner = payable(msg.sender);
    }

    // Function to deposit Ether into this contract.
    // Call this function along with some Ether.
    // The balance of this contract will be automatically updated.
    function deposit() public payable {}

    // Call this function along with some Ether.
    // The function will throw an error since this function is not payable.
    function notPayable() public {}

    // Function to withdraw all Ether from this contract.
    function withdraw() public {
        // get the amount of Ether stored in this contract
        uint amount = address(this).balance;

        // send all Ether to owner
        // Owner can receive Ether since the address of owner is payable
        (bool success, ) = owner.call{value: amount}("");
        require(success, "Failed to send Ether");
    }

    // Function to transfer Ether from this contract to address from input
    function transfer(address payable _to, uint _amount) public {
        // Note that "to" is declared as payable
        (bool success, ) = _to.call{value: _amount}("");
        require(success, "Failed to send Ether");
    }
}

大致讲解下:

  • deposit:存eth进合约。
  • notPayable: 调用这个函数时不可以付钱。
  • withdraw:把这个合约内的所有钱都打给合约的owner。
  • transfer:从这个合约里转移一定数额的钱给指定账户。
  1. 编写migration文件。

migration文件是指导truffle如何部署合约的文件,具体原理还没深入看过,目测是truffle会先部署一个deploy用的合约,后面的合约会借助这个deployer来做。

migrations/2_payable_migration.js

const Payable = artifacts.require("Payable");

module.exports = function (deployer) {
  deployer.deploy(Payable);
};
  1. 部署

可以在命令行执行truffle migrate,或是在truffle console里执行migrate即可。

$ truffle migrate

Compiling your contracts...
===========================
> Compiling ./contracts/Migrations.sol
> Compiling ./contracts/Payable.sol
> Artifacts written to /Users/zhangwei/program/crypto/test/build/contracts
> Compiled successfully using:
   - solc: 0.8.13+commit.abaa5c0e.Emscripten.clang


Starting migrations...
======================
> Network name:    'development'
> Network id:      5777
> Block gas limit: 6721975 (0x6691b7)


1_initial_migration.js
======================

   Deploying 'Migrations'
   ----------------------
   > transaction hash:    0xc5612b8e2f3c1f59627085ecfe1bbfd02f8426245e231ebcf6320b7676d46c23
   > Blocks: 0            Seconds: 0
   > contract address:    0xedF2d3b63b29bdE8ab015f7c306877BEE162eb23
   > block number:        1
   > block timestamp:     1654262832
   > account:             0xf0Ac7625E3D35B2eed3b6D96E3aeebeCBC5af091
   > balance:             99.99502292
   > gas used:            248854 (0x3cc16)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00497708 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00497708 ETH


2_payable_migration.js
======================

   Deploying 'Payable'
   -------------------
   > transaction hash:    0x4e6ec2f7b722deea05d0f9108d66b8b8a7e15877bb7b6a4bc2b77e34271b1a9e
   > Blocks: 0            Seconds: 0
   > contract address:    0x767836195C5A17217B3eeb2a9D2Fbe239B7Ea28D
   > block number:        3
   > block timestamp:     1654262832
   > account:             0xf0Ac7625E3D35B2eed3b6D96E3aeebeCBC5af091
   > balance:             99.98769456
   > gas used:            323905 (0x4f141)
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.0064781 ETH

   > Saving migration to chain.
   > Saving artifacts
   -------------------------------------
   > Total cost:           0.0064781 ETH

Summary
=======
> Total deployments:   2
> Final cost:          0.01145518 ETH

5. 与你的新合约交互

展示一下如何与你的合约交互,及一些常用的操作方法。

$ truffle console
truffle(development)> let accounts = await web3.eth.getAccounts()
undefined
truffle(development)> let instance = await Payable.deployed()
undefined

// accounts[1]存入3 ether,1 ether==10^18 wei
truffle(development)> instance.deposit({from:accounts[1], value:"3000000000000000000"})
......

截屏2022-06-03 下午9.56.33.png

注意,第一个合约只有99.99eth,0.01eth是在部署合约时作为Gas费燃烧掉了。 继续:

// 从合约里转移2ether给accounts[2]
truffle(development)> instance.transfer(accounts[2], "2000000000000000000")

截屏2022-06-03 下午9.59.06.png

继续:

// 剩下的所有钱(1eth)全部提取出来给owner(account[0])
truffle(development)> instance.withdraw()

截屏2022-06-03 下午10.05.42.png

6. 完结

基本就这样了,后续有别的操作技巧再来补充,bye~~