Building an advert market place on the Ethereum Blockchain

This tutorial is targeted at coders, with at least some experience in React and Solidity. It will guide step by step through the smart contract and point out the specifics of an Ethereum react app.

Bernd Strehl
elbstack
10 min readAug 8, 2018

--

The goal of this tutorial is to build a fictional market place on the blockchain for advertisements. As a user you can create tokens, that represent ad space on your website. Other users then can buy the token for an amount of ETH, specified by you, to take ownership of the token and define the content of the advert. If you’re just interested in the code, you can find the repository containing the whole application here.

Prerequisites

Firstly, if you don’t have any experience with Solidity, the programming language for implementing smart contracts on the Ethereum blockchain, I recommend you to start with some tutorials. Cryptozombies is a nice interactive to get started with the basics, and the Pet Shop Tutorial from Truffle is a nice start to get started with the Truffle framework, which we will make use of.

We also need a local running node of IPFS. To get it running you can check out my story about IPFS. Then we need the browser extension MetaMask, you can download it for Chrome, Firefox and Brave. Lastly we need Ganache, a local running Ethereum Blockchain for Mac.

Setup

To get started we first need to install truffle, if you don’t already have it: npm install -g truffle. Create a directory for your project and cd into it. Get the boilerplate for a react/truffle app with truffle unbox react. Start Ganache and update your truffle.js, to use Ganache:

Now you can compile and deploy the example contract to your local blockchain using truffle migrate and then start your decentralized app (dapp) with npm start.

Next we import the accounts from Ganache into MetaMask: Open the MetaMask popup and click “Import existing DEN”. Copy the mnemonic from Ganache and paste it into the wallet seed field. You can import more accounts by clicking the icon in the right upper corner and clock import account. In Ganache you can then show the privat key with the key icon on the right of any account and import it into MetaMask. Later you will need two or three imported accounts. Now click the “Main Network” on the top left of MetaMask, click “Custom RPC” and enter the RPC server http://127.0.0.1:7545/ of Ganache (I had to use the trailing slash because of a bug in the current version of MetaMask). Your balance (99.something) should now show up in MetaMask.

Lastly we need the contracts of OpenZeppelin, install them by running npm install -E openzeppelin-solidity. OpenZeppelin provides a lot of predefined contracts, if you have some time left go ahead and read some contracts in their GitHub repository!

Now we are ready to start the development!

The contract

Create a contract named AdvertMarket.sol in the contracts folder. We then define the version of solidity and define the contract, that inherits from the contract Ownable (Note: technically we don’t need the Ownable contract here with this functionality, but it may be handy for later features).

Don’t worry, the whole code is available in a repository at the end of the tutorial.

Next we define a struct, that contains all information our ads can have. We subsume the content of the ad (what should it display), and the meta information (where is the ad displayed) as IPFS hashes. The hashes are immutable, so we are fine here.

Now we add an array for all the adverts and two mappings to store the creator and the owner of each advert. Moreover we add two modifiers, to restrict access to certain methods.

The following function allows every user of the dapp to create an advert. The parameters are the hash of the meta information and the price, at which the advert can be bought. We create a new advert and set the data hash to 32 x 0, because it is empty for now, the previous price to 0 and the creator to the sender of the message, which is the address, that wants to create the advert. Then the advert is pushed to the array and the index is used to map the advert to creator and owner, which in both cases is the sender. (Note that the push method returns the length of the array so we subtract 1.) Lastly we emit an event, that we defined before, we will be using that event in our dapp later.

The next function returns the advert data for a given advert id. This function is a view function, so it will cost no gas to use it, because it only accesses data and doesn’t write data. We will return following values: Hash of the meta information, hash of the data, price, previous price, address of the creator and address of the owner.

Next we need the logic to implement the marketplace and buying. Of course we want to make money with out marketplace, so we use following price logic:

  • When an advert is sold, the new price can not be higher as 2x the prior price.
  • Whenever profit is made with reselling an advert, we take 25% of the profit and the creator of the advert gets 25% of the profit.
  • For each trade, no matter if profit was made or not, we take 5% of the trade price.

For this purpose we define following variables:

Following function is used to buy an advert. It takes the id of the advert, that is going to change the owner, the new price and the hash of the new data as parameters. The function is payable, which means the transaction can contain ETH (other than gas), which is accessed in msg.value. We check if the provided value is equal to the price for the advert and that the new price is not higher than 2x the current price. If all checks pass, an event is fired to announce, that the advert changed it’s owner. Next the distribution of the payment is done in the payShareholders function, which we’ll discuss next. Then the advert data is updated and an event emitted, that contains the id and the hash of the new data.

We use the following function to distribute the payment among us, the previous owner and the creator, according to the logic we stated above. The fee is calculated on the add price and the creatorPayment is set to 0 by default. The profit is calculated by subtracting the previous price from the current price. If the profit is above 0 and the account, which sells the advert is not the creator, we take a fee on the profit and give the creator a share of the profit. The owner payment is the price paid minus our fee and the payment for the creator. Next the leftover payment is forwarded to the owner and if the creator receives payment, it is also forwarded.

Lastly we define a function, that allows the current owner to change the data of the advert.

The full code for the contract can be found here.
To be able to deploy the contract we have to modify the file migrations/2_deploy_contracts.js. We import the Ownable contract, and the contract we just wrote and deploy it.
Now we can deploy the contract to the local blockchain using truffle migrate.

The frontend

While our smart contract serves as a replacement for a conventional server, we still need a frontend to let the users interact with the smart contract. You can access the whole code in the GitHub repository, I will go through the parts, that are special for our dapp and not so much through the stuff, that behaves like regular React does.

First we need to import the contract .json file in the build folder, that was generated during the deployment, we also need the Ethereum JavaScript API, which is called web3 and ipfs-mini to interact with IPFS nodes.

Besides an array for our adverts, we need to store the instance of the contract, the web3 provider, the current account address and the IPFS instance in our state.

In the componentWillMount() function, we first instantiate the IPFS api and provide our local node as endpoint. You need to update your .ipfs/config :

Then we get the web3 provider, which will be injected by MetaMask. If the user doesn’t have MetaMask installed you can fallback to a public provider like Infura.

As soon as the web3 provider is set we need to instantiate the contract. This will be done by passing our imported contract to truffle-contract. Truffle does all the magic for us and knows the address of the deployed contract. Next the web3 providers needs to be set as the provider for the contract. When the contract is deployed we can set the instance of the contract in our state and start the watchers. We also regularly check if the selected account in MetaMask changed and set the current account in our state accordingly.

We can listen to the events, that are fired in our smart contract and update our UI accordingly. Note, that we can access all events based on the block number, again and again. Unlike events from for example web sockets, that just can be accessed one time, at the time they are fired, these events are permanent on the blockchain and can be more seen as a kind of record on the blockchain.

We jump over the display part of the React App and move on to the creation of an advert, which happens in the component AdSpaceForm. The form field accepts the price as ether, so we need to convert it to Wei, because the smart contract’s API assumes all Ethereum values to be in Wei. (10¹⁸ Wei are an ether.) The conversion functionality is provided by web3. Next we need to add the json representation of the add meta information to the IPFS to receive the hash. This hash needs to be converted (using the function shorten) , because the hashes returned by IPFS are in base58 and we’ll store it as hex. The purchasing of an advert is analogous.

The AdSpace component, which displays the adverts, needs to retrieve the .json data from the IPFS, before displaying it. We use the lengthen function to convert the hash to a IPFS readable format.

Wrapping Up

Now you’re done. Go ahead and try to create and advert with your first account in MetaMask and then switch accounts to buy the advert. Of course this example app lacks a lot of features, but it’s purpose is just to demonstrate how to build a marketplace with crypto tokens. Try to add more features to the application and share your insights!

You cannot customize how your token is traded, when using ERC721.

You may have wondered why we didn’t use the ERC721 token standard for non-fungible tokens. (Non-fungible tokens are unique and not separable, like adverts.) This is because the ERC721 token API just defines information about ownership and the methods to trade it. If you look into the contract implementation of OpenZeppelin you can see, that those tokens have no price on their own. If you wanted to build a trade platform for this tokens, you would need a new contract that implements an escrow to store the token for the owner and trade it to a buyer if the buyer supplies the agreed amount of ETH. Basically if our adverts would be ERC721 tokens, everyone could build a trade platform for them and take fees. But we a custom logic for payments and profit sharing, so the tokens come with a built-in buy mechanism.

--

--

Bernd Strehl
elbstack

Tixl, serverless, DevOps mindest, DApps, Node.js & Beer 🍺