Your resource for web content, online publishing
and the distribution of digital products.
«  
  »
S M T W T F S
 
 
 
 
1
 
2
 
3
 
4
 
5
 
6
 
7
 
8
 
9
 
 
 
 
 
 
 
16
 
17
 
18
 
19
 
20
 
21
 
22
 
23
 
24
 
25
 
26
 
27
 
28
 
29
 
30
 
 

Upgrade contracts in Solidity

DATE POSTED:February 1, 2021
The planet from a distance with light connections on surfacePhoto by NASA on UnsplashUpgradability in Solidity — Data and App contracts

Once a smart contract is deployed it cannot be changed. However, there are good reasons one might want to change a contract after it is deployed:

  • a bug is found and needs to be fixed
  • business rules have changed and require the contract code to be updated
  • access to owner’s private key is lost or compromised
  • gas fees have increased and code optimization is required

In this post we’ll talk about separating the contract into a Data and App contracts. This works well unless you need to change data structures. There are alternatives, such as contract migration, app/proxy/data and eternal storage, to name a few. Each comes with its own benefits and drawbacks, so no one solution will fit all use cases.

Simple Contract: Num

Num.sol:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract Num {
uint num;

function set(uint n) external {
num = n;
} function get() external view returns (uint) {
return num;
}}

We’ll focus on the method of separation instead of the actual app

  1. Create App contract
// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract NumApp {

}

2. Move logic that can change from Data to App contract

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract NumApp {
function set(uint n) external {
num = n;
} function get() external view returns (uint) {
return num;
}}

3. Create stubs for missing functions (copy function definition from data to app contract)

Here we will create a public function which uses the internal set function

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract NumApp {
function setNum(uint n) external {
set(n);
}

function getNum() ex returns (uint) {
return get();
}}

4. Move internal/private functions in use in App contract to Data contract interface and change to external in both contracts

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract NumApp {
function setNum(uint n) external {
set(n);
}

function getNum() external returns (uint) {
return get();
}}interface Num {
set(uint n) external;
get() external returns (uint);}

5. Create dataContract state variable inside App contract and initialize it in constructor

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract NumApp {
Num dataContract; constructor(address dataContractAddress) {
dataContract = Num(dataContractAddress);
} function setNum(uint n) external {
set(n);
}

function getNum() external returns (uint) {
return get();
}
}interface Num {
set(uint n) external;
get() external returns (uint);}

6. Change App contract functions that are present in the Data contract to dataContract. inside the App contract

// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract NumApp {
Num dataContract;constructor(address dataContractAddress) {
dataContract = Num(dataContractAddress);
} function setNum(uint n) external {
dataContract.set(n);
}

function getNum() external returns (uint) {
return dataContract.get();
}
}interface Num {
set(uint n) external;
get() external returns (uint);}

7. Restrict Data contract callers

Inside the Data contract:

  • create mapping(address => bool) private authorizedAddresses
  • add if missing:
address private contractOwnerconstructor() {
contractOwner = msg.sender;
}modifier requireContractOwner() {
require(msg.sender == contractOwner, "Caller is not contract owner");
_;
}
  • add a way to authorize and deauthorize contracts:
function authorizeContract(address appContract) external requireContractOwner {
authorizedContracts[appContract] = true;
}function deauthorizeContract(address appContract) external requireContractOwner {
delete authorizedContracts[appContract];
}
  • add modifier to functions that will be called externally to make sure the caller is authorized
modifier isCallerAuthorized() {
require(authorizedContracts[msg.sender] == true, "Caller is not authorized");
_;
}Final Data Contract// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract Num {
uint num;

address private contractOwner;
mapping(address => bool) private authorizedContracts;

constructor() {
contractOwner = msg.sender;
}

modifier requireContractOwner() {
require(msg.sender == contractOwner, "Caller is not contract owner");
_;
}

modifier isCallerAuthorized() {
require(authorizedContracts[msg.sender] == true, "Caller is not authorized");
_;
}

function authorizeContract(address appContract) external requireContractOwner {
authorizedContracts[appContract] = true;
}

function deauthorizeContract(address appContract) external requireContractOwner {
delete authorizedContracts[appContract];
}

function set(uint n) external isCallerAuthorized {
num = n;
}

function get() external view isCallerAuthorized returns (uint) {
return num;
}
}Final App Contract// SPDX-License-Identifier: GPL-3.0
pragma solidity >= 0.8.0 < 0.9.0;contract NumApp {
Num dataContract;

constructor(address dataContractAddress) {
dataContract = Num(dataContractAddress);
}

function setNum(uint n) external {
dataContract.set(n);
}

function getNum() external view returns (uint) {
return dataContract.get();
}
}interface Num {
function set(uint n) external;
function get() external view returns (uint);
}How to deploy and test
  1. Go to https://remix.ethereum.org
  2. Create Num.sol and paste Data contract
  3. Create NumApp.sol and paste App contract
  4. Compiler version needs to be at least 0.8.0
  5. Go to the deploy tab (3rd from top to bottom)
  6. Deploy Num
  7. Copy Num address, select NumApp contract paste address next to deploy button and click deploy
  8. Copy NumApp contract address, open Num contract, paste next to authorize and click button

Noteworthy:

  • NumApp should not work before being authorized
  • To change App contract, create new App contract, deploy in the same way, authorize it and deauthorize previous

Comments and suggestions are welcome.

Join Coinmonks Telegram group and learn about crypto trading and investingAlso, ReadGet Best Software Deals Directly In Your Inbox

Upgrade contracts in Solidity was originally published in Coinmonks on Medium, where people are continuing the conversation by highlighting and responding to this story.