How to do it...

  1. Making a contract upgradable can be achieved through multiple methods. One of the popular implementations is to use a proxy contract that keeps track of the logic contract. The proxy will redirect calls to the latest contract that has the upgraded code.
  2. To create a basic upgradable smart contract system, we will need three basic contracts. They are the following:
  • Storage contract
  • Proxy contract
  • Logic contract
  1. Use the logic contract to write the upgradable piece of code and the storage to keep track of all contract states. The proxy uses the latest code from the logic contract to update the state of the storage contract.
  2. To keep things simple and easy to understand, consider a storage contract that has one state variable:
pragma solidity^0.4.24;

/**
* Basic storage contract
*/
contract State {
uint public result;
}
  1. Create a simple logic contract that modifies the value of the state variable, for example, a contract that adds 1 to the current value of the state variable. Create the local variable in the logic contract with the same type and name:
pragma solidity^0.4.24;

/**
* Basic logic contract
* Increments the state variable value by 1
*/
contract AddOne {

uint result;

function increment() public {
result = result + 1;
}
}
  1. Here comes the interesting part: the proxy contract. Create a simple contract that inherits the storage contract:
pragma solidity^0.4.24;

contract Proxy is State {
...
}
  1. Add a state variable to store the address of the logic contract and a function to modify the value. This is the function that basically acts as the upgrade mechanism:
address logicContract;

function upgrade(address _newLogicContract) public {
logicContract = _newLogicContract;
}
  1. Handle the redirection using the fallback function inside the proxy contract. Use the low-level delegatecall method to invoke the logic. 
  2. The delegatecall method accepts a specific function signature and parameters to call the function. In the proxy contract, obtain these details from calldata.
  3. Since the delegatecall method is used, the logic contract will run in the calling contract's context and storage. This helps in modifying the state of the calling contract with the logic written in the target contract.
  4. The following contract shows the complete code of the proxy contract used in this example:
pragma solidity^0.4.24;

/**
* Proxy contract which handles state and logic
* Includes the ability to update the logic
*/
contract Proxy is State {
address logicContract;

/**
* @dev Function to change the logic contract
* @param _newLogicContract address of new logic contract
*/
function changeLogic(address _newLogicContract) public {
logicContract = _newLogicContract;
}

/**
* @dev Fallback function to redirect calls
*/
function fallback() public {
require(logicContract.delegatecall(msg.data));
}
}
  1. To test this contract, deploy the proxy and logic contracts independently and use the changeLogic function in the proxy contract to update the logicContract address:
proxyContract.changeLogic(<logic_contract_address>);
  1. Once the logic contract is updated, call the proxy contract with the function signature of the logic contract. In this case, that will be the first four bytes of the keccak256 hash of the function name:
proxyContract.sendTransaction({
input: <function_signature>
...
});
Function signatures are calculated from the first four bytes of the keccak256 hash of a function. For example, to call the increment function, the signature can be calculated in solidity with bytes4(keccak256("increment()")).
  1. Since there is no function in the proxy contract that matches the increment function, the fallback function will be called. As per our fallback function logic, it will use the msg.data value to forward the call to the logic contract.

  2. Assume that we need to modify the function to increment by 2 when called. To do this, deploy the same logic contract with modified logic:
/**
* Logic contract
* Increments the state variable value by 2
*/
contract AddTwo {

uint result;

/**
* @dev
*/
function increment() public {
result = result + 2;
}
}
  1. Update the address of the logic in the proxy contract. This will start executing the new contract for subsequent calls. 

  2. This simple contract can be expanded to do more complex tasks. For example, we can create an interface for the logic contract to follow a certain standard, or add a versioning system on top of the proxy contract. This can vary based on the use case and requirements. 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.139.224