Created
May 24, 2016 09:49
-
-
Save Arachnid/4ca9da48d51e23e5cfe0f0e14dd6318f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Base contract that all upgradeable contracts should use. | |
* | |
* Contracts implementing this interface are all called using delegatecall from | |
* a dispatcher. As a result, the _sizes and _dest variables are shared with the | |
* dispatcher contract, which allows the called contract to update these at will. | |
* | |
* _sizes is a map of function signatures to return value sizes. Due to EVM | |
* limitations, these need to be populated by the target contract, so the | |
* dispatcher knows how many bytes of data to return from called functions. | |
* Unfortunately, this makes variable-length return values impossible. | |
* | |
* _dest is the address of the contract currently implementing all the | |
* functionality of the composite contract. Contracts should update this by | |
* calling the internal function `replace`, which updates _dest and calls | |
* `initialize()` on the new contract. | |
* | |
* When upgrading a contract, restrictions on permissible changes to the set of | |
* storage variables must be observed. New variables may be added, but existing | |
* ones may not be deleted or replaced. Changing variable names is acceptable. | |
* Structs in arrays may not be modified, but structs in maps can be, following | |
* the same rules described above. | |
*/ | |
contract Upgradeable { | |
mapping(bytes4=>uint32) _sizes; | |
address _dest; | |
/** | |
* This function is called using delegatecall from the dispatcher when the | |
* target contract is first initialized. It should use this opportunity to | |
* insert any return data sizes in _sizes, and perform any other upgrades | |
* necessary to change over from the old contract implementation (if any). | |
* | |
* Implementers of this function should either perform strictly harmless, | |
* idempotent operations like setting return sizes, or use some form of | |
* access control, to prevent outside callers. | |
*/ | |
function initialize(); | |
/** | |
* Performs a handover to a new implementing contract. | |
*/ | |
function replace(address target) internal { | |
_dest = target; | |
target.delegatecall(bytes4(sha3("initialize()"))); | |
} | |
} | |
/** | |
* The dispatcher is a minimal 'shim' that dispatches calls to a targeted | |
* contract. Calls are made using 'delegatecall', meaning all storage and value | |
* is kept on the dispatcher. As a result, when the target is updated, the new | |
* contract inherits all the stored data and value from the old contract. | |
*/ | |
contract Dispatcher is Upgradeable { | |
function Dispatcher(address target) { | |
replace(target); | |
} | |
function initialize() { | |
// Should only be called by on target contracts, not on the dispatcher | |
throw; | |
} | |
function() { | |
bytes4 sig; | |
assembly { sig := calldataload(0) } | |
var len = _sizes[sig]; | |
var target = _dest; | |
assembly { | |
// return _dest.delegatecall(msg.data) | |
calldatacopy(0x0, 0x0, calldatasize) | |
delegatecall(sub(gas, 10000), target, 0x0, calldatasize, 0, len) | |
return(0, len) | |
} | |
} | |
} | |
contract Example is Upgradeable { | |
uint _value; | |
function initialize() { | |
_sizes[bytes4(sha3("getUint()"))] = 32; | |
} | |
function getUint() returns (uint) { | |
return _value; | |
} | |
function setUint(uint value) { | |
_value = value; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I guess this method would not work with the Zeppelin contracts, as they have BasicToken that has balances as a mapping, and if you change that basicToken to a newer version, you would lose all the stored data within balances, unless there is a way to override balances with the calling contract. I might be confused here, but if any of you could shed some light, its appreciated.