Truffle Migrations Explained

Share this article

Truffle Migrations Explained

Migrations, generally speaking, are ways for developers to automate the deployment of data and its supporting structures. They are very useful for managing the deployment of new software versions, and as such aren’t exclusive to blockchain development.

Truffle migrations enable us to “push” the smart contracts to the Ethereum blockchain (either local, tesnet or mainnet) and to set up necessary steps for linking contracts with other contracts as well as populate contracts with initial data.

Where migrations really shine is the management of contract addresses on the blockchain. This usually tedious job gets almost entirely abstracted away with Truffle.

Prerequisites

Make sure that you have installed the Truffle Framework and Ganache CLI.

Getting Started

For starters, choose a project folder and then run truffle init. You should get an output similar to this:

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

Commands:

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

This command creates a barebones Truffle project in the directory where you’re positioned. The directory looks like this:

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

For starters, in the contracts directory, create a new file called Storage.sol, which should look like this:

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

As you might have noticed, two files are created when you run truffle init. They are Migrations.sol and 1_initial_migration.js.

The initial migration files rarely need to be changed. What they do is essentially keep track of addresses on the blockchain.

The Migrations.sol file can look any way you want it to, but it must conform to a fixed interface which looks like the interface created by the truffle init command. What you can do in those files is some advanced mangling of migrations, but as I’ve said, it’s rarely needed.

The same goes for the 1_initial_migration.js file. What it does is simply push the Migrations.sol file to the desired blockchain.

Migrations Data

In order to deploy the smart contracts to the Ethereum blockchain, you must first write migrations. In order to get started, in your migrations directory, create a file called 2_deploy_contracts.js. Your project structure should now look like this:

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

In order to deploy smart contracts with migrations, first we need to access their artifacts. These are files which describe the contract addresses, the networks on which the contracts have been deployed and the functions which contracts have.

So where does all of this data come from?

In your project directory, run truffle compile. If all goes well, you should have an output similar to this:

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

Writing artifacts to ./build/contracts

Depending on the compiler version, you might get some warnings, but as long as there are no errors, you’re good to go.

Now check your project directory structure again:

.
├── 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

Notice that there is now a build folder containing two files — Migrations.json and Storage.json — which match the smart contract files in the contracts directory.

These *.json files contain descriptions of their respective smart contracts. The description includes:

  • Contract name
  • Contract ABI (Application Binary Interface — a list of all the functions in the smart contracts along with their parameters and return values)
  • Contract bytecode (compiled contract data)
  • Contract deployed bytecode (the latest version of the bytecode which was deployed to the blockchain)
  • The compiler version with which the contract was last compiled
  • A list of networks onto which the contract has been deployed and the address of the contract on each of those networks.

This file enables Truffle to create a JavaScript wrapper for communicating with the smart contract. For example, when you call contract.address in your JavaScript code, the Truffle framework reads the address from the *.json file and enables effortless transitions between contract versions and networks.

Writing Migrations

Armed with this knowledge, let’s write our first migration. In the 2_deploy_contracts.js file, write this:

// Fetch the Storage contract data from the Storage.json file
var Storage = artifacts.require("./Storage.sol");

// JavaScript export
module.exports = function(deployer) {
    // Deployer is the Truffle wrapper for deploying
    // contracts to the network

    // Deploy the contract to the network
    deployer.deploy(Storage);
}

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

truffle migrate

You should get an error saying:

Error: No network specified. Cannot determine current network.

This means that Truffle couldn’t find the network to which you want to deploy.

In order to use a simulated Ethereum blockchain, open a new tab in the terminal and run ganache-cli. You should get an output similar to this:

Ganache CLI v6.1.0 (ganache-core: 2.1.0)

Available Accounts
==================
(0) 0x828da2e7b47f9480838f2077d470d39906ad1d8e
(1) 0xa4928865329324560185f1c93b5ebafd7ae6c9e8
(2) 0x957b8b855bed52e11b2d7e9b3e6427771f299f3f
(3) 0xf4b6bcabedaf1ccb3d0c89197c4b961460f1f63d
(4) 0x4bcae97be4a0d1f9a6dea4c23df8a2bffdb51291
(5) 0xe855c7cccac3a65ad24f006bf084c85c0197a779
(6) 0x168cb232283701a816a3d118897eedfcae2aec9d
(7) 0x49563e64868e1d378e20b6ab89813c1bbaa0fd48
(8) 0x467c6f6f526eee9f66776197e3a9798c1cbf78e0
(9) 0xf65b47a3c663e2cc17ded8f197057a091686da43

