Building a Web3 Dapp (Part 1) — Lessons Learned

Tobias Fan
Coinmonks
7 min readNov 12, 2021

--

This is Part 1 of of a 4-part article series on ScratchCollective.app — a Dapp I built for educational purposes. For those who haven’t read the intro, ScratchCollective is an AI Art Generation & NFT minting tool. It provides an easy to use means of leveraging AI to create a neural style transfer image and mint the result as an NFT.

In this article I go over a few specific challenges that I faced building the app. Throughout the process, it was generally pretty easy to find resources on the basics and on commonly faced issues. What I found more difficult were specific, niche problems which took a bit more creativity and elbow grease to overcome. I cover these problems here with the hope that others might be able to use the solutions found here to their own advantage.

Here are (just a few) of the struggles I faced:

  • Getting the Nodejs to correctly spawn my python child process
  • The cost of deploying a smart contract to the mainnet
  • Deploying Tensorflow to Heroku
  • A method to to check if user’s active account in Metamask is connected to application

1. Getting Nodejs to correctly spawn my python child process

In ScratchCollective, I spawn a Nodejs child process in one of my server routes to call the AI-Generation portion of the app (which is written in Python). My problem was getting the child process to call my Tensorflow script in the first place. Node child_process’ spawn() method takes a command to run and some optional arguments — in this case, calling the “Python” command on a styletransfer.py script. I found that I was calling “Python” incorrectly — locally I needed to specify the full path to the Python executable, and in production (i.e. on a hosted server) you’d have to specify the path relative to that server’s configuration. What threw me off was specifying the wrong Python executable — locally, the path I needed was the full path to the executable in my virtual environment . In production servers (Heroku specifically) — I thought calling the PYTHON_PATH environment variable, automatically created by Heroku with the Python buildpack, would work. However, I figured out that I needed the full path here as well so I created by own environment variable pointing to the path ‘/app/.herok`u/python/bin/python’. It took me a while but I finally got it to work.

Heroku Config Variables

2. The cost of deploying a smart contract to the mainnet

The cost to deploy a smart contract to the mainnet ended up being prohibitively expensive. I had budgeted about $300 dollars to deploy the contract (lol) and initially thought I had written atrocious Solidity in my ERC-1155 and something was wrong. So I did a few tests via Truffle/Ganache to see how far off I was, and no… I deployed OpenZeppelin’s ERC721Mintable, ERC20MinterPauser, ERC1155MinterPauser, and even the BoredApes (NFT) smart contract on a Ganache instance to test the gas expenditure at different gas prices (based on ethgasstation.info). At the time of testing, deployment ranged from $400 — $2000, with the ERC-20MinterPauser being the cheapest at the lower range of recommended gas price (i.e. slower transaction) and the highest being Bored Ape’s at $2000. For those curious into the specifics, I plan on writing about gas fee calculation and optimizing your smart contract to minimize fees in the future. What I did in the end was grab a bunch of test ETH from Rinkeby, Ropsten, and Goerli faucets (free “ETH”!!) and deployed my contracts there. Long story short, the reason the app is only live on test networks and not the mainnet is because I decided to use that money for rent and groceries.

3. Deploying Tensorflow to Heroku

One of the main application functions includes running two images through a Python script that uses Tensorflow and an Arbitrary Image Stylization Model to produce the “AI-Generated image”. This is the python child process mentioned above, and it became a two-part deployment problem. One issue was the size of the Tensorflow module. The other issue was the amount of memory (RAM) required to run the script. In retrospect, I probably shouldn’t have used Heroku — instead maybe opting for Digital Ocean or AWS. But hindsight is 20/20, and I don’t find deployment very fun so I stuck it out with Heroku.

Heroku only allows for a 500mb slug size, which is essentially the size of your compressed, pre-packaged application on the dyno (server). When I originally deployed the application, I was requiring the Tensorflow module which was nearly 500mb in itself. Each time I tried to deploy, I’d get an error saying I had surpassed the limit. Very frustrating. Eventually I found that the solution was changing the module from Tensorflow to Tensorflow-cpu. The original tensorflow module includes GPU support, but ultimately isn't needed and is skipped by the module anyways if not present. Find out more here: https://stackoverflow.com/questions/61062303/deploy-python-app-to-heroku-slug-size-too-large

Another issue I faced was memory usage being surpassed. Again, Heroku free tier dynos limit your RAM usage to 500mb here. Tensorflow loads your model (in this case the Arbitrary Stylization Model) into memory before processing — which for the most part is the entire RAM allocation. There really isn’t a workaround here besides increasing the RAM limit, so I forked over some beer money to increase the limit to 1GB, but will soon be looking into redeploying on Digital Ocean or AWS :/

4. A method to to CHECK if user’s Metamask wallet is connected to application

In order for your Ethereum wallet to interact with a Dapp, the wallet account must be connected to the dapp / website. In order to be connected, the user (you) must approve account access (EIP 1102). Since my aim was to implement an automated authentication process, I needed a method to automatically check if the user had connected their wallet to the app, and prompt them to do so (i.e. request permission) if they aren’t. This isn’t a built-in method in the Ethereum Provider API, which providers the browser methods of interacting and listening to the Ethereum blockchain (more on the Ethereum provider here). There is a confusingly similarly named method — ethereum.isConnected() that has nothing to do with the user’s accounts, rather it is only a boolean for whether or not the provider can make RPC requests to the current chain (again nothing to do with the accounts).

So in order to make this work, I had to create my own function. My logic was that if a user’s accounts were connected — calling web3.eth.getAccounts() would return a list of accounts associated with the mnemonic (in the case of metamask, this only returns the first address in the list). If a user wasn’t connected, it would return an empty list. This ended up being my working function:

These were just a few of the challenges I faced while building the app. The important thing to keep in mind here is that this is a fairly new space. Many of the issues are not well documented and furthermore — yet to be uncovered. Languages and frameworks that have been utilized for a while by a large community are likely to have more accessible solutions. So be prepared to do a bit of tinkering on your own when working with Web 3.0!

Join Coinmonks Telegram Channel and Youtube Channel learn about crypto trading and investing

Also Read

--

--

Tobias Fan
Coinmonks

Cryptos Biggest Fan | Defi + Web3 @LunarCrush