-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial Proxy implementation forked from github.com/zeppelinos/labs/t…
…ree/master/upgradeability_using_inherited_storage
- Loading branch information
1 parent
03da07b
commit ce60010
Showing
9 changed files
with
279 additions
and
3 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
/** | ||
* @title IRegistry | ||
* @dev This contract represents the interface of a registry contract | ||
*/ | ||
interface IRegistry { | ||
/** | ||
* @dev This event will be emitted every time a new proxy is created | ||
* @param proxy representing the address of the proxy created | ||
*/ | ||
event ProxyCreated(address proxy); | ||
|
||
/** | ||
* @dev This event will be emitted every time a new implementation is registered | ||
* @param version representing the version name of the registered implementation | ||
* @param implementation representing the address of the registered implementation | ||
*/ | ||
event VersionAdded(string version, address implementation); | ||
|
||
/** | ||
* @dev Registers a new version with its implementation address | ||
* @param version representing the version name of the new implementation to be registered | ||
* @param implementation representing the address of the new implementation to be registered | ||
*/ | ||
function addVersion(string version, address implementation) public; | ||
|
||
/** | ||
* @dev Tells the address of the implementation for a given version | ||
* @param version to query the implementation of | ||
* @return address of the implementation registered for the given version | ||
*/ | ||
function getVersion(string version) public view returns (address); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
/** | ||
* @title Proxy | ||
* @dev Gives the possibility to delegate any call to a foreign implementation. | ||
*/ | ||
contract Proxy { | ||
|
||
/** | ||
* @dev Tells the address of the implementation where every call will be delegated. | ||
* @return address of the implementation to which it will be delegated | ||
*/ | ||
function implementation() public view returns (address); | ||
|
||
/** | ||
* @dev Fallback function allowing to perform a delegatecall to the given implementation. | ||
* This function will return whatever the implementation call returns | ||
*/ | ||
function () payable public { | ||
address _impl = implementation(); | ||
require(_impl != address(0)); | ||
|
||
assembly { | ||
let ptr := mload(0x40) | ||
calldatacopy(ptr, 0, calldatasize) | ||
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) | ||
let size := returndatasize | ||
returndatacopy(ptr, 0, size) | ||
|
||
switch result | ||
case 0 { revert(ptr, size) } | ||
default { return(ptr, size) } | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
import './IRegistry.sol'; | ||
import './Upgradeable.sol'; | ||
import './UpgradeabilityProxy.sol'; | ||
|
||
/** | ||
* @title Registry | ||
* @dev This contract works as a registry of versions, it holds the implementations for the registered versions. | ||
*/ | ||
contract Registry is IRegistry { | ||
// Mapping of versions to implementations of different functions | ||
mapping (string => address) internal versions; | ||
|
||
/** | ||
* @dev Registers a new version with its implementation address | ||
* @param version representing the version name of the new implementation to be registered | ||
* @param implementation representing the address of the new implementation to be registered | ||
*/ | ||
function addVersion(string version, address implementation) public { | ||
require(versions[version] == 0x0); | ||
versions[version] = implementation; | ||
VersionAdded(version, implementation); | ||
} | ||
|
||
/** | ||
* @dev Tells the address of the implementation for a given version | ||
* @param version to query the implementation of | ||
* @return address of the implementation registered for the given version | ||
*/ | ||
function getVersion(string version) public view returns (address) { | ||
return versions[version]; | ||
} | ||
|
||
/** | ||
* @dev Creates an upgradeable proxy | ||
* @param version representing the first version to be set for the proxy | ||
* @return address of the new proxy created | ||
*/ | ||
function createProxy(string version) public payable returns (UpgradeabilityProxy) { | ||
UpgradeabilityProxy proxy = new UpgradeabilityProxy(version); | ||
Upgradeable(proxy).initialize.value(msg.value)(msg.sender); | ||
ProxyCreated(proxy); | ||
return proxy; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
import './Proxy.sol'; | ||
import './IRegistry.sol'; | ||
import './UpgradeabilityStorage.sol'; | ||
|
||
/** | ||
* @title UpgradeabilityProxy | ||
* @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded | ||
*/ | ||
contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage { | ||
|
||
/** | ||
* @dev Constructor function | ||
*/ | ||
function UpgradeabilityProxy(string _version) public { | ||
registry = IRegistry(msg.sender); | ||
upgradeTo(_version); | ||
} | ||
|
||
/** | ||
* @dev Upgrades the implementation to the requested version | ||
* @param _version representing the version name of the new implementation to be set | ||
*/ | ||
function upgradeTo(string _version) public { | ||
_implementation = registry.getVersion(_version); | ||
} | ||
|
||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
import './IRegistry.sol'; | ||
|
||
/** | ||
* @title UpgradeabilityStorage | ||
* @dev This contract holds all the necessary state variables to support the upgrade functionality | ||
*/ | ||
contract UpgradeabilityStorage { | ||
// Versions registry | ||
IRegistry internal registry; | ||
|
||
// Address of the current implementation | ||
address internal _implementation; | ||
|
||
/** | ||
* @dev Tells the address of the current implementation | ||
* @return address of the current implementation | ||
*/ | ||
function implementation() public view returns (address) { | ||
return _implementation; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
import './UpgradeabilityStorage.sol'; | ||
|
||
/** | ||
* @title Upgradeable | ||
* @dev This contract holds all the minimum required functionality for a behavior to be upgradeable. | ||
* This means, required state variables for owned upgradeability purpose and simple initialization validation. | ||
*/ | ||
contract Upgradeable is UpgradeabilityStorage { | ||
/** | ||
* @dev Validates the caller is the versions registry. | ||
* THIS FUNCTION SHOULD BE OVERRIDDEN CALLING SUPER | ||
* @param sender representing the address deploying the initial behavior of the contract | ||
*/ | ||
function initialize(address sender) public payable { | ||
require(msg.sender == address(registry)); | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
pragma solidity ^0.4.18; | ||
|
||
import '../Upgradeable.sol'; | ||
|
||
contract TokenV1_0 is Upgradeable { | ||
mapping (address => uint) balances; | ||
|
||
event Transfer(address indexed from, address indexed to, uint256 _value); | ||
|
||
function initialize(address sender) public payable { | ||
super.initialize(sender); | ||
mint(sender, 10000); | ||
} | ||
|
||
function balanceOf(address addr) public view returns (uint) { | ||
return balances[addr]; | ||
} | ||
|
||
function transfer(address to, uint256 value) public { | ||
require(balances[msg.sender] >= value); | ||
balances[msg.sender] -= value; | ||
balances[to] += value; | ||
Transfer(msg.sender, to, value); | ||
} | ||
|
||
function mint(address to, uint256 value) public { | ||
balances[to] += value; | ||
Transfer(0x0, to, value); | ||
} | ||
|
||
} | ||
|
||
contract TokenV1_1 is TokenV1_0 { | ||
mapping (address => mapping (address => uint)) allowances; | ||
|
||
function transferFrom(address from, address to, uint256 value) public { | ||
require(allowances[from][msg.sender] >= value); | ||
allowances[from][msg.sender] -= value; | ||
balances[from] -= value; | ||
balances[to] += value; | ||
Transfer(from, to, value); | ||
} | ||
|
||
function approve(address spender, uint256 value) public { | ||
allowances[msg.sender][spender] = value; | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const TokenV1_0 = artifacts.require('TokenV1_0') | ||
const TokenV1_1 = artifacts.require('TokenV1_1') | ||
|
||
const Registry = artifacts.require('Registry') | ||
const Proxy = artifacts.require('UpgradeabilityProxy') | ||
|
||
contract('Upgradeable', function ([sender, receiver]) { | ||
|
||
it('should work', async function () { | ||
const impl_v1_0 = await TokenV1_0.new() | ||
const impl_v1_1 = await TokenV1_1.new() | ||
|
||
const registry = await Registry.new() | ||
await registry.addVersion("1.0", impl_v1_0.address) | ||
await registry.addVersion("1.1", impl_v1_1.address) | ||
|
||
const {logs} = await registry.createProxy("1.0") | ||
|
||
const {proxy} = logs.find(l => l.event === 'ProxyCreated').args | ||
|
||
await TokenV1_0.at(proxy).mint(sender, 100) | ||
|
||
await Proxy.at(proxy).upgradeTo("1.1") | ||
|
||
await TokenV1_1.at(proxy).mint(sender, 100) | ||
|
||
const transferTx = await TokenV1_1.at(proxy).transfer(receiver, 10, { from: sender }) | ||
|
||
console.log("Transfer TX gas cost using Inherited Storage Proxy", transferTx.receipt.gasUsed); | ||
|
||
const balance = await TokenV1_1.at(proxy).balanceOf(sender) | ||
assert(balance.eq(10190)) | ||
|
||
}) | ||
|
||
}) |