A proxy interface is available to view external functions:
interfaceIUpgradableProxyDAO {
function getImplementation() externalviewreturns (address);
function getOwner() externalviewreturns (address);
function getGovernance() externalviewreturns (address);
function transferOwnership(address_newOwner) externalpayable;
function upgrade(address_newAddress, uint256_utcStartVote, uint256_utcEndVote) externalpayable;
function voteUpgradeCounting() externalpayable;
function voteUpgrade(boolapprove) externalpayable;
function getAllUpgrades() externalviewreturns (ProxyUpgrades.Upgrade[] memory);
function getLastUpgrade() externalviewreturns (ProxyUpgrades.Upgrade memory);
}To start with, once the proxy is deployed, you will have to call the upgrade function for each update as shown in the example below:
constcurrentBlockTimeStamp=((awaitweb3.eth.getBlock("latest")).timestamp)+10;conststartUTC=`${currentBlockTimeStamp.toFixed(0)}`;constendUTC=`${(currentBlockTimeStamp+86400).toFixed(0)}`;awaitproxy.upgrade(functionalContractV2.address,startUTC,endUTC,{from: owner});Then a period of minimum 24 hours is valid to proceed to the vote by governance each holder of at least one unit of the governance token provided to the deployment of the proxy can vote favorably or unfavorably to each update of the implementation of the proxy.
awaitproxy.voteUpgrade(true,{from: owner});awaitproxy.voteUpgrade(true,{from: tester});Once the voting period has expired the proxy manager can call the function that will check the result of the vote and apply or not the implementation change.
awaitproxy.voteUpgradeCounting({from: owner});This last one if an implementation is approved favorably will change implementation and will call the initialize function of the new implementation. (This allows you to make changes if necessary to promote the simplicity of the version change).
The Upgradable Proxy DAO must have reserved memory spaces to avoid confusion with the implementation's memory which begins with storage slot 0x00000000000000000000000000000000000000000000000000000000000000000000. We have therefore followed the EIP-1967 standard for the storage of:
- _IMPLEMENTATION_SLOT (keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1) = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
- _ADMIN_SLOT (keccak-256 hash of "eip1967.proxy.admin" subtracted by 1) = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
And we have respected the security (substract -1) applied on this storage addresses that we have added to our own addresses.
Here are the areas we added to allow governance in a secure way.
_GOVERNANCE_SLOT (keccak-256 hash of "io.checkdot.proxy.governance-token" subtracted by 1) = 0xa104a226b802ae177ad07b7b101c32acd246fa967c70ae9245f6070074d0ef0d
_UPGRADES_SLOT (keccak-256 hash of "io.checkdot.proxy.upgrades" subtracted by 1) = 0x5369eef32e208f60e8918f320ffd798e56b416ec90d29edfed41f71d65e56165
By following this standard and applying the -1 to the hash, which increases the difficulty for any hacker who could potentially specify a hash via a string key, the latter would not be able to find the keyword simply thanks to the -1.
These memory spaces are really far from the starting zone and it is extremely complicated to write to this zone, of course if you want to use this proxy please don't allow your implementation to specify a storage slot used only for storage by definition.
So much for security, if you have any other questions or suggestions don't hesitate to contact me directly by email which can be found simply on checkdot.io
Clone and initialize the repository:
$ git clone https://github.com/checkdot/CheckDot.DAOProxyContract.git
$ cd CheckDot.DAOProxyContract
$ npm iCompile the project:
$ truffle compileDeploy CheckDot Smart Contract to interact with CDT locally. Choose a local address of your choice from Ganache and modify the truffle-config.js in module.exports > networks > development > from by writing your selected local address.
constHDWalletProvider=require('@truffle/hdwallet-provider');varsecret=require("./secret");module.exports={plugins: ['solidity-coverage','truffle-plugin-verify'],api_keys: {bscscan: secret.API_KEY},networks: {development: {// truffle deploy --network developmenthost: "127.0.0.1",port: 7545,network_id: "*",from: "0x7f87C43136F7A4c78788bEb8e39EE400328f184a"},
...
}And deploy locally
$ truffle deployDo the same in your CheckDot DAO Proxy Contract local repository. In the migration module replace the contract address with the one you generated deploying your local CheckDot Smart Contract. Deploy the contract:
$ truffle deploy --network development$ truffle compile && truffle test --network developmentJeremy Guyet
MIT