Contract Inheritance

Very intelligent people before us have found common patterns in Ethereum contracts and published standardized and hardened examples to extend. We will stand on their shoulders using contract inheritance for some parts of our fleet. Let’s dig into an example of how this works.

Instead of coding up our own layer of ownership and possibly introducing insecurities, let’s look at inheriting from OpenZeppelin’s zeppelin-solidity repo.

We’ll create a contract called Inherit that will explore inheriting OpenZeppelin’s Ownable contract:

pragma solidity ^0.4.11;

import 'zeppelin-solidity/contracts/ownership/Ownable.sol';

contract Inherit is Ownable {

    string public message;

    function Inherit(string _message) {
      message = _message;
    }

    function setMessage(string _message) onlyOwner {
        message = _message;
    }
}

This light-weight contract has only one state variable, the message string, and it can only be set by the owner. Instead of handling the logic ourselves, we use a modifier from the Ownable contract.

Also, before we can deploy, the Inherit contract will need to add a dependencies.js:

const fs = require('fs');
module.exports = {
  'zeppelin-solidity/contracts/ownership/Ownable.sol': fs.readFileSync('zeppelin-solidity/contracts/ownership/Ownable.sol', 'utf8')
}

And an arguments.js to pass in a string to the Inherit() constructor.

const fs = require('fs');
module.exports = ["Ethereum is totally rad!"]

Now we can compile and deploy Inherit:

node compile Inherit
node deploy Inherit

(Deployment transaction on etherscan.io)

Contract address on Ropsten testnet:

0xd5fa4a24897db806d4879fd72c1637af5c83af65

We’ll want a script that can tell us what the current message is on the Inherit contract:

// usage: node contract getMessage Inherit
//
// ex: node contract getMessage Inherit
//
module.exports = (contract,params,args)=>{
  contract.methods.message().call().then((message)=>{
    console.log("MESSAGE:"+message)
  })
}

node contract getMessage Inherit

MESSAGE:Ethereum is totally rad!

We’ll also want a script that can tell us what account is the current owner:

// usage: node contract getOwner Inherit
//
// ex: node contract getOwner Inherit
//
module.exports = (contract,params,args)=>{
  contract.methods.owner().call().then((owner)=>{
    console.log("OWNER:"+owner)
  })
}

node contract getOwner Inherit

OWNER:0xA3EEBd575245E0bd51aa46B87b1fFc6A1689965a

Awesome, so it looks like stuff is setup correctly. Now, let’s see if it functions correctly when we try a setMessage.js script:

// usage: node contract setMessage Inherit null #ACCOUNTINDEX# #MESSAGE#
//
// ex: node contract setMessage Inherit null 1 "WHAT'S GUCC'?"
//
module.exports = (contract,params,args)=>{
  console.log("**== setting message to "+args[6]+" with account "+params.accounts[args[5]])
  return contract.methods.setMessage(args[6]).send({
    from: params.accounts[args[5]],
    gas: params.gas,
    gasPrice:params.gasPrice
  })
}

node contract setMessage Inherit null 1 "WHAT'S GUCC'?"

(Transaction with status: ‘0x1’ on etherscan.io)

node contract getMessage Inherit

MESSAGE:WHAT'S GUCC'?

Perfect, so the owner can change the message. Let’s make sure the other account can’t change the message:

node contract setMessage Inherit null 0 "Something nefarious..."

(Transaction with status: ‘0x0’ on etherscan.io)

node contract getMessage Inherit

MESSAGE:WHAT'S GUCC'?

Great, we seem to be secure without having to write and audit our own ownership functionality. One last test of inheritance would be to see if the transferOwnership() function built into the Ownable contract will just work for our contract.

We’ll need a transferOwnership.js script:

// usage: node contract transferOwnership Inherit null #ACCOUNTINDEX# #ACCOUNTADDRESSOFNEWOWNER#
//
// ex: node contract transferOwnership Inherit null 1 0xXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
//
module.exports = (contract,params,args)=>{
  console.log("**== transferring ownership from "+params.accounts[args[5]]+" to "+args[6])
  return contract.methods.transferOwnership(args[6]).send({
    from: params.accounts[args[5]],
    gas: params.gas,
    gasPrice:params.gasPrice
  })
}

node contract transferOwnership Inherit null 1 0x4ffd642a057ce33579a3ca638347b402b909f6d6

(Transaction on etherscan.io)

node contract getOwner Inherit

OWNER:0x4fFD642A057Ce33579a3CA638347b402B909f6D6

After writing Solidity for a while and trying to be super safe and working through as many possible scenarios as we can think of, it’s really nice to be able to rely on a trusted third party that has already had plenty of eyes on their code. As the ecosystem grows, projects will have more and more audits and we’ll be able to trust more and more libraries.