Michal Zalecki
Michal Zalecki
software development, testing, JavaScript,
TypeScript, Node.js, React, and other stuff

Deploying smart contracts with Truffle

Truffle provides a system for managing the compilation and deployment artifacts for each network. To make an actual transaction and put a smart contract on-chain we have to provide Truffle with an appropriate configuration. We configure each network separately. From this post, you will learn how to prepare a setup and deploy to a few widely used test networks.

Each transaction (deployment included) will cost you some Ether. It does not matter whether you use a testnet or mainnet. The difference is that Ether on a testing network is worthless and you can obtain it a from the faucet. Each test network has different rules which change from time to time, so you have to do some research. In my opinion, the easiest way is just to open MetaMask, select a network, press Buy, and explore available options.

Configuration for each network should appear in truffle.js file in the root of the Truffle project.

Migrations

Before we go into specifics of particular test network, let's focus on migrations. When you spin up a new truffle project, you will find a Migration.sol contract. It stores a history of previously run migrations on-chain. Truffle reads Migration address from a build artifact. It saves the number of the latest completed migration. There is no need to change anything within this contract.

Files we add to make migrations happen are JavaScript files under the migrations directory. Each migration file exports a function which accepts a deployer module, network name, and list of available accounts.

This is how a simple migration file may look like. First, we require contracts we want to deploy and then we can deploy them linking the Ledger library to the Funding contract.

// migrations/2_functing.js

const Funding = artifacts.require("./Funding.sol");
const Ledger = artifacts.require("./Ledger.sol");

const ETHER = 10 ** 18;
const DAY = 3600 * 24;

module.exports = function(deployer, _network, _accounts) {
  deployer.deploy(Ledger);
  deployer.link(Ledger, Funding);
  deployer.deploy(Funding, ETHER, 7 * DAY);
};

Changing the smart contract or deployment script after migration run and rerunning has no effect unless --reset option is specified. Migrations run from the last completed.

That is why naming files is important. Migration file name should start with a number. The rest of the filename is there just for readability.

Deploy to Ganache

Ganache is your personal Ethereum blockchain which is convenient for testing and interacting with your contracts during development. It ships with a helpful GUI that allows you to see available test accounts quickly, explore transactions, read logs, and see how much gas was consumed.

Configuration for the Ganache network:

module.exports = {
  networks: {
    ganache: {
      host: "127.0.0.1",
      port: 7545,
      network_id: "*" // matching any id
    }
  }
};

Deploy to the Ganache network:

truffle migrate --network ganache

Geth

Geth is the command line interface for running an Ethereum node. We need a synced node to be able to deploy smart contracts unless we do want to use a third-party provider like Infura (I am going to cover deploying to Infura later in that article).

I recommend you to create a deterministic wallet you can then use to switch between different networks quickly. You create one using MetaMask.

Save a private key in a keyfile and import it.

geth account import <keyfile>

You will be prompted for a passphrase. Remember it as you need to use it to unlock your account later.

Run a node connected to Ropsten and specify the address of the default (first) account to unlock. You will be prompted for a passphrase.

geth --unlock <account> --testnet --syncmode fast --cache 1024 --rpc

To check the progress of node syncing connect to locally running geth:

geth attach http://127.0.0.1:8545

Paste the following snippet to track the syncing progress:

setInterval(function () { console.log("Blocks: " + (web3.eth.syncing.currentBlock * 100 / web3.eth.syncing.highestBlock).toFixed(4) + "%") }, 1000)

Deploy to Ropsten

Configuration for the Ropsten network:

module.exports = {
  networks: {
    ropsten: {
      host: "127.0.0.1",
      port: 8545,
      network_id: 3,
      gas: 4700000
    },
  }
};

Deploy to the Ropsten network:

truffle migrate --network ropsten

Potential problems

If you forget to unlock your account migration will fail.

Error: authentication needed: password or unlock

Default gas limit in Truffle is 4712388 (gas limit for Homestead release). This exceeds Ropsten's limit. Make sure to set a gas limit to 4700000. Otherwise, you will see the following error.

Error: exceeds block gas limit

It might also be the case that your contract is too big to fit a single transaction. In such case, you have to split it into smaller ones.

You can use transaction hashes printed during migration to explore transactions on Etherscan. This was my Migration contract deployment: 0x68e5c28fec7846…

Deploy to Rinkeby

Rinkeby network is available only using geth client.

geth --unlock <account> --rinkeby --syncmode fast --cache 1024 --rpc

Configuration for the Rinkeby network:

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

Deploy to the Rinkeby network:

truffle migrate --network rinkeby

Parity

Parity is a different Ethereum client. Similarly to Geth, it has a command line interface which mirrors Geth's commands. Additionally, it has a web-based interface.

To import an account into Parity, we can use a keystore we already created with Geth.

parity account import <keystore>

The path to the keystore depends on your operating system. You can find keystores organized by the network:

  • Mac: ~/Library/Ethereum
  • Linux: ~/.ethereum
  • Windows: %APPDATA%\Ethereum

Deploy to Kovan

Kovan network is available only using Parity client. Gas limit at the time of writing is 4704584. Start Parity connected to Kovan chain.

parity ui --chain kovan

Configuration for the Kovan network:

module.exports = {
  networks: {
    kovan: {
      host: "127.0.0.1",
      port: 8545,
      network_id: 42,
      gas: 4700000
    },
  }
};

Deploy to the Kovan network:

truffle migrate --network kovan

During deployment, you will be asked a few times to enter a password.

Deploy with Infura

Infura by ConsenSys is an infrastructure which provides you with access to a few Ethereum networks and IPFS. You can use Infura to deploy smart contracts to mainnet as well as Ropsten, Rinkeby, and Kovan. It does not require you to have a synced node running locally.

Apart from host/port configuration truffle allows for configuring a network to use a custom provider. We can use HDWalletProvider to connect to Ropsten. It does not matter which network you choose as long as you own Ether necessary to pay a for a transaction.

const HDWalletProvider = require("truffle-hdwallet-provider");

module.exports = {
  networks: {
    "ropsten-infura": {
      provider: () => new HDWalletProvider("<passphrase>", "https://ropsten.infura.io/<key>"),
      network_id: 3,
      gas: 4700000
    }
  }
};

You can obtain a key by signing up on Infura's website. It is free of charge. At the time of writing, you might omit the key, but your requests can be affected by more restrictive throttling.

You, of course, do not want to keep your passphrase and key in the repository. I recommend you to use the environmental variable for that and maybe dotenv to save you some keystrokes during development.

Conclusion

Truffle is not the only way to deploy a smart contract. You can also use Remix IDE or Mist. You definitely would like to give Remix a try.

Doing deployment for the first time might be annoying and time-consuming. Waiting for a node to sync for sure is. You probably stumble upon some issues I did not explain here. You may always try to ask a question on Truffle gitter if StackOverflow did not provide you with a solution.

Further reading:

Photo by Cytonn Photography on Unsplash.