Deploy to a public testnet using OpenZeppelin SDK

Deploy your contracts to a public network

This guide builds on the first tutorial where we created a new OpenZeppelin SDK project, and created a simple Counter contract in a local development network. We will now see how to deploy this contract to a public network.

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

NOTE: 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 will use the my-project project folder we created earlier.

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 networks.js

Update networks.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 networks.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: {
    development: {
      protocol: 'http',
      host: 'localhost',
      port: 8545,
      gas: 5000000,
      gasPrice: 5e9,
      networkId: '*',
    },
    ropsten: {
      provider: () => new HDWalletProvider(process.env.DEV_MNEMONIC, "https://ropsten.infura.io/v3/" + infuraProjectId),
      networkId: 3,       // Ropsten's id
    },
  },
};

NOTE: 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

NOTE: 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

Use the interactive command openzeppelin accounts, selecting the desired network to view the configured accounts for that network:

$ npx openzeppelin accounts
? Pick a network ropsten
Accounts for ropsten:
Default: 0x581f96c12064e2dfb72E8B9722a18731D756Fe73
All:
- 0: 0x581f96c12064e2dfb72E8B9722a18731D756Fe73

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 OpenZeppelin SDK interactive commands by running openzeppelin create. Select the Counter contract, the ropsten network and press N for no to calling a function on the instance after creating it.

$ npx openzeppelin create
Nothing to compile, all contracts are up to date.
? Pick a contract to instantiate Counter
? Pick a network ropsten
✓ Contract Counter deployed
All contracts have been deployed
? Do you want to call a function on the instance after creating it? No
✓ Setting everything up to create contract instances
✓ Instance created at 0x584Fcb424b17d3505B21c881d57EF9Bf1B18c4A7
0x584Fcb424b17d3505B21c881d57EF9Bf1B18c4A7

Interact

We can send transactions to our contract using OpenZeppelin SDK interactive commands by running openzeppelin send-tx. Select the Counter contract, the ropsten network, the function to use and an amount to increase the Counter by. e.g. 23.

$ npx openzeppelin send-tx
? Pick a network ropsten
? Pick an instance Counter at 0x584Fcb424b17d3505B21c881d57EF9Bf1B18c4A7
? Select which function increase(amount: uint256)
? amount (uint256): 23
✓ Transaction successful. Transaction hash: 0x5f3449b06aee60146ccb3c63d4bdbc8f03bf9140ce9b23b51defe98e32b81a74

We can call functions on our contract using OpenZeppelin SDK interactive commands by running openzeppelin call. Select the Counter contract, the ropsten network and the function to call value().

$ npx openzeppelin call
? Pick a network ropsten
? Pick an instance Counter at 0x584Fcb424b17d3505B21c881d57EF9Bf1B18c4A7
? Select which function value()
✓ Method 'value()' returned: 23
23

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 of your instance of Counter, remember that the contract address is displayed during deployment and when interacting with it (Instance created at 0x...).


That’s it! You now know how to deploy an OpenZeppelin SDK contract to a public network and interact with it using OpenZeppelin SDK interactive commands.

3 Likes

Thank you very much for this excellent tutorial, @abcoathup.

However, something is odd with the account, that will be used for deployment.

I have a little example project with a simple Counter contract.

In my .env File, i use the following mnemonics:

tooth spike smile swarm adapt history motion salt outdoor document observe cloth

When I import this mnemonic into MyCrypto (selected Network: Ropsten), the very first address will be 0x9dbFAFB5Edd1D69aCaC1C104C526C4ACDc20F3Ae. I have sent 0.99 ETH onto this address.

zos balance will confirm:

$ zos balance --network ropsten 0x9dbFAFB5Edd1D69aCaC1C104C526C4ACDc20F3Ae
Balance: 0.99 ETH
990000000000000000

But zos push --network ropsten is still running out of Gas.

$ zos push --network ropsten
Nothing to compile, all contracts are up to date.
:heavy_multiplication_x: Validating and deploying contract Counter
Counter deployment failed with error: insufficient funds for gas * price + value

This is my network.config:

require('dotenv').config();

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

module.exports = {
  networks: {
    development: {
      protocol: 'http',
      host: 'localhost',
      port: 8545,
      gas: 5000000,
      gasPrice: 5e9,
      networkId: '*',
    },
    ropsten: {
      provider: () => new HDWalletProvider(process.env.DEV_MNEMONIC, "https://ropsten.infura.io/v3/" + infuraProjectId),
      networkId: 3,       // Ropsten's id
    },
  },
};

I don’t know, which address is used by zos create but it is definitely not this account that MyCrypto gives me as the very first address from these mnemonics.

How did you solved this?

1 Like

I have found out my issue!

It is caused by incompatibilities in the HD derivation paths between MyCrypto and truffle-hdwallet-provider.

When we import a mnemonic into MyCrypto for Ropsten-Network, it derives the address with the following path: m/44'/1'/0'/0, while truffle-hdwallet-provider constantely is using m/44'/60'/0'/0/ no matter wich network will be used.

Therefor, I get a different address as the first address as it will be used by truffle-hdwallet-provider.

So, when I use also m/44'/60'/0'/0/ for address derivation, I end up with the following address:

0xC0306fe5c3E90025AD56832A4399670be51b3Eec
instead of
0x9dbFAFB5Edd1D69aCaC1C104C526C4ACDc20F3Ae

After I have send Test-Ether to 0xC0306fe5c3E90025AD56832A4399670be51b3Eec on Ropsten, the deployment works fine.

Fortunately the constructor of truffle-hdwallet-provider offers an argument containing the wallet_hdpath. So for using Ropster addresses properly with MyCrypto / MyEtherWallet, the following constructor helps:

ropsten: {
  provider: () => new HDWalletProvider(
    process.env.DEV_MNEMONIC, "https://ropsten.infura.io/v3/" + infuraProjectId, 
        0, 1, true, "m/44'/1'/0'/0/"
    ),
  networkId: 3,       // Ropsten's id
},

0 = start with first address
1 = derive only 1 addresses
true = sharedNonce (default)

“m/44’/1’/0’/0/” = BIP44 derivation path that is used for test networks (across all coins)

For debugging purposes, we can find out the address with the method “getAddresses()”:

const x = new HDWalletProvider(
  process.env.DEV_MNEMONIC, "https://ropsten.infura.io/v3/" + infuraProjectId, 0, 1,
  true, "m/44'/1'/0'/0/"
  )

console.log(x.getAddresses())
3 Likes

Awesome @itinance
Glad you could resolve. truffle-hdwallet-provider and MetaMask use the same derivation path so I hadn’t run into this issue.

1 Like

I have updated the guide to the latest OpenZeppelin SDK including openzeppelin accounts and use npx mnemonics from @itinance.

Thanks @itinance for creating mnemonics :pray:

1 Like

Thanks for mentioning @abcoathup :slight_smile:

Thank you @itinance for creating the package. It was exactly what was needed for this workflow and guide.

2 posts were split to a new topic: When using OpenZeppelin SDK how can you use truffle console?