Creating a LinkedList contract for EVM package deployment with ZeppelinOS

tutorial

#1

EVM package deployment with ZeppelinOS — Part 2

Setup and Deployment

Download and install Node.js and NPM.

If you are on OSX, you might find it useful to have Xcode installed as well. Alternatively, if you don’t want to download everything that comes with Xcode you can check out this tutorial to install only the command line tools, saving yourself a much larger download.

Truffle:

Go ahead and get Truffle. Truffle is one of several development frameworks for Ethereum that can make developing dApps (distributed applications) easier.

npm install truffle@4 -g

Ganache:

Ganache is a development blockchain that runs locally on your computer and is used for testing. Previously called TestRPC, Ganache runs a full ephemeral Ethereum blockchain on our machine that behaves and acts like the real thing (with customizable differences). We will use it for testing our contracts.

npm install -g ganache-cli

ZeppelinOS :

ZeppelinOS is the development platform that will allow us to not only create and deploy our EVM package but will also allow us to upgrade smart contracts when we want to add new features or fix bugs. Additionally, ZeppelinOS introduces economic incentives to support a healthy ecosystem of secure applications. Learn more at https://zeppelinos.org/.

npm install --global zos

To see a list of command line options once installed:

zos --help

To learn more, check out the API reference docs.

Project Setup:

First, create a new directory and cd into it.

mkdir LinkedList
cd LinkedList

Feel free to name your directory and project to whatever suits you best.

We will now use NPM to create your package.json file.

npm init

Take care to answer the prompts as this information will be need when you publish to NPM.

Now we are ready to initialize our ZeppelinOS project.

zos init LinkedList
>>Successfully written zos.json

Like npm init , zos init will create its own JSON file, zos.json to track the details of your ZeppelinOS project. The command will also initialize Truffle which will create two directories, contracts and migrations as well as a truffle-config.js file.

Your directory structure should look like this:

contracts
migrations
package.json
truffle-config.js
zos.json

Both contracts and migrations are empty folders.

The truffle-config.js file exports an object which defines the networks truffle can use and the settings for deployment. For now, you should only see a “local” network. This network will be our development network.

'use strict';

module.exports = {
  networks: {
    local: {
      host: 'localhost',
      port: 9545,
      gas: 5000000,
      gasPrice: 5e9,
      network_id: '*'
    }
  }
};

The zos.json file will fill up as soon as you start adding and deploying contracts. The information about our contracts will be stored here.

{
  "zosversion": "2",
  "name": "LinkedList",
  "version": "0.1.0",
  "contracts": {}
}

The files and folders so far are package.json (created by npm init ), two empty directories named contracts and migrations (created by zos for Truffle) and a zos.json file (created by zos for ZeppelinOS).

Creating the LinkedList Contract:

The goal of the LinkedList.sol contract is to create a data structure that will allow us to add and remove nodes from the head of a list. In essence, we are going to create a stack . Custom data structures are useful because solidity has limited built-in array methods and array manipulation can quickly become an expensive (or even impossible! ) operation to complete on-chain because of gas costs.

Need a Linked List Refresh? Here are some references I used for this tutorial.

The Little Guide of Linked List in JavaScript
Is a list of nodes that are represented by a head that is the first node in the list and the tail that is the last one. hackernoon.com

JS Data Structures: Linked List
Learn what a Linked List is and how to write one in Javascript codeburst.io

Open your code editor of choice and navigate to your project. Create a solidity contract called LinkedList.sol and save it in the contracts folder.

If you are interested in an excellent tutorial about building a Linked List in Solidity, I highly recommend Austin Thomas Griffith’s “Linked Lists in Solidity” which I used as a starting point for the following code:

On line 3 we are importing:

import "zos-lib/contracts/Initializable.sol";

Initializable.sol is not installed by default with ZeppelinOS and you will need it in order to compile the contract. To install it:

npm install zos-lib

Here we are importing from the zos-lib a contract called Initializable.sol, and we are telling the compiler on line 5 that our contract LinkedList is Initializable . Then on line 26, we create a function called initialize() which uses the library modifier initializer which can be found in the zos-lib/contract/Initializable.sol contract.

It is not entirely critical to understand the purpose of the initializer pattern for this tutorial, but you should know that when building upgradable smart contracts using the ZeppelinOS system, you must use initializer functions instead of constructor functions in your contracts. Constructor functions are incompatible with the way ZeppelinOS proxy contracts manage upgradable smart contracts. To learn more see the docs here.

Strictly speaking in this example it is not really necessary to use the initializer function, as the LinkedList.sol contract only uses the initialize() function to set the value of length to zero. In Solidity undeclared values are by default zero, making this initializer redundant. I am including it as a reminder that in the ZeppelinOS system, we do not use constructor functions.

Deploying to our development network:

To get started with testing the contract (to be sure it works!)first deploy it to Ganache, our development blockchain environment.

Open a new terminal window and type:

ganache-cli --port 9545 --deterministic

You will want to run this in a new window because you will need to keep it running during development and if you close it, you will lose your development blockchain state, including all of your locally deployed contracts and be forced to start again. After starting ganache-cli you should see an output like this.

Ganache CLI v6.1.8 (ganache-core: 2.2.1)

Available Accounts
==================
(0) 0x04760fbb151ba69e773c6bebd10ade21450d3fb9 (~100 ETH)
(1) ...
(2) ...
...

Private Keys
==================
(0) 0xca9aca918d1c47237a858be560fa444k4qoid23s4e46d63ffc5edeeab2918f
(1) ...
(2) ...
...

HD Wallet
==================
Mnemonic: <<generated twelve word mnemonic here>>
Base HD Path: m/44'/60'/0'/0/{account_index}

Gas Price
==================
20000000000

Gas Limit
==================
6721975

Listening on 127.0.0.1:8545

This is a list of pre-generated and pre-funded accounts that you can use on your development blockchain, along with a generated mnemonic, details about gas price, gas limit, and base HD(Hierarchical deterministic) wallet path.

VERY IMPORTANT: NEVER USE THIS MNEMONIC FOR ANYTHING OTHER THAN DEVELOPMENT PURPOSES. IT IS NOT SECURE AND YOU WILL LOSE ALL YOUR ETH.

You have been warned.

Note that as arguments for the command ganache-cli we have both:

--port

--deterministic

The --port argument has the same value (9545) as we saw in our truffle-config.js file.

The --deterministic means that every time we run ganache-cli we are going to use the same mnemonic so that we can ensure we get the same test wallet addresses and corresponding private keys. Without this flag, we would have a list of random addresses each time which can be confusing when testing projects.

I like to try and keep a single development mnemonic for all my dev work, so I recommend copying down this mnemonic and saving it. If you are doing development work across multiple teams or platforms, it can be useful for everyone to work with the same set of addresses. To use your mnemonic run ganache-cli with the following additional argument:

–mnemonic "your twelve word mnemonic here"

Remember. This mnemonic is for DEVELOPMENT ONLY. For a complete list of command line options see the Github for Ganache-cli.

Return to your original terminal window and create a new session for ZeppelinOS.

zos session --network local --from 0x1df62f291b2e969fb0849d99d9ce41e2f137006e --expires 3600

The command line argument: --network local instructs zos to use the local network as specified in our truffle-config.js file, while the --from argument specifies the address from which ZeppelinOS will be deploying contracts and managing them. In this case, the --from address is the 10th address that Ganache-cli generated.

The reason why we need to specify an address is to get around something called the “transparent proxy pattern”. Essentially the proxy contract that is deployed when creating upgradable contracts responds differently depending on whether or not the address calling the contract is the same address that deployed it. Only the address that deploys the proxy can interact directly with the proxy, while all other addresses that call the proxy, will be proxied onto the contract the proxy points to.

A simple way to think of the “transparent proxy pattern”

