IPO Technical Review

How the IPO team built their Ethereum based pixel economy and marketplace.

Background

We were mere hours away from daybreak during a Redbull-fueled session of product strategy and brainstorming on our 25' whiteboard wall, when our team stumbled upon, what we assumed, was a brilliant blockchain project idea.

We have always had a cautiously enthusiastic interest in cryptocurrency and blockchain technology. Members of the team have dabbled in mining, trading, speculating, and even worked for Blockchain startups; however, we had yet to create a Blockchain product together as a team.

We decided the best way to get up to speed was to perform an internal weekend hackathon to build a fully functional project.

Flash forward 2 weeks…

A Few Things We Learned

  1. It’s easy to underestimate the time required to build a Web3 / Solidity based product. Teams should be prepared to run into more unforeseen hurdles than a typical software project.
  2. The Ethereum development tools and ecosystem is still in its infancy. Documentation across the board could use some TLC. It is outdated, contains old pending Github issues, and in some areas it feels like important rudimentary functionality has been left out.
  3. Developing contracts requires more of an emphasis of space vs. time then most developers are familiar with coming from the modern computing world — it becomes vital to keep track of max stack size, # of instructions, and gas estimates as you write your contract code.
  4. Storing data on-chain is nearly impossible, even if it was, it’s not a good idea.
  5. Testing a dApp in the various platforms that support web3 (Metamask, Mist, Parity, etc.) is almost more painful than testing IE8.
  6. dApps are really rad (when everything is working properly)…
  7. however, the flood of new projects, media coverage, and ICO investment hype is giving Ethereum a lot to live up to.

To be fair — many of the above items listed are simply the nature of working on an emerging technology. The individuals and organizations who have contributed to the Ethereum ecosystem have done a fantastic job so far.


Architecture

Building a dApp requires a lot of moving parts, especially when you are having to mix a number of modern frameworks and platforms with dApp technologies. There are several reasons we were forced to mix centralized + decentralized technologies, which we discuss in more detail below.

Geth, TestRPC, & Kovan

  • Wallet management for testing is difficult if you are using anything other than Truffle, unfortunately testrpc was too slow for certain cases (Specifically when executing 100 consecutive JSON-RPC calls to re-build our map). So we were forced to use a custom Geth node, especially for in-house testing.
  • Running Geth in Docker is not possible due to the inability to use a custom genesis block, so we couldn’t just execute our clean docker-compose up to setup our environments like we were hoping.
  • Kovan was lightning fast and a completely inaccurate depiction of how our app would perform on the big boy chain.
  • Gas estimates are entirely different across each RPC implementation.

Laravel & Mysql

This is a dApp… Isn’t that sacrilegious?! Yes, we think so too, unfortunately, our master plan of storing image data on-chain was simply not feasible due to gas limits. We also decided it would be best to not contribute to Ethereum bloat. If you are still interested, we go into detail regarding our on-chain storage methodology below.

We felt Laravel was an easy way to quickly setup some API endpoints for generating our map, retrieving our image data, and handling a few other misc. features.

Image CDN Cache

Several of our engineers have learned things the hard way in the past, by not preparing for the (highly unlikely) event that the project will go viral, so we decided to setup a geo load balanced CDN to serve our images just in-case. We did this by using Route53 for DNS geo load balancing in conjunction with some Digital Ocean nodes in several availability zones.


The IPO Contract

Overview

In conjunction with the functionality required to make our dApp work, we decided to implement the ERC20 standard as close as possible. This means that when you buy a section on the site, you are given an “IPO” token. As the project matures and (hopefully) gains traction, we plan to use these tokens for future easter eggs.

We learned a few key things while familiarizing ourselves with Solidity development:

  1. Testing contracts is fairly difficult— Truffle makes this less painful with a really awesome unit testing framework, however, we were still forced to manually define “debugging” events that we later removed, due to the inability to easily catch throws. Unit tests aside, we also had three different engineers read through the contract several times and compare and contrast notes for security auditing.
  2. On-chain storage limitations– we go into detail on this below.
  3. It’s difficult to work with arrays for various reasons– specifically, you can’t return an entire array, but have to call each index from web3. This makes looping or returning an entire array of any significant size (10,000 indexes for our map) on the frontend practically impossible.
  4. Wrestling the EVM stack can be tough: we originally had convenient helper methods in order to attempt to implement DRY, however, we realized that with the jumpdest operation this was taking up too much of the stack. Not using helper functions saved on gas and cut down on space. Which became necessary in a couple of our methods where we hit max stack.

Image Storage

