Truffle Migrations - How to link libraries when multiple contracts are deployed together

From this article you can learn:

  1. What is the role of Truffle's migration contract  Migrations.sol ?
  2. How to deploy multiple contracts together;
  3. If conditional deployment is performed for different networks and different accounts;
  4. How to link the library.

Migrations are a set of methods for developers to automate the deployment of data and its supporting structures. They are useful for managing the deployment of new software versions and are therefore not limited to blockchain development.

Truffle migrations allow us to push contracts onto the Ethereum chain (whether it's the local network, testnet, or mainnet), as well as link with other contracts or use initialized contract data.

The really cool thing about migrations is the management of contract addresses on the blockchain. Truffle  almost completely abstracts this tedious work.

Prepare

Make sure Truffle Framework  and  Ganache CLI are installed  .

Start

Create (or select) a project directory, then run  truffle init , and output similar to the following:

Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test

The init command initializes a Truffle project in the current directory. The directory looks like this:

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

In the  contracts directory, create a contract file: the  Storage.sol code is as follows:

pragma solidity > 0.4.21;

contract Storage {

    mapping (string => string) private _store;

    function addData(string key, string value) public {
        require(bytes(_store[key]).length == 0);
        _store[key] = value;
    }

    function removeData(string key) public returns (string) {
        require(bytes(_store[key]).length != 0);
        string prev = _store[key];
        delete _store[key];
        return prev;
    }

    function changeData(string key, string newValue) public {
        require(bytes(_store[key]).length != 0);
        _store[key] = newValue;
    }

}

Initial Migrations file and deployment rules

You may have seen the   sum  truffle init generated at runtime  .Migrations.sol1_initial_migration.js

Initial migration contracts generally do not need to be modified, they track the addresses deployed on the blockchain. Of course, you can also modify the contract file according to your own needs  Migrations.sol and perform some advanced migration management, but you need to keep truffle initthe interface created by the command.

1_initial_migration.js The migration file only explains how to  Migrations.sol deploy the contract to the corresponding chain.

1_initial_migration.js The migration file name, the serial number in front, represents the order in which truffle migrate runs the migration files, and 1 represents the first migration file to run (starting from 1). We can create other migration files:  2_mycontract_migration.js , after each contract is deployed, Truffle will save the migration sequence number to the Migrations contract last_completed_migration

Assuming  migrations just these two migration files, truffle migrate4 transactions actually occur at runtime:

  1. run  1_initial_migration.js to deploy
  2. Write serial number 1 to the contract Migrations
  3. run  2_mycontract_migration.js to deploy
  4. Write serial number 2 to the contract Migrations

last_completed_migration Indicates the last deployed migration, and then adds other migration files: 3_yourcontract.js When running truffle migrate, Truffle will first read the  last_completed_migration state variables, see which ones have been deployed before, and deploy  last_completed_migration (all) migration files larger than the serial number, so as to ensure that No duplicate deployments.

Note that if you modify an existing contract and need to redeploy, it  truffle migrate will not be automatically deployed when running directly. You need to add (or modify) a migration file with a higher serial number before running it  truffle migrate.

truffle migrate Can be -f 序号 used to force re- start migrations one by one (values ​​ignored  last_completed_migrationin this case). For example:  truffle migrate -f 2 deployment will start from the 2nd migration file.

Migrated related data

In order to deploy a smart contract to the Ethereum blockchain, you first need to write a migration file. In the migrationsdirectory, create a migration file  2_deploy_contracts.js. Now the structure of the project is as follows:

.
├── contracts
│   ├── Migrations.sol
│   └── Storage.sol
├── migrations
│   ├── 1_initial_migration.js
│   └── 2_deploy_contracts.js
├── test
├── truffle-config.js
└── truffle.js

To deploy a contract using migrations, you need to read the contract's artifacts (build) . The build file will describe the network and address of the contract deployment and the functions contained in the contract.

Where does the artifacts (build) data come from?

In the project directory, run  truffle compile, if there are no errors, there will be the following output:

Compiling ./contracts/Migrations.sol...
Compiling ./contracts/Storage.sol...

Writing artifacts to ./build/contracts

Perhaps under different compiler versions, there may be some warnings, as long as there are no errors.

Looking at the directory structure of the project again, there will be an additional build directory, as follows:

.
├── build
│   └── contracts
│       ├── Migrations.json
│       └── Storage.json
├── contracts
│   ├── Migrations.sol
│   └── Storage.sol
├── migrations
│   ├── 1_initial_migration.js
│   └── 2_deploy_contracts.js
├── test
├── truffle-config.js
└── truffle.js

build The directory contains two files: Migrations.json and  Storage.json they correspond to  contracts the two contract files in the directory.

*.json The files describe their corresponding contracts, including:

  • contract name
  • Contract ABI (Application Binary Interface — describes all methods of the contract and their corresponding parameters and return values)
  • Contract bytecode bytecode (compiled contract data)
  • The bytecode deployed by the contract (the latest bytecode that has been deployed on the chain)
  • The compiler version that the contract was last compiled with
  • A list of networks that have deployed contracts, and the contract addresses on each network.

This file enables Truffle to create a JavaScript wrapper (ie  truffle-contract ) to communicate with smart contracts. For example, when called in JavaScript code contract.address , the Truffle framework  *.json reads addresses from files and lets us easily convert between different contract versions and networks.

write migration file

Armed with this knowledge, let's write our first migration file  2_deploy_contracts.jswith the following code:

// 获取对应的合约文件
var Storage = artifacts.require("./Storage.sol");

// JavaScript export
module.exports = function(deployer) {
    // deployer 是用来部署

    // 部署
    deployer.deploy(Storage);
}

Writing migrations is that simple. In order to run the migration script, run the following command in a terminal:

truffle migrate

But this time, you will get an error:

Error: No network specified. Cannot determine current network.

It means that Truffle cannot find the network to be deployed to. At this time, you can open a new tab in the command line terminal and run  ganache-clito start a simulated blockchain. After startup, there will be a similar output:

Ganache CLI v6.9.0 (ganache-core: 2.10.0)

Available Accounts
==================
(0) 0x828da2e7b47f9480838f2077d470d39906ad1d8e
(1) 0xa4928865329324560185f1c93b5ebafd7ae6c9e8
(2) 0x957b8b855bed52e11b2d7e9b3e6427771f299f3f
(3) 0xf4b6bcabedaf1ccb3d0c89197c4b961460f1f63d
...

Private Keys
==================
(0) 8729d0f1d876d692f2f454f564042bd11c1e6d0c9b1808954f171f6f7b926fd6
(1) 452dfeee16e5a0e34fa5348f0ef11f39a8b4635e5f454f77fc228ca9598f6997
(2) 9196ad9fd6234f09ee13726cb889dcbc438c15f98e8ff1feb36a93758fa6d10a
(3) fa47edd832e896314544b98d7e297ac2ce2097b49f8a9d7e7ae0e38154db8760
....

HD Wallet
==================
Mnemonic:      void august badge future common warfxlb ...
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

A private blockchain has now been built and it runs localhost:8545on . Now let's configure Truffle to deploy contracts to the network.

Open the  truffle-config.js file and add the following:

module.exports = {
  networks: {
    development: {
      host: "127.0.0.1",
      port: 8545,
      network_id: "*"
    }
  }
};

The meaning of the above configuration is to deploy the compiled contract to  localhost:8545 the network where it is located.

Run it now  truffle migrate and get the following output:

Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x06595c0eccde8cb0cf642df07beefea11e3e96bfb470e8tinyxiong6567cecc37aed8
  Migrations: 0x6008e9a2c213d51093d0f18536d1aa3b00a7e058
Saving successful migration to network...
  ... 0x392fb34c755970d1044dc83c56df6e51d5c4d4011319f659026ba27884126d7b
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying Storage...
  ... 0xb8ec575a9f3eca4a11a3f61170231a1816f7c68940d8487e56567adcf5c0a21e
  Storage: 0xd8e2af5be9af2a45fc3ee7cdcb68d9bcc37a3c81
Saving successful migration to network...
  ... 0x15498a1f9d2ce0f867b64cdf4b22ddff56f76xlbcd3d3a92b03b7aa4d881bac
Saving artifacts...

Truffle migrates contracts to the network and saves artifacts. In the file of the build directory  , check  that it is correct Storage.jsonby inspecting the  object. networksYou should see something like this:

"networks": {
  "1525343635906": {
    "events": {},
    "links": {},
    "address": "0xd8e2af5be9af2a45fc3ee7cdcb68d9bcc37a3c81",
    "transactionHash": "0xb8ec575a9f3eca4a11a3f61170231a1816f7c68940d8487e56567adcf5c0a21e"
  }
}

1525343635906 is the ID of the network. Both the Ethereum main network and the test network have a fixed ID (the main network is 1).

address is the address where the contract is deployed.
transactionHash is the transaction hash of the deployed contract.

You will see how to use it later.

Deploy multiple contracts

The real highlight of the Truffle migration is the ability to compile, deploy, and track multiple contracts (as is the case with almost all blockchain projects).

The migration file not only allows us to deploy multiple contracts with a single command, but also allows us to call the functions of the contracts, such as getting the return values ​​of those functions and passing them to subsequent contracts.

We  contracts add a new contract in the directory  InfoManager.sol, the code is as follows:

pragma solidity >0.4.21;

import "./Storage.sol";

contract InfoManager {

    Storage private _dataStore;

    uint private _lastAdded;

    constructor(Storage dataStore) public {
        _dataStore = dataStore;
    }

    function addData(string key, string value) public {
        require((now - 1 days) > _lastAdded);
        _dataStore.addData(key, value);
    }

}

It can be seen that, InfoManager depending on  Storage the contract, not only that, the constructor of InfoManager also requires the  Storage contract as a parameter.

 Rework the deployment to  2_deploy_contracts.js make it work  :InfoManager

var Storage = artifacts.require("./Storage.sol");
var InfoManager = artifacts.require("./InfoManager.sol");

module.exports = function(deployer) {

    // 部署 Storage
    deployer.deploy(Storage)
        // 等待、直到合约部署完成
        .then(() => Storage.deployed())
        // 传递 Storage 合约地址,部署 InfoManager 合约
        .then(() => deployer.deploy(InfoManager, Storage.address));
}

The syntax for deployment is:

...
deployer.deploy(`ContractName`, [`constructor params`]) // 返回一个 promise
...

Since it  deploy(...) returns a promise, we can handle it any way we like, but don't notice that in the deployment file, it's not supported yet  async .

We can also customize the calling function after deploying the contract. For example, the migration can also be like this:

deployer.deploy(Storage)
    .then(() => Storage.deployed())
    .then((instance) => {
        instance.addData("Hello", "world")
    }).then(() => deployer.deploy(InfoManager, Storage.address));

In this way, before deploying  InfoManager , Storage add a piece of data.

This trick is useful because sometimes interdependent contracts may need to be written outside of the constructor.

Deploy according to the network

Different deployments can also be performed according to different networks. This is useful both for using the simulated chain's data during the development phase and for the mainnet launch using the deployed mainnet contract as an input parameter to the contract.

module.exports Extend a parameter  by exporting a function  network :

module.exports = function(deployer, network) {
    if (network == "live") {
        // do one thing
    } else if (network == "development") {
        // do other thing
    }
}

Select an account to deploy

module.exports The default function can also expose an account parameter, these accounts are "exposed" by the Ethereum node or wallet provider, see the following example:

module.exports = function(deployer, network, accounts) {
    var defaultAccount;
    if (network == "live") {
        defaultAccount = accounts[0]
    } else {
        defaultAccount = accounts[1]
    }
}

link library

You can also  deployer.link(...) link to an already existing (deployed) library by:

...
deployer.deploy(MyLibrary);
deployer.link(MyLibrary, MyContract);
deployer.deploy(MyContract);
...

in conclusion

By using these techniques, it is possible to automate much of the blockchain deployment work and reduce a lot of repetitive work involved in developing decentralized applications.

Reference article:

  1. Truffle Migrations Explained - SitePoint
  2. Truffle Chinese Documentation

Explain Truffle Migrations in detail - contract deployment is no longer confusing | Chainlink Community | Blockchain technology in simple terms 

Guess you like

Origin blog.csdn.net/u013288190/article/details/123863384