EVM package deployment with ZeppelinOS - Part III Linking to mainnet

Pulling it all together

Now that you've created your first EVM package, let's go through the steps you would take to link it to your project as if you were linking directly from NPM. These commands you already know, so I'll just run through them quickly:

mkdir myproject2
cd myproject2

npm init -y
zos init myproject2
npm install [email protected]

Now we need to link the NPM package:

zos link <<name of your npm package>>

This will install your NPM package and add it to your zos.json file as a dependency. You will also now find your linked contract in the node modules folder. Open it up and you will see the ZeppelinOS project configuration files along with your contract and build folders.

For convenience, I've already deployed our linked list to NPM, so if you don't want to deploy your own, you can use:

zos link zos-linkedlist


Return to your myproject2 top level, and create a new contract to test the linked LinkedList contract. In your contracts folder, make a new contract called QuickContract.sol. Note that for the import of the LinkedList NPM module, I refer to the folder by the name "LinkedList." As you will not be able to publish your NPM package under that name, be sure to reference the module name according to the name you gave your NPM package.

pragma solidity >=0.4.24 <>0.6.0;
import "zos-linkedlist/contracts/LinkedList.sol";
//The NPM package name will have it's own folder under modules

contract QuickContract {
    LinkedList private _linkedlist;

    function setLinkedList(LinkedList linkedlist) external {
        _linkedlist = linkedlist;

    function getHead() public view returns (bytes32) {
        bytes32 _node = _linkedlist.head();
        return _node;

    function addNode(string memory _data) public {

This is a fairly simple contract designed to test the basic functions of the LinkedList.sol contract. Note that the first function setLinkedList() takes an instance of LinkedList as an address. This is how the QuickContract knows where the LinkedList contract is deployed, but remember, this address will actually be the proxy contract, which will serve as the permanent address for the contract but actually points to the LinkedList implementation we deployed earlier. When we call into the proxy, the proxy will forward the contract call to the logic code as managed by ZeppelinOS. To upgrade your contract, you can tell the proxy to point to a new logic contract, keeping the same address but with new logic. As upgradable smart contracts are outside of the scope of this tutorial, I encourage you to see this article for more information.

Make sure you have your development blockchain running, as follows:

ganache-cli --port 9545 --deterministic

And then start a new session:

zos session --network local --from <<your_10th_Ganache_address_here>> --expires 3600

Now, from the top level of our project, we're going to add QuickContract to the project:

zos add QuickContract

The next command will push our changes to the blockchain:

zos push --deploy-dependencies --network local

Let's take a quick look at the flag --deploy-dependencies while we have deployed our NPM contract to the Ethereum mainnet. For testing purposes, we're testing again on our local blockchain. ZeppelinOS will need to also deploy its own copy of the LinkedList.sol to this development blockchain in order to link to it. We only need to do this in cases where the dependencies are not already deployed on our blockchain (since we just created it when we started Ganache!), and we only need to do it once. If you have successfully deployed to the mainnet, you will not need to do this again.

As before, you need to create an instance of our contract:

zos create QuickContract

Be sure to call the LinkedList.sol contract initializer (ZeppelinOS will remind you if you forget):

zos create zos-linkedlist/LinkedList --init initialize --args "MyList"

Note that after zos create, I have included the node_modules folder where LinkedList.sol has been installed via NPM.

Now we can test the contract:

npx truffle console --network local

First, create an instance of our QuickContract:

quickContract = await QuickContract.at('<QuickContract-contract-address>')

Then link the contract by setting the address of our LinkedList contract:


Remember that you can find your "" and "" inside your zos..json file.

Time to test! First, check if a head exists:

truffle(local)> quickContract.getHead()


Now try adding a node:

truffle(local)> quickContract.addNode("Hello World!")

And now get the head again:

truffle(local)> quickContract.getHead()


// The actual bytes32 response will be unique to your project

It works!


That brings us to the end of the tutorial. Just to recap, in this tutorial you:

Good luck with your projects! Have any questions or comments? Shoot me an email or follow me on Twitter.

Thanks to Santiago Palladino for reading early drafts and providing feedback.