Eternal Upgradeable Pattern in Solidity


Upgradation in smart contract is very important part, to enhance functionality or improve security. But updating a smart contract is different from tradition method. e.g.

storage.sol a seperate storage contract to handle storage. and its library.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Storage{
    
    mapping(bytes32=>uint) uintVars;
    mapping(bytes32=>string) stringVars;
    
    
    function setUintVars(bytes32 varName, uint value)public {
        uintVars[varName] = value;
    }
    
    function getUintVarsValue(bytes32 varName)public view returns(uint){
        return uintVars[varName];
    }
    
    function setStringVars(bytes32 varName, string calldata value)public {
        stringVars[varName] = value;
    }
    
    function getStringVarsValue(bytes32 varName)public view returns(string memory){
        return stringVars[varName];
        
    }

}

library StorageLib{
    
    function setXVar(address storageContractAddress, uint value)public {
        Storage(storageContractAddress).setUintVars(keccak256('x'),value);
    }
    
    function getXVar(address storageContractAddress)public view returns(uint){
       return Storage(storageContractAddress).getUintVarsValue(keccak256('x'));
    } 
    
    function setUserNameVar(address storageContractAddress, string memory value)public {
        Storage(storageContractAddress).setStringVars(keccak256('Username'),value);
    }
    
    function getUserNameVar(address storageContractAddress)public view returns(string memory){
       return Storage(storageContractAddress).getStringVarsValue(keccak256('Username'));
    }
}

Contract version 1:

use the library of storage contract to access the storage contract.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;
import './Storage.sol';

contract BackendContract{
    using StorageLib for address;
    address storageAddr;
    
    constructor(address storageContractAddress){
        storageAddr = storageContractAddress;
    }
    
    function addX(uint a, uint b)public {
        uint x = a + b;
        storageAddr.setXVar(x);
    }
    
    function getX()public view returns(uint){
        return storageAddr.getXVar();
    }
    
}

Backend contract version 2

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;
import './Storage.sol';

contract BackendContractVersion2{
    using StorageLib for address;
    address storageAddr;
    
    constructor(address storageContractAddress){
        storageAddr = storageContractAddress;
    }
    
    function addX(uint a, uint b)public {
        uint x = a + b + 5;
        storageAddr.setXVar(x);
    }
    
    function getX()public view returns(uint){
        return storageAddr.getXVar();
    }
}

Here is another storage contract with assembly to optimize gas cost significantly.

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

contract Storage{
    
    function setUintVars(bytes32 varName, uint value)public {
        assembly {
          sstore(varName, value)
        }
    }
    
    function getUintVarsValue(bytes32 varName)public view returns(uint val){
        assembly {
          val := sload(varName)
        }
    }
    
    function setStringVars(bytes32 varName, string memory value)public {
        assembly {
          sstore(varName, value)
        }
    }
    
    function getStringVarsValue(bytes32 varName)public view returns(string memory val){
        assembly {
          val := sload(varName)
        }
    }

}

library StorageLib{
    
    function setXVar(address storageContractAddress, uint value)public {
        Storage(storageContractAddress).setUintVars(keccak256('x'),value);
    }
    
    function getXVar(address storageContractAddress)public view returns(uint){
       return Storage(storageContractAddress).getUintVarsValue(keccak256('x'));
    } 
    
    function setUserNameVar(address storageContractAddress, string memory value)public {
        Storage(storageContractAddress).setStringVars(keccak256('Username'),value);
    }
    
    function getUserNameVar(address storageContractAddress)public view returns(string memory){
       return Storage(storageContractAddress).getStringVarsValue(keccak256('Username'));
    }
}
,