Private Keys
==================
(0) 8729d0f1d876d692f2f454f564042bd11c1e6d0c9b1808954f171f6f7b926fd6
(1) 452dfeee16e5a0e34fa5348f0ef11f39a8b4635e5f454f77fc228ca9598f6997
(2) 9196ad9fd6234f09ee13726cb889dcbc438c15f98e8ff1feb36a93758fa6d10a
(3) fa47edd832e896314544b98d7e297ac2ce2097b49f8a9d7e7ae0e38154db8760
(4) 7ba1a96db190c14aaee5401dd5faab1af9074d7e6f24bc2f24b5084514bbf405
(5) 90088ce271f227db6be251c3055872c0d3dbdda9fc23ed119cf9d55db7c91259
(6) c36afd6f8f291b45e94ef0059576a86602e9a982b87e0c6fb25cfab4d68e9030
(7) 2766ac8aee110e9ad1ea68d1f28aaafb464fb1ef2a759bf5b2f628d256043c15
(8) 51ccf45f87806e8e9f30f487d6cdd0b44de3ad103f0d8daf9f1e20d9a4728dd9
(9) 398c0f079448c1e3724c9267f07ca4ab88233fc995a3d463c7c64d1a191688f5

HD Wallet
==================
Mnemonic:      void august badge future common warfare dismiss earn dog shell vintage dice
Base HD Path:  m/44'/60'/0'/0/{account_index}

Listening on localhost:8545

This means that you’ve spun up a private blockchain and it’s running on localhost:8545. Now let’s set up Truffle to deploy to that network.

Place the following in the truffle.js file:

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

This simply means you’re deploying your contract to the network running on localhost:8545.

Now run truffle migrate. You should get an output similar to this:

Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x06595c0eccde8cb0cf642df07beefea11e3e96bfb470e8dbaf6567cecc37aed8
  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...
  ... 0x15498a1f9d2ce0f867b64cdf4b22ddff56f76aee9cd3d3a92b03b7aa4d881bac
Saving artifacts...

Truffle migrated your contract to the network and saved the artifacts. In the build directory, in the Storage.json file, check that this is correct by inspecting the networks object. You should see something similar to this:

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

1525343635906 is the ID of the network. (The Ethereum main network and all the major testnets have fixed IDs like 1,2,3 etc.)

address is the address to which the contract was deployed.

transactionHash is the hash of the transaction which was used for contract deployment.

We’ll see how this is useful later on in the tutorial.

Multiple contracts

Where Truffle migrations really shine is when there are multiple contracts to compile, deploy and keep track of (which almost all blockchain projects have).

Not only do migrations allow us to deploy multiple contracts with a single command, they allow us to run arbitrary functions on the contracts, get return values of those functions and pass them to subsequent contracts.

Now in your contracts directory, create a file called InfoManager.sol. In the file write a contract like this:

pragma solidity ^0.4.21;

import "./Storage.sol";


contract InfoManager {

    Storage private _dataStore;

    uint private _lastAdded;

    function InfoManager(Storage dataStore) public {
        _dataStore = dataStore;
    }

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

}

As we can see, this contract depends on the Storage contract. Not only that, it takes the Storage contract as a parameter in its constructor. Let’s examine the migrations which will make this possible. The migrations are contained in the same file called 2_deploy_contracts.js:

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

module.exports = function(deployer) {

    // Deploy the Storage contract
    deployer.deploy(Storage)
        // Wait until the storage contract is deployed
        .then(() => Storage.deployed())
        // Deploy the InfoManager contract, while passing the address of the
        // Storage contract
        .then(() => deployer.deploy(InfoManager, Storage.address));
}

The syntax for deploying is:

...
deployer.deploy(`ContractName`, [`constructor params`]) // Returns a promise
...

Since the deploy(...) function returns a promise, you can handle it any way you like, with the notable exception of async not working in migrations for some reason.

You can also run custom steps after the contract has been deployed. For example, the migration could look like this:

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

This would populate the Storage contract with a string world at the key data before deploying the InfoManager contract.

