QNimate

  • CoursesVideos
  • WP PremiumPlugins
  • DemosLab
  • Home
  • QIdea
  • QTrack
Home Carbon Ads Writing Upgradable Smart Contracts in Solidity

Writing Upgradable Smart Contracts in Solidity

SmartContracts

As you must be aware that smart contracts are immutable programs that run exactly as programmed without any third party interface. Smart contracts cannot be modified once they are deployed which is actually a feature of smart contracts but in some blockchain use-cases it may be needed to change the smart contracts interfaces and/or their implementation. In this tutorial we will learn the basic architecture you should adopt to make the interfaces and their implementations upgradable after smart contracts are deployed.

Our architecture will involve 4 contracts in total:

  1. Key-Value Store Contract: This contract will contain the data of your dapp in form of key-value store.
  2. Application Logic Contract: This contract will contain the application logic. The function in this contracts will add/update keys in the key-value store contract. To add/remove features in the application you simply need to destroy this contract and deploy a new contract pointing to the same key-value store therefore you preserve the state of the contract
  3. Events Contract: To make sure that we don’t loose the events when we upgrade the application logic contract we need to create a events contract to fire events. Make sure that the events are very generic i.e., different events are differentiated using the event arguments not the event names or parameter types. For example:
    to fire event for renewable of tenancy contract you shouldn’t have a event name as “tenancyContractRenewed” instead a generic event with first argument as “tenancyContractRenewed” and the second argument as the tenancy contract id and so on. To make the events contract scaleable you can index events so that you don’t have to loop through all events to find event types.
  4. Address Contract: Finally a contract to which your client will point to. This contract will contain the address of the application logic contract. So whenever you upgrade the application logic contract you can simply change the address of the application contract in the address contract and client will automatically point to the new contract.

Here is an example of key-value store and application logic contract:

pragma solidity ^0.4.8;

contract DataStore
{
    address owner;
    address appContract;
   
    mapping (string => string) kayValueStore;
   
    function str_to_bytes(string str) constant returns (byte[250]){
        bytes memory b = bytes(str);
        byte[250] memory final_str;
        for(uint i; i<b.length; i++){
            final_str[i] = b[i];
        }

        return final_str;
    }
   
    function DataStore()
    {
        owner = msg.sender;
    }
   
    function changeAppContract(address _appContract)
    {
        if(msg.sender == owner)
        {
            appContract = _appContract;
        }
    }
   
    function updateKey(string key, string value)
    {
        if(msg.sender == appContract)
        {
            kayValueStore[key] = value;
        }
    }
   
    function getValue(string key) returns (byte[250] value)
    {
        return str_to_bytes(kayValueStore[key]);
    }
}

contract MainContract
{
    DataStore store;
   
    function MainContract(address _DataStore)
    {
        store = DataStore(_DataStore);
    }
   
    function updateData()
    {
        store.updateKey("Name", "Dave");
    }
   
    function getData() returns (string name)
    {
        bytes memory bytesStringTrimmed = new bytes(250);
        for(uint iii = 0; iii < 250; iii++)
        {
            bytesStringTrimmed[iii] = store.getValue("Name")[iii];
        }
       
        return string(bytesStringTrimmed);
    }
}

Here we are storing the key and value both as strings in the DataStore contract. As a contract cannot read a string returned by another contract due to EVM’s limitation therefore we are converting the string to bytes[250] (assuming that the value length will be less than equal to 250 always) and then again converting it back to string once read. If your strings are likely to be above 250 characters then you can change the length in the source code.

Here we have also set permissioning stating that only the MainContract i.e., the application logic contract can only write to the key-value store.

Here is the web3js code to test the above contracts:

var web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:22000"));
var browser_untitled_datastoreContract = web3.eth.contract([{"constant":true,"inputs":[{"name":"str","type":"string"}],"name":"str_to_bytes","outputs":[{"name":"","type":"bytes1[250]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"key","type":"string"}],"name":"getValue","outputs":[{"name":"value","type":"bytes1[250]"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_appContract","type":"address"}],"name":"changeAppContract","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"key","type":"string"},{"name":"value","type":"string"}],"name":"updateKey","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
var browser_untitled_datastore = browser_untitled_datastoreContract.new(
   {
     from: web3.eth.accounts[0],
     data: '0x6060604052341561000f57600080fd5b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506107278061005e6000396000f300606060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630fd9c61b14610067578063960384a014610100578063eacbb41c14610199578063fbececac146101d2575b600080fd5b341561007257600080fd5b6100c2600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610272565b604051808260fa60200280838360005b838110156100ed5780820151818401526020810190506100d2565b5050505090500191505060405180910390f35b341561010b57600080fd5b61015b600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610367565b604051808260fa60200280838360005b8381101561018657808201518184015260208101905061016b565b5050505090500191505060405180910390f35b34156101a457600080fd5b6101d0600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610482565b005b34156101dd57600080fd5b610270600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190505061051c565b005b61027a6105f7565b610282610642565b61028a6105f7565b60008492505b825181101561035c5782818151811015156102a757fe5b9060200101517f010000000000000000000000000000000000000000000000000000000000000090047f010000000000000000000000000000000000000000000000000000000000000002828260fa8110151561030057fe5b60200201907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff191690817effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815250508080600101915050610290565b819350505050919050565b61036f6105f7565b61047b6002836040518082805190602001908083835b6020831015156103aa5780518252602082019150602081019050602083039250610385565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104715780601f1061044657610100808354040283529160200191610471565b820191906000526020600020905b81548152906001019060200180831161045457829003601f168201915b5050505050610272565b9050919050565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156105195780600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b50565b600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156105f357806002836040518082805190602001908083835b6020831015156105ab5780518252602082019150602081019050602083039250610586565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902090805190602001906105f1929190610656565b505b5050565b611f406040519081016040528060fa905b60007effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906001900390816106085790505090565b602060405190810160405280600081525090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061069757805160ff19168380011785556106c5565b828001600101855582156106c5579182015b828111156106c45782518255916020019190600101906106a9565b5b5090506106d291906106d6565b5090565b6106f891905b808211156106f45760008160009055506001016106dc565b5090565b905600a165627a7a723058201b24b6f574ff8c94c714d43bf887a017b3808ae99d1e3669ead0a392ee91ebe10029',
     gas: '4700000', gasPrice: 0
   }, function (e, contract){
    console.log(e, contract);
    if (typeof contract.address !== 'undefined') {
        DatastoreInstance = browser_untitled_datastoreContract.at(contract.address);
        var _DataStore = contract.address;
        var browser_untitled_maincontractContract = web3.eth.contract([{"constant":false,"inputs":[],"name":"getData","outputs":[{"name":"name","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"updateData","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_DataStore","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]);
        var browser_untitled_maincontract = browser_untitled_maincontractContract.new(
           _DataStore,
           {
             from: web3.eth.accounts[0],
             data: '0x6060604052341561000f57600080fd5b60405160208061046783398101604052808051906020019091905050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506103ec8061007b6000396000f30060606040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633bc5de30146100515780638e00a2bb146100df575b600080fd5b341561005c57600080fd5b6100646100f4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a4578082015181840152602081019050610089565b50505050905090810190601f1680156100d15780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34156100ea57600080fd5b6100f2610289565b005b6100fc610398565b6101046103ac565b600060fa6040518059106101155750595b9080825280601f01601f19166020018201604052509150600090505b60fa811015610281576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663960384a06000604051611f4001526040518163ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018080602001828103825260048152602001807f4e616d6500000000000000000000000000000000000000000000000000000000815250602001915050611f4060405180830381600087803b151561020557600080fd5b6102c65a03f1151561021657600080fd5b50505060405180611f40016040528160fa8110151561023157fe5b6020020151828281518110151561024457fe5b9060200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508080600101915050610131565b819250505090565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fbececac6040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401808060200180602001838103835260048152602001807f4e616d6500000000000000000000000000000000000000000000000000000000815250602001838103825260048152602001807f446176650000000000000000000000000000000000000000000000000000000081525060200192505050600060405180830381600087803b151561038257600080fd5b6102c65a03f1151561039357600080fd5b505050565b602060405190810160405280600081525090565b6020604051908101604052806000815250905600a165627a7a72305820de9f93d7b31329849e20fcad8bb94412ea3ccb09b6032e5564a8c04be72269140029',
             gas: '4700000', gasPrice: 0
           }, function (e, contract){
            console.log(e, contract);
            if (typeof contract.address !== 'undefined') {
                MainContractInstance = browser_untitled_maincontractContract.at(contract.address);
                DatastoreInstance.changeAppContract.sendTransaction(contract.address, {
                    from: web3.eth.accounts[0],
                    gas: '4700000'
                }, function(){
                    MainContractInstance.updateData.sendTransaction({
                        from: web3.eth.accounts[0],
                        gas: '4700000'
                    }, function(e, r){
                        console.log(e, r);
                        setTimeout(function(){
                            MainContractInstance.getData.call({from: web3.eth.accounts[0], gas: '4700000', gasPrice: 0}, function(e, r){
                                console.log(e, r);
                                DatastoreInstance.getValue.call("Name", {from: web3.eth.accounts[0], gas: '4700000', gasPrice: 0}, function(e, r){
                                    console.log(e, r);
                                });
                            });
                        }, 100)
                    })
                })
            }
         })
    }
})
Nov 9, 2017Narayan Prusty
Calculating Nonce for Raw Transactions in GethBlockchain based Identity, Records Management and Voting using AWS
Comments: 2
  1. Hoang Le
    4 years ago

    Haha, it seems Smart Contracts is not mutable anymore. The contract owner can easily do some cheats in Logic contract to make it… haha. no one know 😀

    ReplyCancel
  2. Penyebab Luka Di Penis
    4 years ago

    This article is very interesting.
    Thanks for sharing information .. :)

    ReplyCancel

Leave a Reply Cancel reply

To create code blocks or other preformatted text, indent by four spaces:

    This will be displayed in a monospaced font. The first four
    spaces will be stripped off, but all other whitespace
    will be preserved.
    
    Markdown is turned off in code blocks:
     [This is not a link](http://example.com)

To create not a block, but an inline code span, use backticks:

Here is some inline `code`.

For more help see http://daringfireball.net/projects/markdown/syntax

Narayan Prusty

I am a software engineer specialising in Blockchain, DevOps and Go/JavaScript. This is my personal blog where I write about things that I learn and feel interesting to share.

Image4 years ago 3 Comments Ethereum
Share this
0
GooglePlus
0
Facebook
0
Twitter
0
Linkedin
Related Articles
  • Calculating Nonce for Raw Transactions in Geth
  • EIP-1557 – Ethereum Transaction Fees and Block Size
  • Page Visibility API Tutorial with Example
  • Streaming File Uploads to Storage Server with Node.js
  • Understanding Javascript Events In Depth
Our Sponsor
My Books

2014 - 2015 © QNimate
All tutorials MIT license