Skip to content

Instantly share code, notes, and snippets.

@pprados
Created February 23, 2017 11:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pprados/c7e05f479ff0e2f0659136a186162faf to your computer and use it in GitHub Desktop.
Save pprados/c7e05f479ff0e2f0659136a186162faf to your computer and use it in GitHub Desktop.
pragma solidity ^0.4.8;
/**
* Interface shared by Proxy, ContractV1 and ContractV2.
*/
contract Interface {
/** return uint, depend of the current version. */
function doSomething() constant returns(uint);
}
/**
* The version 1 of the contract.
* The attr is initialized to 1000.
* The method doSomething() return attr + version = 1001
*/
contract ContractV1 is Interface {
uint public attr=1000;
/** return attr+newAttr+version (1001). */
function doSomething() constant returns(uint) {
return attr+1;
}
}
/**
* The version 2 of the contract.
* To preserve all the attributs from the v1 version, this version IS a ContractV1.
* All methods can be rewrite, new one can be added and some attributs can be added.
*
* The newAttr is initialized to 100.
* The method doSomething() return attr + newAtttr + version = 1102
*/
contract ContractV2 is Interface {
uint attr;
uint newAttr=100;
/** return attr+newAttr+version (1102). */
function doSomething() constant returns(uint) {
return attr+newAttr+2;
}
/** return 42. Another method in version 2. */
function doOtherThing() constant returns(uint) {
return 42;
}
// -- Technical functions and attributs
/**
* Propagate the states from the v1 to v2.
*/
function ContractV2(ContractV1 contractV1) {
attr=contractV1.attr();
}
}
// -- Technical contract
/**
* A proxy to delegate method to the current version.
*/
contract Proxy {
/** The current version. */
address public currentVersion;
/**
* Call to another contract.
* Use TARGET code with TARGET storage and change caller and callvalue.
*
* @param target The target contract.
* @param returnSize The maximum return size of all methods invoked.
*/
function callCode(address target, int returnSize) internal {
assembly {
let brk := mload(0x40) // Special solidity slot with top memory
calldatacopy(brk, 0, calldatasize) // Copy data to memory at offset brk
let retval := call(sub(gas,150)
,target //address
,0 //value
,brk //mem in
,calldatasize //mem_insz
,brk // reuse mem
,returnSize) // arbitrary return size
// 0 == it threw (jump to bad destination)
jumpi(0x00,iszero(retval)) // Throw (access invalid code)
return(brk,returnSize) // Return returnSize from memory to the caller
}
}
/** Create a proxy with a specific version. */
function Proxy(address _currentVersion){
currentVersion=_currentVersion;
}
/** Delegate all call to the current version. */
function () payable {
callCode(currentVersion,32);
}
/** Change to the current version. */
function changeVersion(address _currentVersion) {
currentVersion=_currentVersion;
}
}
// -------------
/**
* Unit test.
*
* After created an instance,
* - call 'init()'
* - call 'doSomething()' return 1001
* - call 'changeToV2'
* - call 'doSomething()' return 1102
* - call 'doOtherthing()' return 42
*/
contract A_UnitTest {
/** Current proxy. */
Interface aContract;
function init() {
aContract=Interface(new Proxy(new ContractV1()));
}
/** Invoke the method 'doSomething()', valide with a contract v1 or v2. */
function doSomething() constant returns(uint) {
return aContract.doSomething();
}
/** Invoke a method 'doOtherThing()', only valide with a contract v2. */
function doOtherThing() constant returns(uint) {
return ContractV2(aContract).doOtherThing();
}
/** Change to V2. */
function changeToV2() {
ContractV1 contractV1=ContractV1(Proxy(aContract).currentVersion());
Proxy(aContract).changeVersion(new ContractV2(contractV1));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment