基于truffle进行合约部署

发布时间 2023-12-06 11:54:38作者: 若-飞

1.Truffle 简介

1.1.什么是 Truffle

Truffle 是一个世界级的开发环境,测试框架,以太坊的资源管理通道,致力于让以太坊上的开发变得简单。

Truffle 有以下特性:

内置的智能合约编译,链接,部署和二进制文件的管理;

快速开发下的自动合约测试;

脚本化的,可扩展的部署与发布框架;

部署到不管多少的公网或私网的网络环境管理功能;

使用 EthPM&NPM 提供的包管理,使用 ERC190 标准;

与合约直接通信的直接交互控制台;

可配的构建流程,支持紧密集成;

在 Truffle 环境里支持执行外部的脚本。

1.2.环境要求和安装

环境要求:

NodeJS 5.0+;

Windows,Linux,或 Mac OS X。

安装方式:

$ npm install -g truffle

Truffle 需要以太坊客户端,需要支持标准的 JSON RPC API。

如果你是 Windows 用户,推荐使用 Powershell 或 Git BASH 来安装和使用 Truffle 框架。

1.3.Truffle 开发工具

当开发基于 Truffle 的应用时,推荐使用 EthereumJS TestRPC,它是一个完整的在内存中的区块链仅仅存在于开发的设备上。它在执行交易时是实时返回,而不等待默认的出块时间,这样可以快速验证新写的代码,当出现错误时,也能即时反馈给你。它同时还是一个支持自动化测试的功能强大的客户端,Truffle 充分利用它的特性,能将测试运行时间提速近 90%。

使用 TestRPC 客户端充分测试后,可是尝试使用正式发布的客户端:Geth (go-ethereum)、WebThree(cpp-ethereum)、More,这些是完整的客户端实现,包括挖矿,网络,块及交易的处理,Truffle 可以在不需要额外配置的情况下发布到这些客户端。

2.Truffle 工程创建

创建工程目录,可以使用文件浏览器或使用下面的命令在命令行创建一个目录:

$ mkdir myproject

接下来,通过下面的命令初始化一个 Truffle 工程:

$ truffle

Truffle v3.4.11 - a development framework for Ethereum

$ cd myproject

$ truffle init

完成后,将拥有如下目录:

  • app/ - 应用文件运行的默认目录,这里面包括推荐的 javascript 文件和 css 样式文件目录,但可以完全决定如何使用这些目录;
  • contract/ - Truffle 默认的合约文件存放地址;
  • migrations/ - 存放发布脚本文件 ;
  • test/ - 用来测试应用和合约的测试文件;
  • truffle.js - Truffle 的配置文件。
  • truffle init 会默认创建一个构建在以太坊内的代币 demo 应用:METACOIN,可以使用这个工程来进行快速的学习,或者也可以删除这些文件来创建一个自己的工程。

3.编译合约

3.1.合约位置

所有合约应该位于 ./contracts 目录,默认提供一个合约文件,一个库文件,均以 .sol 结尾作为示例。

尽管库文件有一定的特殊性,但为简单起见,当前均称之为合约。

3.2.命令

要编译合约,使用:

truffle compile

Truffle 仅默认编译自上次编译后被修改过的文件,来减少不必要的编译。如果想编译全部文件,可以使用 --compile-all 选项:

truffle compile —compile-all

3.3. 约定

Truffle 需要定义的合约名称和文件名准确匹配。

举例来说,如果文件名为 MyContract.sol,那么合约文件须为如下两者之一:

contract MyContract {

  ...

}

// or

library MyContract {

  ...

}

这种匹配是区分大小写的,也就是说大小写也要一致,推荐大写每一个开头字母。

3.4.依赖

可以通过使用 import 来声明依赖,Truffle 将会按正确顺序依次编译合约,并在需要的时候自动关联库。

3.5.编译目录

编译的输出位于 ./build/contracts 目录,如果目录不存在会自动创建。

编译文件对于 Truffle 框架能否正常工作至关重要,不应该在正常的编译或发布以外手动修改这些文件。

4..移植

4.1.移植目的

 

移植是由一些 Javascript 文件组成来协助发布到以太坊网络,主要目的是用来缓存你的发布任务,它的存在基于你的发布需求会改变的前提。

