Coming to one of the basic principles at Ethereum that is a Smart Contract cannot be modified after deployment.
BUT, you can still have Upgradable Smart Contracts if you put the following into consideration
This has to be planed from the start. The key point is number 4. But all the others are essential to have a real and smooth Smart Contract Upgrade.
So, you will need to design your smart contract taking in consideration the following 5 points:
- Keep your smart contracts modular and fairly separate rules and logic from data structure. So if you will need to change something, you will change just the related contract and you will not need to change many or all contracts.
- You should be prepared by having an emergency stop or circuit-breaker to be able to stop all operations during any migration. Because you do not want to be in a situation where people can still update/insert data to the old version of the smart contract while you are migrating and thereafter.
- You should previously provided the ability to read all the data from your smart contract. Of course you can do a permissioned read by restricting reading all data to the owner or any other trusted user or even another smart contract. You will need to read from the old version of your smart contract and insert in the new version.
- You will use of of the following strategies to communicate with your smart contract. I copied them from Smart Contact Best Practices:
Upgrading Broken Contracts
Code will need to be changed if errors are discovered or if
improvements need to be made. It is no good to discover a bug, but
have no way to deal with it
...
However, there are two basic approaches that are most commonly used.
The simpler of the two is to have a registry contract that holds the
address of the latest version of the contract. A more seamless
approach for contract users is to have a contract that forwards calls
and data onto the latest version of the contract.
Example 1: Use a registry contract to store latest version of a contract
In this example, the calls aren't forwarded, so users should fetch the
current address each time before interacting with it.
contract SomeRegister {
address backendContract;
address[] previousBackends;
address owner;
function SomeRegister() {
owner = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner)
_;
}
function changeBackend(address newBackend) public
onlyOwner()
returns (bool)
{
if(newBackend != backendContract) {
previousBackends.push(backendContract);
backendContract = newBackend;
return true;
}
return false;
}
}
There are two main disadvantages to this approach:
Users must always look up the current address, and anyone who fails
to do so risks using an old version of the contract
You will need to think carefully about how to deal with the contract
data when you replace the contract
The alternate approach is to have a contract forward calls and data to
the latest version of the contract:
Example 2: Use a DELEGATECALL to forward data and calls
contract Relay {
address public currentVersion;
address public owner;
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
function Relay(address initAddr) {
currentVersion = initAddr;
owner = msg.sender; // this owner may be another contract with multisig, not a single contract owner
}
function changeContract(address newVersion) public
onlyOwner()
{
currentVersion = newVersion;
}
function() {
require(currentVersion.delegatecall(msg.data));
}
}
This approach avoids the previous problems but has problems of its
own. You must be extremely careful with how you store data in this
contract. If your new contract has a different storage layout than the
first, your data may end up corrupted. Additionally, this simple
version of the pattern cannot return values from functions, only
forward them, which limits its applicability. (More complex
implementations attempt to solve this with in-line assembly code
and a registry of return sizes.)
Regardless of your approach, it is important to have some way to
upgrade your contracts, or they will become unusable when the
inevitable bugs are discovered in them.
However, I also recommend checking Proxy Libraries in Solidity that is posted by Zeppelin Solutions and Aragon. There is a planning to make an industry standard for this matter.
- You have to have a good testing strategies and tactics. Because the
cost of updating the your smart contract can really ruin your life.
I created a story on Medium for this with title: Essential Design Consideration for Ethereum dApps (1): Upgradeable Smart Contracts and I provided a sample for every point of the above 5.