Deploy contracts to a public network

Deploy contracts to a public network

Note: To deploy OpenZeppelin SDK upgradeable contracts to a public network please use Deploy your contracts to a public network

This guide deploys a simple ERC20 token using OpenZeppelin Contracts.

We will use the Ropsten public test network for this guide, though you could use other public test networks.

Ethereum public test networks include:

  • Ropsten (Proof of Work, multi-client, ~ 15 second blocktimes);
  • Rinkeby (Proof of Authority, geth client only, 15 second blocktimes);
  • Kovan (Proof of Authority, parity client only, 4 second blocktimes);
  • Goerli (Proof of Authority, multi-client, 15 second blocktimes)

Setup

We’ll first create a node.js project in a new directory. Head over to a terminal and run:

mkdir my-project
cd my-project
npm init -y

Then install Truffle locally

Note: This guide uses npx to run npm packages locally, though you could install packages such as Truffle globally.

npm i truffle

Let’s now initialize a Truffle project. We will be advised that the directory is non-empty, we want to proceed anyway, so press Y for Yes.

npx truffle init

Install OpenZeppelin Contracts as we are using this for our token

npm i @openzeppelin/contracts

Create Contract

We will create an ERC20 token. See the OpenZeppelin ERC20 documentation for more details.

Save the following simple ERC20 contract to SimpleToken.sol in the contracts directory.

pragma solidity ^0.5.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol";

/**
 * @title SimpleToken
 * @dev Very simple ERC20 Token example, where all tokens are pre-assigned to the creator.
 * Note they can later distribute these tokens as they wish using `transfer` and other
 * `ERC20` functions.
 */
contract SimpleToken is ERC20, ERC20Detailed {

    /**
     * @dev Constructor that gives msg.sender all of existing tokens.
     */
    constructor () public ERC20Detailed("SimpleToken", "SIM", 18) {
        _mint(msg.sender, 1000 * (10 ** uint256(decimals())));
    }
}

Create Migrations

Save the following migrations script as 2_deploy_token.js in the migrations directory.

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

module.exports = function(deployer) {
  deployer.deploy(SimpleToken);
};

Test account

We use mnemonics to generate a 12 word mnemonic. Run the following to generate a mnemonic for testing purposes:

npx mnemonics

Create Infura account

We will use Infura to interact with public Ethereum nodes (if you are not running your own nodes). Infura Core is free to use. Follow the instructions on the Infura website to sign up and create a new project.

Install dotenv

We will use dotenv to store your Infura Project ID and your development mnemonic. Run the following in your project folder to install as a development dependency:

$ npm install --save-dev dotenv

Configure .gitignore

We configure .gitignore to ensure that values in .env (Infura Project ID and development mnemonic) don’t get accidentally committed to our source code repository. Create a .gitignore file in your project folder with the following contents:

# Dependency directory
node_modules

# local env variables
.env

# truffle build directory
build

Configure .env

We configure .env to store our Infura Project ID and development Mnemonic used for testing. Create a .env file in your project folder with the following contents, using the Infura Project ID and the development Mnemonic you created earlier:

INFURA_PROJECT_ID="ENTER INFURA PROJECT ID"
DEV_MNEMONIC="ENTER 12 WORD SEED PHRASE"

Install HD Wallet Provider

We will use @truffle/hdwallet-provider to sign transactions for addresses derived from a 12 or 24 word mnemonic. Run the following in your project folder to install as a development dependency:

$ npm install --save-dev @truffle/hdwallet-provider

Configure truffle-config.js

Update truffle-config.js to configure additional networks to connect to. We require dotenv and @truffle/hdwallet-provider and then specify the provider for the network using the Infura Project ID and development mnemonic. Your truffle-config.js should look like the following:

require('dotenv').config();

const HDWalletProvider = require('@truffle/hdwallet-provider');
const infuraProjectId = process.env.INFURA_PROJECT_ID;

module.exports = {
  /**
   * Networks define how you connect to your ethereum client and let you set the
   * defaults web3 uses to send transactions. If you don't specify one truffle
   * will spin up a development blockchain for you on port 9545 when you
   * run `develop` or `test`. You can ask a truffle command to use a specific
   * network from the command line, e.g
   *
   * $ truffle test --network <network-name>
   */

  networks: {
    // Useful for testing. The `development` name is special - truffle uses it by default
    // if it's defined here and no other network is specified at the command line.
    // You should run a client (like ganache-cli, geth or parity) in a separate terminal
    // tab if you use this network and you must also set the `host`, `port` and `network_id`
    // options below to some value.
    //
    development: {
     host: "127.0.0.1",     // Localhost (default: none)
     port: 8545,            // Standard Ethereum port (default: none)
     network_id: "*",       // Any network (default: none)
    },

    // Another network with more advanced options...
    // advanced: {
      // port: 8777,             // Custom port
      // network_id: 1342,       // Custom network
      // gas: 8500000,           // Gas sent with each transaction (default: ~6700000)
      // gasPrice: 20000000000,  // 20 gwei (in wei) (default: 100 gwei)
      // from: <address>,        // Account to send txs from (default: accounts[0])
      // websockets: true        // Enable EventEmitter interface for web3 (default: false)
    // },

    // Useful for deploying to a public network.
    // NB: It's important to wrap the provider as a function.
    ropsten: {
      provider: () => new HDWalletProvider(process.env.DEV_MNEMONIC, "https://ropsten.infura.io/v3/" + infuraProjectId),
      network_id: 3,       // Ropsten's id
      gas: 5500000,        // Ropsten has a lower block limit than mainnet
      confirmations: 2,    // # of confs to wait between deployments. (default: 0)
      timeoutBlocks: 200,  // # of blocks before a deployment times out  (minimum/default: 50)
      skipDryRun: true     // Skip dry run before migrations? (default: false for public nets )
    },

    // Useful for private networks
    // private: {
      // provider: () => new HDWalletProvider(mnemonic, `https://network.io`),
      // network_id: 2111,   // This network is yours, in the cloud.
      // production: true    // Treats this network as if it was a public net. (default: false)
    // }
  },

  // Set default mocha options here, use special reporters etc.
  mocha: {
    // timeout: 100000
  },

  // Configure your compilers
  compilers: {
    solc: {
      // version: "0.5.1",    // Fetch exact version from solc-bin (default: truffle's version)
      // docker: true,        // Use "0.5.1" you've installed locally with docker (default: false)
      // settings: {          // See the solidity docs for advice about optimization and evmVersion
      //  optimizer: {
      //    enabled: false,
      //    runs: 200
      //  },
      //  evmVersion: "byzantium"
      // }
    }
  }
}