当你的工程发生了重要的改变,你将创建新的移植脚本来将这些变化带到区块链上,之前运行移植的历史记录通过一个特殊的 Migrations 合约来记录到链上。

4.2.命令

执行移植,使用下述命令:

truffle migrate

如果你之前的移植是成功执行的,这个命令会执行所有的位于 migrations 目录内的移植脚本,truffle migrate 仅会执行新创建的移植。

如果没有新的移植脚本,这个命令不同执行任何操作,可以使用选项 --reset 来从头执行移植脚本。

4.3.移植脚本文件

一个样例文件如下:文件名:4_example_migration.js

module.exports = function(deployer) {

  // deployment steps

  deployer.deploy(MyContract);

};

 

需要注意的是文件名以数字开头,一个描述性的后缀结尾,数字前缀是必须的,用于记录移植是否成功,后缀仅是为了提高可读性,以方便理解。

移植 js 里的 exports 的函数接受一个 deployer 对象作为第一个参数,这个对象用于发布过程,提供了一个清晰的语法支持,同时提供一些通过的合约部署职责,比如保存发布的文件以备稍后使用。deployer 对象是用来缓存(stage)发布任务的主要操作接口。

像所有其它在 Truffle 中的代码一样,Truffle 提供了自己代码的合约抽象层(contract abstractions),并且进行了初始化,以方便可以便利的与以太坊的网络交互,这些抽象接口是发布流程的一部分。

