Instructions on how to test the governed upgradability of the smart contracts:
Install ZeppelinOS 2.0: npm install --global zos
Install Ganache: https://truffleframework.com/ganache and make sure it runs on port 8545
npm install
truffle migrate
- this should deploy the token, governance contract and initialize them, their addresses will be show in the console, save them somewhere because they will be needed later
in a new console
zos session --network development --from 0x2CF2A84F2f62128De32Fc055B75F4247Efa1a04E --expires 720000
the address is the second address from the ganache account list(or any address besides the first one can be there, there will be issues if it's the first one)
zos push
- this will deploy the logic contracts and will create a file zos.dev-* with the details
zos create MyContract_v0 --init initialize --args 64
- this will deploy the proxy for the logic contract deployed earlier and wil call the initialize function(the equivalent of the constructor in ZOS) with the value 64; this will print the address of the proxy, save it somewhere because it will be useful later on(or you can get it from the zos.dev-* file)
now we should test the first version of the contract:
truffle console --network development
inside it:
let abi = require("./build/contracts/MyContract_v0.json").abi
let contract = new web3.eth.Contract(abi, "{{your-proxy-address}}")
(await contract.methods.value()).call()
- should return 64
contract.methods.add(1).send({from:"0x6af8E70f2253E89c49f8AB19b728c958C0DEE76c"})
- the third address
(await contract.methods.value()).call()
- should return 66, the value has been added, this means we have a bug and we need to update the contract
we want the token holders to controll the upgrading process so we have to set the admin of the proxy contract to be the governance contract:
outside of the truffle console in the project run
zos set-admin {{proxy-address}} {{governance-contract-address}} -y
- from this point only the governance contract can perform upgrades so let's add an upgrade proposal:
go to https://abi.hashex.org in the "Or enter your parameters manually" select your function and write "upgradeTo" in the first line and in the second select address and place the address of the second version of the logic contract, it can be found in zos.dev-*/contracts/MyContract_v1/address add a 0x in from of the string resulted and save it
now it's time to add the proposal in the governance contract, in the truffle console write this:
let gov_abi = require("./build/contracts/QExecutiveGovernance.json").abi
let gov_contract = new web3.eth.Contract(gov_abi, "{{governance_contract_address}}")
gov_contract.methods.addProposal("{{proxy-contract-address}}",0,"{{0xencoded_data}}").send({from:"0xd1daEC4795bC607Cc009BAc60686e307E5051Ef8", gasLimit:2000000})
this will add an proposal to upgrade the implementation to MyContract_v1 where the bug isn't anymore now we should approve the proposal
gov_contract.methods.voteProposal(0,11,true).send({from:"0xd1daEC4795bC607Cc009BAc60686e307E5051Ef8", gasLimit:2000000})
- vote yes with 11 tokens
to check:
(await gov_contract.methods.getTokensForProposal(0,"0xd1daEC4795bC607Cc009BAc60686e307E5051Ef8")).call()
- should return 11
(await gov_contract.methods.getVerdictForProposal(0,"0xd1daEC4795bC607Cc009BAc60686e307E5051Ef8")).call()
- should return true;
now wait 5 minutes(that's the voting period for this POC)
(await gov_contract.methods.isTransactionProposalConfirmed(0)).call()
- when this returns true this means the proposal is approved and can be executed in order to update the implementation
gov_contract.methods.executeTransaction(0).send({from:"0xd1daEC4795bC607Cc009BAc60686e307E5051Ef8", gasLimit:2000000})
- this will perform the update
now we can call the proxy again and the bug should not be there:
(await contract.methods.value()).call()
- should return 66, the last value
contract.methods.add(1).send({from:"0x6af8E70f2253E89c49f8AB19b728c958C0DEE76c"})
(await contract.methods.value()).call()
- should return 67, works as intended now with no bugs
The upgradability could be used to fix the state that was wrongly modified by buggy code too, for example if the DAO smart contracts used this mechanism, they could quickly update the logic contracts to get rid of the bug AND introduce some additional temporary code that would restore the funds back without the need for a hard fork
And this is how you don't get unstoppabble bugs in your smart contracts :)