If you want to use an account other than the first account generated by the mnemonic then you can provide the account index (zero based) as the third parameter to HDWalletProvider. See the truffle tutorial for details: https://www.trufflesuite.com/tutorials/using-infura-custom-provider

To expose additional addresses from the same mnemonic set num_addresses in your config. To change the derivation path to derive addresses set wallet_hdpath in your config. See the @truffle/hdwallet-provider repository for details.

Fund test account

Add test Ether to the account that you want to use e.g. the default/first account derived from the mnemonic. You can add funds using a faucet (e.g. https://faucet.ropsten.be). If the account doesn’t have enough test Ether when you attempt to deploy, you will get the error deployment failed with error: insufficient funds for gas * price + value.

You can also import the 12 word seed phrase into MetaMask and then you can request test Ether from the MetaMask faucet. (https://faucet.metamask.io)

Deploy contract

We deploy our contract using Truffle by running truffle migrate. Specify the network you want to use.

$ npx truffle migrate --network ropsten

Interact

We can interact with our contract using Truffle Console.

$ truffle console --network ropsten
truffle(ropsten)> token = await SimpleToken.deployed()
undefined
truffle(ropsten)> await token.name()
'SimpleToken'
truffle(ropsten)> (await token.totalSupply()).toString()
'1000000000000000000000'

View your transactions on a blockchain explorer

You can view your transactions on a blockchain explorer that supports the network you used. For instance, Etherscan supports Ropsten at https://ropsten.etherscan.io/. You can search Etherscan using the contract address.

You can also verify your smart contract on Etherscan using the following:

My contract can be found here:
https://ropsten.etherscan.io/address/0xF2413EF167058A730D76aAbEA066dd420F6A2AB6#code

That’s it! You now know how to deploy a simple ERC20 token to a public network and interact with it.

1 Like

Deploying to mainnet

Appropriately tested and audited

First ensure that your smart contracts are appropriately tested and audited.

Regards testing, the testing guide is a good place to start: Test smart contracts like a rockstar

Prior to an audit it is worth going through the checklist before an audit.

To organize an audit, you would need to engage a third party auditor such as OpenZeppelin, see https://openzeppelin.com/security-audits/ for details.

1 Like

Wow very good tutorial!

What code I have to add in truffle-js if I want to deploy to mainnet?

Than you!

1 Like

Hi @Michael,

Ensure that your smart contracts are appropriately tested and audited.

Calculate the cost of deployment based on the gas used (as per deployment to public testnets), gas price and current price of Ethereum.

Configure .env

Add a mnemonic for mainnet.
Recommend that this is not the same as used in development/testing. Ensure this mnemonic is not stored in version control (e.g. git) and is not shared in any way.

MAINNET_MNEMONIC="ENTER 12 WORD SEED PHRASE"

Add mainnet totruffle-config.js

Add mainnet network to truffle-config.js
Set the gas limit, based on deployment to public testnets
Set the gas price, check https://ethgasstation.info

    mainnet: {
      provider: () => new HDWalletProvider(process.env.MAINNET_MNEMONIC, "https://mainnet.infura.io/v3/" + infuraProjectId),
      network_id: 1,       // mainnet
      gas: 5500000,  
      gasPrice: 2000000000,  // check https://ethgasstation.info/
      confirmations: 2,    // # of confs to wait between deployments. (default: 0)
      timeoutBlocks: 200,  // # of blocks before a deployment times out  (minimum/default: 50)
      skipDryRun: false     // Skip dry run before migrations? (default: false for public nets )
    },

Fund account

Add required amount of Ether to the account that you want to use e.g. the default/first account derived from the mnemonic. Suggest not to have more Ether than required in case of human error with gas price.

Deploy contract

We deploy our contract using Truffle by running truffle migrate . Specify mainnet as the network you want to use.

Perform a dry run only first

truffle migrate --network mainnet --dry-run

Deploy

$ npx truffle migrate --network mainnet

Then check on Etherscan.
Interact using Truffle console
Verify contract on Etherscan
Ensure mainnet mnemonic appropriately secured.

1 Like