This is useful because sometimes the interdependence between contracts can be such that some data must be either retrieved or inserted outside of the scope of the contract constructor.

Networks

You’re able to conditionally run certain migrations depending on which network you’re on. This can be very useful for either populating mock data in the development phase or inputting already deployed mainnet contracts into your contracts.

This is done by “expanding” the inserted parameters of the module.exports function:

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

Accounts

The module.exports default function also exposes the accounts which you have access to through your Ethereum node or the wallet provider. Here’s an example:

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

Libraries

You’re also able to link existing libraries (already deployed), by using the deployer.link(...) function:

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

Conclusion

By using the techniques outlined above, you can automate most of your blockchain deployments and reduce much of the boilerplate work involved in the development of decentralized applications.

Frequently Asked Questions (FAQs) about Truffle Migrations

What are the prerequisites for using Truffle Migrations?

Before you start using Truffle Migrations, you need to have a basic understanding of Ethereum, Solidity, and the Truffle framework. You should also have Node.js and npm installed on your system. Truffle can be installed globally on your system using npm. You also need to have Ganache installed for creating a personal Ethereum blockchain which you can use to run tests, execute commands, and inspect state while controlling how the chain operates.

How do I create a migration file in Truffle?

In Truffle, migration files are created in the ‘migrations’ directory. You can create a new migration file by creating a new .js file in this directory. The file should be named in a way that it can be sorted by a filename. The name of the file should describe the action of the migration. For example, ‘2_deploy_contracts.js’. The number at the beginning of the filename is used by Truffle to determine the order of execution.

What is the purpose of the ‘deployer’ in Truffle Migrations?

The ‘deployer’ is an object available in the migration files that aids in deploying the smart contracts onto the Ethereum network. It is responsible for staging deployment tasks. It provides methods like deploy() which is used to deploy a contract and link() which is used to link a deployed library to a deploying contract.

How can I specify the network for migration in Truffle?

You can specify the network for migration in the ‘truffle-config.js’ file. In this file, you can define various networks like development, testing, or live. Each network has specific configurations like host, port, network_id, and gas. You can specify the network for migration using the ‘–network’ flag followed by the network name in the command line.

What is the role of the ‘artifacts’ in Truffle Migrations?

Artifacts’ are used in Truffle migrations to represent your smart contracts. The ‘artifacts’ object provides a method called ‘require()’ which is used to import the contract abstraction of a specific contract. This method takes the name of the contract as an argument. The contract abstraction includes various details about the contract including its deployed address, ABI, and more.

How can I interact with deployed contracts in Truffle Migrations?

Once a contract is deployed using Truffle Migrations, you can interact with it using the contract abstraction provided by the ‘artifacts.require()’ method. This abstraction provides various methods to interact with the contract. For example, you can call the methods of the contract, listen to events emitted by the contract, or send transactions to the contract.

How can I handle asynchronous operations in Truffle Migrations?

Asynchronous operations in Truffle Migrations can be handled using the ‘async/await’ syntax. The ‘deployer’ methods like ‘deploy()’ and ‘link()’ are asynchronous and return promises. You can use the ‘await’ keyword to wait for these promises to resolve before moving on to the next operation.

What is the purpose of the ‘2_deploy_contracts.js’ file in Truffle Migrations?

The ‘2_deploy_contracts.js’ file is a migration file in Truffle. This file is responsible for deploying your smart contracts onto the Ethereum network. The number ‘2’ at the beginning of the filename indicates the order of execution. This means that this file will be executed second in the migration process.

How can I reset migrations in Truffle?

You can reset migrations in Truffle using the ‘migrate:reset’ command. This command will run all your migrations from the beginning. It is useful when you want to redeploy all your contracts from scratch. However, be careful while using this command as it will also delete all the data stored in your contracts.

How can I test my migrations in Truffle?

You can test your migrations in Truffle using the ‘truffle test’ command. This command will run all your test files located in the ‘test’ directory. You can write tests to check the deployment of your contracts, the execution of your contract methods, and more. Truffle supports both JavaScript and Solidity tests.

Mislav JavorMislav Javor
View Author

Mislav Javor is a software engineer and CEO (in that order). He writes smart contracts, conducts lectures, and blogs at mislavjavor.com.

blockchainethereumethereum-hubethereum-toolsmigrationsTruffle
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week