The original plan for image storage was to store the entire image in the contract. We felt like this would be a cool way to build a completely decentralized “pixel marketplace” but we ultimately decided not to do this for several reasons.

  1. Storing a bitmap on-chain was too expensive from an instructions/gas perspective.
  2. A lack of variadic argument support in the version of Solidity we were developing in would either require multiple function definitions for users to call based on the number of spaces they were spaces they were looking to update or to call one update function per space which would be overly cumbersome.
  3. We felt it was a bad idea to add additional bloat to the blockchain, as data storage does not fit the Ethereum use-case very well.
  4. We looked into alternative solutions, such as IPFS, and none of them were fast/efficient enough in their current state for our specific use case.

Backend

Storing Images

Instead of storing images on-chain, we decided to hash the image data, break images up into 20x20 pixel sections and store references to this information in a simple Mysql database instance.

We were hoping to use the md5 hashing for image verification, however, we were forced to crop, flatten alpha channels, and add meta data to the md5 — so our md5 is now simply an internal UUID. Fortunately, this is still verifiable by hitting our image CDN endpoint and comparing it to the data in the contract, since the contract data is only mutable via the original owner.

Building the map

The IPO map builder has two main goals :

  1. Reduce the number of times image files are loaded from disk to reduce load on the NFS servers.
  2. Be capable of handling multiple uploads of the same image (image collisions) with different hover text and links.

Currently, one processing daemon generates a new map approximately once every 2 minutes, this reduces load on the CDN, processing servers, and client storage while still providing users with visible results in a reasonable amount of time.

The full map begins as a blank 2000 by 2000 pixel Bitmap and PNG stored on an NFS server within the datacenter to be served by the CDN. The map file is stored with the map ID (the block number at time of generation) within its filename so they can be fetched without having to browse the entire directory. Bitmap prevents any image loss based on future compression and PNG reduces load on the CDN and end user devices.

Meta data such as hover text and link text are stored in a JSON file with the same ID as the map image so the frontend can infer the filename without having direct access to the storage. This meta data includes a mapping between section number and its meta data to assist the frontend in building the overlay grid and drawing hover tool tips without having to query the blockchain. This reduces network load on users with high latency or low bandwidth connections. Before a purchase, chain data is used as the authoritative source via web3 rather than the metadata file in order to reduce the chances of a double spend attempt due to latency in the building of the meta data file.

  1. Using JSON-RPC we fetch all non-empty Sections from the contract via a redundant pair of Ethereum full nodes. For testing, we were running into timeouts fairly consistently, so we were basically forced to wrap each RPC call in a while(true) until it responded properly.
  2. With contract section data in-memory, we iterate through the sections until a new image identifier is discovered.
  3. Each section is a struct with meta data that we use to retrieve additional meta data (like hover text and links) from the Mysql database.
  4. Using image meta data, the script iterates through the sections based on the height and width of the image that the first encountered section is set to. If those sections are set to the same image, the slice that corresponds to that space will be placed on the new map image. This causes each source image to only be loaded into memory once, which significantly improves the efficiency of building the map.

Frontend

The frontend was built with brevity in mind. We wanted to use Truffle, however, the React box felt like overkill for our purposes. We decided a balance of Truffle for contract unit tests, migrations, and contract interactions along with basic jQuery + Rivets.js for data binding and DOM updates would suffice.

We also built the grid interactions, animations, and functionality from scratch on HTML5 canvas, as there was nothing else like it. This wasn’t trivial when you take into account scaling the image for the browser window size and attempting to create intuitive UI interactions with the grid.

Lastly, for analytics / tracking purposes we went with Google Analytics (with custom events for most UI interactions) and Hotjar.

Here are a few key things we learned about building on Web3:

  1. We started out by using floating points for Ether calculations, we quickly realized floating point math is the devil, and switched to doing integers with precision calculations. Once again, this was a bad idea because we are dealing with numbers where javascript hits max int. This was an amateur mistake, but most likely one that many new web3 developers could run into. Lesson? Use BigNumber.js for everything that will ultimately be coming from or going to a uint256.
  2. Creating an interface that has fallback when there is no Web3 is tricky and requires explaining how to use a contract ABI to your users or manually building the data string that is passed to the contract methods. We decided to go with the latter, which we accomplished by pulling the Web3 utils from the ethereum-js package and using it to help with padding/hex.
  3. There are still bugs in the development tools due to how new the ecosystem is. For example: we ran into serious thread locking when using the truffle-contract package that made our app practically unusable by completely locking the browser for about 30 seconds. After some digging we resolved the issue, and since then it has been documented by the community and fixed.

Thanks for hanging in there. If you made it this far, our guess is you might be interested in buying a pixel or two?

All said and done, we have thoroughly enjoyed developing our first dApp and look forward to future projects.

Stay tuned for our next post, where we will discuss the blockchain art movement.

InitialPixelOffering

Written by

An Ethereum based pixel economy and marketplace where you can buy and trade digital real estate.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade