How to upgrade programmatically?

I’m trying to follow this https://github.com/OpenZeppelin/openzeppelin-sdk/blob/master/examples/upgrades-library

to make my contract programatically upgradable.
Firstly, I wanted to deploy it in one script and then upgrade it with some changes made in another script. I wrote the deploy.js script like this:

    const Web3 = require('web3');
    const { Contracts, ProxyAdminProject, ZWeb3 }=require('@openzeppelin/upgrades');

    async function main(web3) {
     // Create web3 provider and initialize OpenZeppelin upgrades
    if (!web3) web3 = new Web3('http://localhost:8545');
    ZWeb3.initialize(web3.currentProvider)
    // Create an OpenZeppelin project
    const [from] = await ZWeb3.eth.getAccounts();
    const project = new ProxyAdminProject('MyProject', null, null, { from, gas: 1e6, gasPrice: 1e9 });

    // Deploy an instance of MyContractV0
    console.log('Creating an upgradeable instance of v1...');
    const SimpleStorageV1 = Contracts.getFromLocal('SimpleStorageV1');
    const instance = await project.createProxy(SimpleStorageV1, {initArgs: [0]});
    const address = instance.options.address;
    console.log(`Contract created at ${address}`);
    }

    main();

but what I’ve been unable to understand is where does it get the contracts from via Contracts.getFromLocal?
I assumed it was from the build directory so I went on to run $ npx oz compile where it began to throw the error shown below:

$ npx oz compile
/media/asmee/Presentations/upgradeProgrammatically/build/contracts/Initializable.json: Unexpected end of JSON input

And I checked that the build directory contains bothe initializable.json and SimpleStorageV1.json but both are empty!

Can you please guide me on this?

Here’s by SimpleStorageV1.sol file:

//"SPDX-License-Identifier: UNLICENSED"
pragma solidity ^0.6.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

contract SimpleStorage is Initializable {
        
    uint256 storedValue;
    
    function initialize (uint256 _initialValue) public initializer {
        storedValue = _initialValue;
    }

    function retrieve() public view returns (uint256){
        return storedValue;
    }
}
1 Like

okay Idk how but the error got resolved after I removed and added the SimpleStorage.sol and ran compilation again!

1 Like

Hi @asmeedhungana,

We need to compile the contracts before deploying and upgrading programmatically.

I’m not sure what happened with the artifacts. If you come across it again, please share your contracts and I can try to reproduce. I have seen this where two contracts with the same name are being compiled.

Glad that it is working for you now.

1 Like

Nope, it again is throwing errors!..and the names I’d given were not same but similar…
“SimpleStorage” and “SimpleStorageV2”…
I added the new contract in the contracts dir and
Upon running the command $npx oz compile for compiling the upgraded version of the contract [i.e. SimpleStorageV2], I began to get this error later:

Compilation warning:
There is more than one contract named SimpleStorage. The compiled artifact for only one of them will be generated.

I then changed the upgraded version’s name to “V2.sol” and again, I’m getting the same error!

Can you tell me the right steps to go through this? I think I’m going the wrong way!

And how shall I write the deploy and upgrade scripts in separate files? In the eg given in the docs: https://docs.openzeppelin.com/learn/upgrading-smart-contracts#:~:text=Upgrading%20Contracts%20Programmatically,interaction%2C%20and%20source%20code%20verification. , both the deployment of the first version and the upgrading are done together… This doesn’t happen in real, does it? We deploy one contract as upgradable first so that in future if we find some bug, we’ll write a upgrade script to fix that…
I want them in separate files, but I’m unable to figure out how exactly…

1 Like

Hmm… I figured out what I was doing wrong!
I had been naming the fileNames differently but despite having different fileNames, both of them had the contract name as “SimpleStorage” !
I totally missed it!!!

Anyways… I still need to find out how I can write the deploy and Upgrade scripts separately! Like, what I got confused mostly in is how to pass the same instance of project onto the next file…
Can I get a small demo on that please? I’m okay with the above deploy script,
what can be its counterpart upgrade script?

1 Like

Hi @asmeedhungana,

Glad you figured out the issue. I wondered if you were changing file names rather than contract names. The contract name is used for creating the artifact.


The following demo is based on the example: https://github.com/OpenZeppelin/openzeppelin-sdk/tree/master/examples/upgrades-library

We will use the contracts from the example.

MyContractV1.sol inherits from MyContractV0.sol (though we don’t need to use inheritance for upgrades)

MyContractV0.sol

pragma solidity ^0.5.0;

import "@openzeppelin/upgrades/contracts/Initializable.sol";

contract MyContractV0 is Initializable {
  uint256 public value;
  
  function initialize(uint256 _value) initializer public {
    value = _value;
  }
}

MyContractV1.sol

pragma solidity ^0.5.0;

import "./MyContractV0.sol";

contract MyContractV1 is MyContractV0 {
  function add(uint256 _value) public {
    value = value + _value;
  }
}

We will use a similar script to the example, but we will separate it into create and upgrade scripts and in the create include the address of the ProxyAdmin so we can use this in our upgrade script.

index.js

const Web3 = require('web3');
const { Contracts, ProxyAdminProject, ZWeb3 } = require('@openzeppelin/upgrades')

async function main() {
  // Create web3 provider and initialize OpenZeppelin upgrades
  web3 = new Web3('http://localhost:8545');
  ZWeb3.initialize(web3.currentProvider)

  // Create an OpenZeppelin project
  const [from] = await ZWeb3.eth.getAccounts();
  const project = new ProxyAdminProject('MyProject', null, null, { from, gas: 1e6, gasPrice: 1e9 });

  // Deploy an instance of MyContractV0
  console.log('Creating an upgradeable instance of v0...');
  const MyContractV0 = Contracts.getFromLocal('MyContractV0');
  const instance = await project.createProxy(MyContractV0, { initArgs: [42] });
  const address = instance.options.address;
  console.log(`ProxyAdmin at ${project.proxyAdmin.address}`)
  console.log(`Contract created at ${address}`);

  // And check its initial value
  const initialValue = await instance.methods.value().call();
  console.log(`Initial value is ${initialValue.toString()}\n`);
}

main();

Create V0

$ node ./src/index.js
Creating an upgradeable instance of v0...
ProxyAdmin at 0xC5aFE31AE505594B190AC71EA689B58139d1C354
Contract created at 0x25AF99b922857C37282f578F428CB7f34335B379
Initial value is 42

upgrade.js

In this simple demo I am manually updated the address of the ProxyAdmin and the proxy contract using the values from index.js

You will need to manually update the addresses.

const Web3 = require('web3');
const { Contracts, ProxyAdminProject, ProxyAdmin, ZWeb3 } = require('@openzeppelin/upgrades')

async function main() {
  // Create web3 provider and initialize OpenZeppelin upgrades
  web3 = new Web3('http://localhost:8545');
  ZWeb3.initialize(web3.currentProvider)

  // Create an OpenZeppelin project
  const [from] = await ZWeb3.eth.getAccounts();

  const proxyAdminAddress = '0xC5aFE31AE505594B190AC71EA689B58139d1C354';
  const proxyAddress = '0x25AF99b922857C37282f578F428CB7f34335B379';

  const proxyAdmin = ProxyAdmin.fetch(proxyAdminAddress, { from, gas: 1e6, gasPrice: 1e9 })
  const project = new ProxyAdminProject('MyProject', proxyAdmin, null, { from, gas: 1e6, gasPrice: 1e9 });

  // Upgrade it to V1
  console.log('Upgrading to v1...');
  const MyContractV1 = Contracts.getFromLocal('MyContractV1');
  const instanceV1 = await project.upgradeProxy(proxyAddress, MyContractV1);
  console.log(`Contract upgraded at ${instanceV1.options.address}`);

  // And check its new `add` method, note that we use instanceV1 since V0 has no `add` in its ABI
  await instanceV1.methods.add(10).send({ from, gas: 1e5, gasPrice: 1e9 });
  const newValue = await instanceV1.methods.value().call();
  console.log(`Updated value is ${newValue.toString()}\n`);
}

main();

Upgrade to V1

$ node ./src/upgrade.js
Upgrading to v1...
Contract upgraded at 0x25AF99b922857C37282f578F428CB7f34335B379
Updated value is 52
1 Like

Thank you so much @abcoathup!

This was the part I was not able to figure out what to do about…
I’ll check this one let you know! Thanks a lot! :heart:

1 Like

@abocoathup I tried the solution and it works perfectly! Thanks a lot! :smiley:
You really are my savior! :heart:

1 Like

Hi @asmeedhungana,

Glad you got it working. :partying_face:

1 Like