Fortunately, ZeppelinOS takes care of proxies for us, and as a developer, it is only important to remember --from needs to be an address that will never be used to call the contract with the intention of forwarding the contract onwards. To read more: ZeppelinOS upgrades pattern section.

Finally, the --expires 3600 sets the duration for which we want to keep the ZeppelinOS session open, and if you time-out, the command will need to be re-entered. You will know your session has timed out if you receive this message when you try to execute other zos commands:

A network name must be provided to execute the requested action

Not to worry, simply re-enter the following and you will have a new session to work with.

zos session --network local --from 0x1df62f291b2e969fb0849d99d9ce41e2f137006e --expires 3600

Once you have a ZeppelinOS session running, you can always check the file .zos.session in your main project. Here you will see the information regarding your current session. The network you are using, the from address and when the session expires.

Adding our contract:

Now that you have set up your ZeppelinOS session, created your contract, and have your development blockchain running, you need to add your contract to the ZeppelinOS project. In the same terminal window, in the top level folder of your project:

zos add LinkedList

If you look at the zos.json file, you will see that your JSON object now contains our contract “LinkedList” under the “contracts” key value.

Similarly, you now have a top level folder in your project named build which contains a subfolder called contracts which is where the JSON objects representing the contracts ABI (application binary interface) are stored.

Now that you have added your LinkedList.sol contract to the ZeppelinOS project, we can deploy the project to the development blockchain.

zos push

When it comes time to deploy to a different network (such as a public test net or main net) you will run the zos session command again with a new network as defined in your truffle-config.js file. You can also add the --network flag followed by your network name if you want to push without starting a session.

Note, if you take a look at your second terminal where Ganache is running, you will see that a transaction has been created.

The final line of output should be something like this:

Created zos.dev-<<some number here>>.json

If you look at your project folder structure, you will see a new .json file has been created in your project with a file name zos.dev-<<some number here>>.json . The “<<some number here>>” will be the network-id of your development blockchain. This will most likely be a pseudo-random number when working with a development chain and is used just to identify the network. There is an unofficial list of public networks and their identification numbers here:

The zos.dev-<<some number here>>.json file holds all the information regarding the current deployment of our contracts on this particular network, and if you open it up you will see that the address the contracts are deployed at along with various information about them.

For more information regarding networks.

Creating an Instance of our contract:

Now that we have deployed our project we need to make an instance of our contract to interact with .

zos create LinkedList --init initialize

This command creates an instance of our LinkedList.sol and calls the initialize function in place of what would normally have been a constructor. It will return an address where the instance was created: (these addresses will be unique to your project).

Using session with network local, sender address 0x1df62f291b2e969fb0849d99d9ce41e2f137006e
Creating proxy to logic contract 0x2adf8b30d4dd24a05ccd9afbdc06a5b49c9c758d and initializing by calling initialize with:

Instance created at 0x25f96b23947f3e57b29d15760fd8af926694fa81
0x25f96b23947f3e57b29d15760fd8af926694fa81

The zos create command creates an instance of the contract on the blockchain that we can directly interact with. While zos push deploys the project, our project isn’t intended to be directly manipulated. Rather the ZeppelinOS give us the ability to create instances of contracts which point to the original contracts that we wrote. This gives us the ability to upgrade our contracts by telling ZeppelinOS we want to point our instance of a contract to a newer version of our original contract.

If you take a look again at the zos.dev-<<some number here>>.json file, and scroll to the bottom, you will now see a top-level value named “proxies” near the bottom of the file. This is where ZeppelinOS keeps track of your contract deployments on this particular network. The address refers to the location of the instance of your contract, version is the version of our instance and implementation points to the logic contract our contract calls will be proxied to.

The address entry will be the address that you will use when you want to connect to your contract (via the proxy) for something like testing or via web3 in a dApp.

To deploy and link this contract as an EVM package: https://forum.zeppelin.solutions/t/deploying-and-linking-a-linkedlist-evm-packaged-with-zeppelinos/