4.4.部署器(deployer

移植文件会使用部署器来缓存部署任务,因此可以按一定顺序排列发布任务,它们会按正确顺序执行:

// Stage deploying A before B

deployer.deploy(A);

deployer.deploy(B);

 

另一选中可选的部署方式是使用 Promise,将部署任务做成一个队列,是否部署依赖于前一个合约的执行情况:

// Deploy A, then deploy B, passing in A's newly deployed address

deployer.deploy(A).then(function() {

  return deployer.deploy(B, A.address);

});

 

如果想更清晰,也可以选择实现一个 Promise 链。

可以根据发布到的网络的具体情况进行不同的部署流程,要实现不同条件的不同部署步骤,移植代码中需要第二个参数 network。示例如下:

module.exports = function(deployer, network) {

  // Add demo data if we're not deploying to the live network.

  if (network != "live") {

    deployer.exec("add_demo_data.js");

  }

}

 

指定一个网络:大多数 Truffle 提供的命令根据指定的网络不同而表现不同,会使用对应网络下的合约和配置信息,可以通过 --network 选项在参数上进行控制:

$ truffle migrate --network live

networks: {

  development: {

    host: "localhost",

    port: 8545,

    network_id: "*" // match any network

  },

  live: {

    host: "178.25.19.88", // Random IP for example purposes (do not use)

    port: 80,

    network_id: 1,        // Ethereum public network

    // optional config values

    // gas

    // gasPrice

    // from - default address to use for any transaction Truffle makes during migrations

  }

}

 

在上面这个例子中,Truffle 会在 live 网络中进行移植,如果配置如上述配置示例的 Example 所指定的内容的话,是最终在以太坊网络上进行部署。

4.5.部署 API

4.5.1.DEPLOYER.DEPLOY

发布一个指定的合约,第一参数是合约对象,后面是一些可选的构造器参数。这个函数适用于单例合约,它只会在 dapp 中只创建一个这个合约的实例(单例),函数会在部署后设置合约的地址(如:C ontract.address 将等于新的部署地址),它将会覆盖之前存储的地址。也可以传入一个合约数组,或数组的数组来加速多合约的部署。

需要注意的是如果库的地址可用,deploy 会自动为这个部署的合约联接任何需要的库,因此如果合约依赖某个库,应该先部署这个库:

// Deploy a single contract without constructor arguments

deployer.deploy(A);



// Deploy a single contract with constructor arguments

deployer.deploy(A, arg1, arg2, ...);



// Deploy multiple contracts, some with arguments and some without.

// This is quicker than writing three `deployer.deploy()` statements as the deployer

// can perform the deployment as a batched request.

deployer.deploy([

  [A, arg1, arg2, ...],

  B,

  [C, arg1]

]);

4.5.2.DEPLOYER.LINK

联接一个已经发布的库到一个或多个合约,destinations 可以是一个合约或多个合约组成的一个数组,如果目标合约并不依赖这个库,部署器会忽略掉这个合约。

这对于在 dapp 中不打算部署的合约(如:非单例)但却需要在使用前先联接的情况下非常有用。

// Deploy library LibA, then link LibA to contract B

deployer.deploy(LibA);

deployer.link(LibA, B);



// Link LibA to many contracts

deployer.link(LibA, [B, C, D]);

4.5.3.DEPLOYER.AUTOLINK(CONTRACT)

关联合约依赖的所有库,这需要所依赖的库已经部署,或在其前一步部署:

// Assume A depends on a LibB and LibC

deployer.deploy([LibB, LibC]);

deployer.autolink(A);

 

另外可以省略参数来调用函数 autolink(),这会自动关联合约依赖的所有库,需要保证在调用这个函数前,所有被需要的库已经部署:

// Link *all* libraries to all available contracts

deployer.autolink();

4.5.4.DEPLOYER.THEN

Promise 语法糖,执行做生意的部署流程:

deployer.then(function() {

  // Create a new version of A

  return A.new();

}).then(function(instance) {

  // Set the new instance of A's address on B.

  var b = B.deployed();

  return b.setA(instance.address);

});

4.5.5.DEPLOYER.EXEC

执行 truffle exec 做为部署的一部分:

// Run the script, relative to the migrations file.

deployer.exec("../path/to/file/demo_data.js");

5.实例

5.1.环境安装

npm install nodejs

npm install -g solc

npm install -g truffle

npm intall -g truffle-flattener

5.2.配置truffle

require('dotenv').config();

//const { MNEMONIC, PROJECT_ID } = process.env;



const private_keys = [

  process.env.PK1

]



const HDWalletProvider = require('@truffle/hdwallet-provider');



module.exports = {



  networks: {

    dev: {

     host: "127.0.0.1",     // Localhost (default: none)

     port: 7545,            // Standard Ethereum port (default: none)

     network_id: "5777",       // Any network (default: none)

    },



    bsctest: {

      provider: () => new HDWalletProvider({

        privateKeys:['0x8b9d4e96fb91e23axxxxxxxxxxx65e0843d'],

        providerOrUrl:`https://bsc-testnet.nodereal.io/v1/77055bfxxxxxxxd5d5b9`,

        numberOfAddress:1  

      }),

      network_id: 97,       

      confirmations: 2,    

      timeoutBlocks: 50, 

      gas: 5000000,         

      gasPrice: 50000000000, 

      skipDryRun: false     // Skip dry run before migrations? (default: false for public nets )

    },



  // Set default mocha options here, use special reporters, etc.

  mocha: {

    // timeout: 100000

  },



  // Configure your compilers

  compilers: {

    solc: {

      version: "0.8.1",      // Fetch exact version from solc-bin

    }

  },



};

配置部署的信息,以bsctest为例。

5.3.编译合约

test@MacBook-Pro TripleC % sudo truffle compile --network bsctest



Compiling your contracts...

===========================

> Everything is up to date, there is nothing to compile.

5.4.部署合约

test@MacBook-Pro TripleC % sudo truffle migrate --network bsctest



Compiling your contracts...

===========================

> Everything is up to date, there is nothing to compile.





Starting migrations...

======================

> Network name:    'bsc'

> Network id:      56

> Block gas limit: 140000000 (0x8583b00)





1685499904_deploy__triplec_medal.js

===================================

[ '0xe245fa636F67E491b8052D7C77256Fc192edAd86' ]



   Deploying 'TriplecMedal'

   ------------------------

   > transaction hash:    0x5aeb0933e5eaf680a2df06b41dc59ab1131481e5e5730400cf8e37b9559a6ee4

   > Blocks: 3            Seconds: 9

   > contract address:    0xB8e8037410D23db82E111111b44920f1E92113A1

   > block number:        34105401

   > block timestamp:     1701830356

   > account:             0xe245fa636F671111111d86

   > balance:             0.401088294

   > gas used:            4581180 (0x45e73c)

   > gas price:           50 gwei

   > value sent:          0 ETH

   > total cost:          0.229059 ETH



   Pausing for 2 confirmations...



   -------------------------------

   > confirmation number: 2 (block: 34105405)

   > Saving artifacts

   -------------------------------------

   > Total cost:            0.229059 ETH



Summary

=======

> Total deployments:   1

> Final cost:          0.229059 ETH

这样就可以得到contract address了