How I deployed a dapp on Ropsten from an EC2 instance (without using Infura)

This is a fairly technical article that I want to publish to help fellow developers with an important part of dApp development: deployment!

This is not the usual spoon-feeding-style step-by-step programming/tech tutorial. I did include as much details and commands as I could, but depending on your skill level you might need to pause and do some extra googling.

I learned the Truffle Framework a few months ago and started playing around with it, then a month ago I decided to develop my first complete dApp, a decentralized file validator (more info on that on another article when I’ll finish it).

After getting to a fairly good working prototype I wanted to deploy it to the live internet, so I could reach it from any browser with MetaMask installed.

Everywhere I read on the web, people ALWAYS gave the same damn solution: use the infura.io service! Just paste this code here, put this API key there, and voila’, you are done! Your dapp is deployed! Easy!

The problem with this approach is that Infura charges a 50$ monthly subscription for having more than 3 projects and… I didn’t like that at all! the blockchain is distributed and public, therefore I should be able to deploy my damn dApp from my own device without paying a recurring subscription no?!

So I did pretend that infura didn’t exist at all, which means I had to figure out a way to run a full node. I chose to do this with an AWS EC2 instance because I did not want to run a full node on my laptop. Paying an EC2 instance just for a few hours (enough to sync the blockchain and deploy contracts) is way cheaper and less limiting than infura.

I was going to deploy to the Ropsten Testnet instead of mainnet for 3 reasons:

  • The dApp wasn’t complete for public release yet, so no point in deploying it to mainnet and spend real ethers to do so.
  • It was my first dApp deployment outside of the local development testrpc/ganache kind of blockchains, so I knew I would have fucked something up (and in fact I did!!).
  • Again, no point in spending real ether for testing an incomplete dApp

I Created a t2.large instance and immediately installed node, npm, truffle and parity. Smaller EC2 instances can be probably used too, but haven’t tried so I cannot say for sure. Keep in mind that you can start and stop instances whenever you need them to save money.

I ran parity on Ropsten testnet with this command parity --chain ropsten --jsonrpc-apis web3,rpc,personal,parity_accounts,eth,net,parity,parity_set,signer which started the full node with an open RPC port on localhost on port 8545.

[!] Please note that the server ONLY accepted an ssh connections from my local IP and no others. Make sure the RPC port cannot be reached from the outside.

So I had a full node running on my EC2 instance while parity was running and syncing with the ropsten blockchain. How could I check the progress on syncing? I had to connect to a web3 console and ask my node.

I went back to my laptop just to add this to the truffle-config.js file:

...
ropsten: {
  host: "127.0.0.1",
  port: 8545,
  network_id: 3,
  gas: 4700000,
  from: "0x8874d4a8296fc415d0d8289b6d0682c964b78668"
},
...

(Note that the value in the from: field is the account that will deploy the contract, you might not have one now, so make sure you update it later.)

Then I pushed changes and did a git clone into the EC2 server to bring this repo into the cloud instance where parity is already running.

I entered the repository’s root directory (where the truffle-config.js file is) and executed this command in the terminal: truffle console --network ropsten.

This opened up a truffle console since my parity node was running fine the prompt should say: truffle(ropsten)>. (If the parity node was not running, I would have gotten an error)

I checked the progress on syncing with web3.eth.isSyncing(). Which returned something like this:

{
  currentBlock: 0,
  highestBlock: 6605903,
  startingBlock: 0,
  warpChunksAmount: '0xd0e',
  warpChunksProcessed: '0x8c'
}

I knew when the syncing was over becausefalse was the output of the `web3.eth.isSyncing()`command, which indicates that the ehtereum node is synced, and therefore ready to deployed contracts.

When the node was synced and ready I created an account with web3.eth.personal.newAccount('write_password_here') and proceeded with funding that account with testnet ether (this account is the one that goes into the from: field of the truffle-config js file, in the ropsten network section we created earlier). I used this faucet to fund the account: https://faucet.ropsten.be

To check an account balance use this command in the console: web3.eth.getBalance('0x123_YOUR_ADDRESS_HERE'). It doesn’t return ethers but wei, so make sure you do the conversion.

Last thing that the account needs before being able to deploy is to be unlocked. To unlock the account I used truffle web3.eth.personal.unlockAccount( accounts[0], 'your_account_password_here').

The account was finally loaded with some testnet ethers and unlocked, therefore ready to deploy contracts.

Still in the console, I ran compile to compile the solidity contracts and then for some reason migrate wound not hang at some point, so I had to execute migrate -f 2 instead, where 2 is the number of my migration. To find the migration number I looked at the name of the migration js file I wanted to use, mine was migrations/2_deploy_contracts.js so I put 2 in the migrate command.

Everything went well and I had an output like this one:

$ truffle(ropsten)> migrate -f 2

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.



Migrations dry-run (simulation)
===============================
> Network name:    'ropsten-fork'
> Network id:      3
> Block gas limit: 0x7a121d


2_deploy_contracts.js
=====================

   Deploying 'Authenticity'
   ------------------------
   > block number:        6604876
   > block timestamp:     1571514628
   > account:             0x8874d4a8296FC415D0D8289B6d0682C964b78668
   > balance:             1.586563006
   > gas used:            553847
   > gas price:           2 gwei
   > value sent:          0 ETH
   > total cost:          0.001107694 ETH

   -------------------------------------
   > Total cost:         0.001107694 ETH


Summary
=======
> Total deployments:   1
> Final cost:          0.001107694 ETH





Starting migrations...
======================
> Network name:    'ropsten'
> Network id:      3
> Block gas limit: 0x7a121d


2_deploy_contracts.js
=====================

   Deploying 'Authenticity'
   ------------------------
   > transaction hash:    0x5d6b6471922c8d2161f14dd65d5119e2ba580475e39d8392525389a10cef0eac
   > Blocks: 0            Seconds: 48
   > contract address:    0x03a54C1903827Bac1b3A3377928912d7E4f413aA
   > block number:        6604875
   > block timestamp:     1571514624
   > account:             0x8874d4a8296FC415D0D8289B6d0682C964b78668
   > balance:             1.57849488
   > gas used:            458791
   > gas price:           20 gwei
   > value sent:          0 ETH
   > total cost:          0.00917582 ETH

   > Saving artifacts
   -------------------------------------
   > Total cost:          0.00917582 ETH


Summary
=======
> Total deployments:   1
> Final cost:          0.00917582 ETH

I waited for a bit and checked on etherscan if the contracts were deployed succesfully.This is one of them https://ropsten.etherscan.io/address/0x03a54c1903827bac1b3a3377928912d7e4f413aa#code

At this point the contract (the app’s backend) was deployed and ready for interactions. I only had the React frontend left to deploy then the app would be fully live.

I did a git status on the server, and committed the files that changed inside client/src/ during the compilation and migration of the contracts. These files are important because they will allow the web3 library inside the frontend’s browser to “connect” to the smart contracts we just deployed and call their functions, etc.

I went into the frontend directory client/, installed the dependencies with npm install and finally ran npm run build which outputs a static deployment-ready version of our React frontend in the build/ folder.

I deployed the React frontend with Amazon S3 but any CDN is fine, I picked a CDN because they are generally safer than a server.

Visited the page with MetaMask connected to Ropsten, and the app loaded perfectly fine and looked just like when it was on the local truffle develop testnet.

Mission complete!

Deploy on mainnet

I haven’t deployed the dapp on the mainnet yet, I still have a few more features to implement… I bet that it must be a similar procedure as deploying on testnet, but with these key differences:

  • Parity needs to run a mainnet node so do not include --chain ropsten in the initial command.
  • The truffle-config.js file needs to have a network config for mainnet.
  • Real ether need to be sent to the account that will deploy the contracts

Thank you for reading this! Hopefully it helped you deploy your amazing dApp project without too many headaches. Keep in mind that ethereum changes often so some commands/concepts in this article might not work in a year or two.. In these cases leave a comment!

You can leave a comment also if you have any doubts or questions, or want to add something to this topic. Did you ever deploy a dApp? Did you use the same method or a different one?

One thought on “How I deployed a dapp on Ropsten from an EC2 instance (without using Infura)”

Comments are closed.