├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── bounding_curve ├── contracts │ ├── ERC20Test.sol │ └── ERC4626BoundingCurve.sol ├── hardhat.config.js ├── package-lock.json ├── package.json └── test │ └── main.test.js ├── contracts-upgradeability-transpiler ├── .eslintrc.json ├── .gitignore ├── .soliumignore ├── .soliumrc.json ├── contracts │ ├── @openzeppelin │ │ └── contracts │ │ │ ├── GSN │ │ │ └── ContextUpgradable.sol │ │ │ └── token │ │ │ └── ERC20 │ │ │ ├── ERC20DetailedUpgradable.sol │ │ │ └── ERC20Upgradable.sol │ ├── GLDToken.sol │ ├── GLDTokenUpgradable.sol │ ├── Simple.sol │ └── SimpleUpgradable.sol ├── index.js ├── networks.js ├── package-lock.json ├── package.json └── src │ ├── ast-utils.js │ ├── get-inheritance-chain.js │ ├── schemas │ └── ast-node.js │ ├── transformations │ ├── append-directive.js │ ├── build-super-calls-for-chain.js │ ├── fix-import-directives.js │ ├── index.js │ ├── prepend-base-class.js │ ├── purge-contracts.js │ ├── purge-var-inits.js │ ├── transform-constructor.js │ ├── transform-contract-name.js │ └── transform-parents.js │ └── transpiler.js ├── crosschain-env ├── optimism │ ├── docker-compose.yml │ └── envs │ │ ├── batches.env │ │ ├── dtl.env │ │ └── geth.env └── package.json ├── extensibility-study ├── base-scenario │ ├── contracts │ │ ├── A.sol │ │ ├── AStor.sol │ │ ├── A_v2.sol │ │ ├── B.sol │ │ ├── BFacade.sol │ │ ├── B_Av2.sol │ │ ├── Initializable.sol │ │ ├── Migrations.sol │ │ └── upgradeability │ │ │ ├── OwnedUpgradeabilityProxy.sol │ │ │ ├── Proxy.sol │ │ │ ├── UpgradeabilityProxy.sol │ │ │ └── UpgradeabilityProxyFactory.sol │ ├── migrations │ │ └── 1_initial_migration.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── BFacade.js │ │ └── helpers │ │ │ ├── assertRevert.js │ │ │ └── encodeCall.js │ └── truffle-config.js ├── child-upgradeability │ ├── contracts │ │ ├── A.sol │ │ ├── AStor.sol │ │ ├── A_v2.sol │ │ ├── B.sol │ │ ├── BFacade.sol │ │ ├── B_Av2.sol │ │ ├── B_v2.sol │ │ ├── B_v2Facade.sol │ │ ├── Initializable.sol │ │ ├── Migrations.sol │ │ └── upgradeability │ │ │ ├── ExtensibilityProxy.sol │ │ │ ├── OwnedUpgradeabilityProxy.sol │ │ │ ├── Proxy.sol │ │ │ ├── UpgradeabilityProxy.sol │ │ │ └── UpgradeabilityProxyFactory.sol │ ├── migrations │ │ └── 1_initial_migration.js │ ├── package-lock.json │ ├── package.json │ ├── test │ │ ├── BFacade.js │ │ └── helpers │ │ │ ├── assertRevert.js │ │ │ └── encodeCall.js │ └── truffle-config.js └── readme.md ├── governance-with-multisig ├── .gitignore ├── README.md ├── change-admin.js ├── contracts │ ├── .gitkeep │ ├── EthBox.sol │ └── MultiSigWallet.sol ├── ganache.sh ├── migrations │ └── .gitkeep ├── package-lock.json ├── package.json ├── query.js ├── submit-upgrade.js └── truffle-config.js ├── initializable_with_multiple_inheritance ├── contracts │ ├── Initializable.sol │ └── test │ │ └── Inheritance.sol ├── package-lock.json ├── package.json ├── readme.md ├── test │ └── Initializable.js └── truffle.js ├── initializable_with_multiple_inheritance_alt ├── contracts │ ├── Initializable.sol │ └── test │ │ └── Inheritance.sol ├── migrations │ └── .gitkeep ├── package-lock.json ├── package.json ├── readme.md ├── test │ └── Initializable.js └── truffle.js ├── initializable_with_multiple_inheritance_safer ├── contracts │ ├── Initializable.sol │ └── test │ │ └── Inheritance.sol ├── migrations │ └── .gitkeep ├── package-lock.json ├── package.json ├── readme.md ├── test │ └── Initializable.js └── truffle.js ├── initializer_contracts ├── .gitignore ├── README.md ├── buidler-config.js ├── contracts │ ├── AdminUpgradeabilityProxy.sol │ ├── Greeter.sol │ ├── Proxy.sol │ └── UpgradeabilityProxy.sol ├── package-lock.json ├── package.json └── scripts │ └── poc-1.js ├── initializer_contracts_with_args ├── .gitignore ├── README.md ├── buidler-config.js ├── contracts │ ├── AdminUpgradeabilityProxy.sol │ ├── Greeter.sol │ ├── MyNFT.sol │ ├── MyToken.sol │ ├── Proxy.sol │ └── UpgradeabilityProxy.sol ├── package-lock.json ├── package.json └── scripts │ └── poc-2.js ├── initializer_with_sol_editing ├── README.md ├── contracts │ ├── AdminUpgradeabilityProxy.sol │ ├── MyContract.sol │ ├── MyContract_implementation.sol │ ├── MyContract_initializer.sol │ ├── MyNFT.sol │ ├── MyNFT_implementation.sol │ ├── MyNFT_initializer.sol │ ├── MyToken.sol │ ├── MyToken_implementation.sol │ ├── MyToken_initializer.sol │ ├── MyToken_initializer_optimized.sol │ ├── Proxy.sol │ └── UpgradeabilityProxy.sol ├── index.js ├── package-lock.json ├── package.json ├── test │ └── Deploy.test.js └── truffle.js ├── migrating_legacy_token_managed ├── README.md ├── contracts │ ├── BurnContract.sol │ ├── LegacyToken.sol │ ├── ManagedMigrationToken.sol │ ├── Migrations.sol │ ├── TestingImports.sol │ └── Token_V1.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── test │ └── migrateFromERC20.js └── truffle.js ├── migrating_legacy_token_opt_in ├── README.md ├── contracts │ ├── TestingImports.sol │ ├── latest_erc20_token │ │ ├── BurnContract.sol │ │ ├── LegacyToken.sol │ │ ├── Migrations.sol │ │ ├── OptInMigrationToken.sol │ │ └── Token_V1.sol │ └── old_erc20_token │ │ ├── MyToken_V0.sol │ │ ├── MyToken_V1.sol │ │ └── zeppelin-solidity-1.1.0 │ │ ├── lifecycle │ │ └── Pausable.sol │ │ ├── math │ │ └── SafeMath.sol │ │ ├── ownership │ │ └── Ownable.sol │ │ └── token │ │ ├── BasicToken.sol │ │ ├── ERC20.sol │ │ ├── ERC20Basic.sol │ │ ├── MintableToken.sol │ │ ├── PausableToken.sol │ │ ├── StandardToken.sol │ │ └── TokenTimelock.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── test │ ├── migrateFromLatestERC20.js │ └── migrateFromOldERC20.js └── truffle.js ├── package-lock.json ├── package.json ├── scripts └── test.sh ├── upgradeability_ownership ├── contracts │ ├── Migrations.sol │ ├── OwnedUpgradeabilityProxy.sol │ ├── OwnedUpgradeabilityStorage.sol │ ├── Proxy.sol │ ├── UpgradeabilityProxy.sol │ ├── UpgradeabilityStorage.sol │ ├── ownership │ │ ├── Ownable.sol │ │ └── OwnableStorage.sol │ └── test │ │ ├── Token_V0.sol │ │ └── Token_V1.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── readme.md ├── test │ ├── OwnedUpgradeabilityProxy.js │ ├── Token_V0.js │ ├── Token_V1.js │ ├── behaviors │ │ └── token_v0.js │ └── helpers │ │ ├── assertRevert.js │ │ └── encodeCall.js └── truffle.js ├── upgradeability_using_eternal_storage ├── contracts │ ├── EternalStorage.sol │ ├── EternalStorageProxy.sol │ ├── Migrations.sol │ ├── OwnedUpgradeabilityProxy.sol │ ├── Proxy.sol │ ├── UpgradeabilityOwnerStorage.sol │ ├── UpgradeabilityProxy.sol │ ├── UpgradeabilityStorage.sol │ └── test │ │ ├── Ownable.sol │ │ ├── Token_V0.sol │ │ ├── Token_V1.sol │ │ ├── Token_V2.sol │ │ └── Token_V3.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── readme.md ├── test │ ├── EternalStorageProxy.js │ ├── OwnedUpgradeabilityProxy.js │ ├── Token_V0.js │ ├── Token_V1.js │ ├── Token_V2.js │ ├── Token_V3.js │ ├── behaviors │ │ ├── token_v0.js │ │ ├── token_v1.js │ │ └── token_v2.js │ └── helpers │ │ └── assertRevert.js └── truffle.js ├── upgradeability_using_inherited_storage ├── contracts │ ├── IRegistry.sol │ ├── Migrations.sol │ ├── Proxy.sol │ ├── Registry.sol │ ├── UpgradeabilityProxy.sol │ ├── UpgradeabilityStorage.sol │ ├── Upgradeable.sol │ └── test │ │ └── Token.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── readme.md ├── test │ └── Upgradeable.js └── truffle.js ├── upgradeability_using_unstructured_storage ├── contracts │ ├── Migrations.sol │ ├── OwnedUpgradeabilityProxy.sol │ ├── Proxy.sol │ ├── UpgradeabilityProxy.sol │ └── test │ │ ├── Ownable.sol │ │ ├── Token_V0.sol │ │ └── Token_V1.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── readme.md ├── test │ ├── OwnedUpgradeabilityProxy.js │ ├── Storage.js │ ├── Token_V0.js │ ├── Token_V1.js │ ├── behaviors │ │ └── token_v0.js │ └── helpers │ │ ├── assertRevert.js │ │ └── encodeCall.js └── truffle.js └── upgradeability_with_vtable ├── README.md ├── contracts ├── IRegistry.sol ├── Migrations.sol ├── Proxy.sol ├── Registry.sol ├── UpgradeabilityProxy.sol ├── UpgradeabilityStorage.sol ├── Upgradeable.sol └── test │ └── Token.sol ├── migrations └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── test ├── Upgradeable.js └── helpers │ ├── assertRevert.js │ └── signature.js └── truffle.js /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .vscode 3 | artifacts/ 4 | build/ 5 | cache/ 6 | coverage/ 7 | 8 | 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 zOS Global Limited. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenZeppelin Labs 2 | 3 | ## Description 4 | 5 | **OpenZeppelin labs** is a space for the community to interact, exchange ideas, and get involved in the development of the OpenZeppelin Platform as soon as possible. **Any code here is not meant for production use.** Check out the [`openzeppelin-sdk`](https://github.com/OpenZeppelin/openzeppelin-sdk) and [`openzeppelin-contracts`](https://github.com/OpenZeppelin/openzeppelin-contracts) for production-ready code to use in your project. 6 | 7 | Over time, we'll be introducing various components of the platform to our Github repo. We invite you to collaborate on how the platform will be designed and function. To participate, you can submit thoughts, ideas, and proposals in the form of pull requests and Github issues. 8 | 9 | - [Read the announcement on our blog](https://blog.zeppelinos.org/announcing-zeppelin_os-labs/). 10 | - [Join our forum](https://forum.openzeppelin.com) 11 | 12 | ## Structure 13 | 14 | This repository holds many strategies, approaches and study cases that we are investigating and developing in favor of the OpenZeppelin SDK. Each experiment should be a project itself so we can make sure their contracts and configurations don't conflict between each other. 15 | 16 | -------------------------------------------------------------------------------- /bounding_curve/contracts/ERC20Test.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.14; 4 | 5 | import "@openzeppelin/contracts/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract ERC20Test is ERC20 { 8 | constructor( 9 | string memory _name, 10 | string memory _symbol 11 | ) 12 | ERC20(_name, _symbol) 13 | {} 14 | 15 | function mint(uint256 value) external { 16 | _mint(msg.sender, value); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bounding_curve/contracts/ERC4626BoundingCurve.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.14; 4 | 5 | import "@prb/math/contracts/PRBMathUD60x18.sol"; 6 | import "@openzeppelin/contracts/contracts/token/ERC20/extensions/ERC20TokenizedVault.sol"; 7 | import "@openzeppelin/contracts/contracts/utils/math/SafeCast.sol"; 8 | 9 | contract ERC4626BoundingCurve is ERC20TokenizedVault { 10 | using PRBMathUD60x18 for uint256; 11 | 12 | uint256 public immutable BUY_CURVE_PARAM; 13 | uint256 public immutable SELL_CURVE_PARAM; 14 | 15 | constructor( 16 | string memory _name, 17 | string memory _symbol, 18 | IERC20Metadata _asset, 19 | uint256 _buyCurveParam, 20 | uint256 _sellCurveParam 21 | ) 22 | ERC20(_name, _symbol) 23 | ERC20TokenizedVault(_asset) 24 | { 25 | require(_buyCurveParam <= _sellCurveParam, "ERC4626BoundingCurve: unsafe params"); 26 | BUY_CURVE_PARAM = _buyCurveParam; 27 | SELL_CURVE_PARAM = _sellCurveParam; 28 | } 29 | 30 | function previewDeposit(uint256 assets) public view override returns (uint256 shares) { 31 | uint256 tA = totalAssets(); 32 | uint256 tS = totalSupply(); 33 | 34 | return tA == 0 35 | ? assets 36 | : tS.fromUint().mul(PRBMathUD60x18.div(tA + assets, tA).pow(BUY_CURVE_PARAM)).toUint() - tS; 37 | } 38 | 39 | function previewMint(uint256 shares) public view override returns (uint256 assets) { 40 | uint256 tA = totalAssets(); 41 | uint256 tS = totalSupply(); 42 | 43 | return tS == 0 44 | ? shares 45 | : tA.fromUint().mul(PRBMathUD60x18.div(tS + shares, tS).pow(BUY_CURVE_PARAM.inv())).toUint() - tA; 46 | } 47 | 48 | function previewWithdraw(uint256 assets) public view override returns (uint256 shares) { 49 | uint256 tA = totalAssets(); 50 | uint256 tS = totalSupply(); 51 | 52 | return tA == 0 53 | ? assets == 0 ? 0 : type(uint256).max 54 | : tS - tS.fromUint().div(PRBMathUD60x18.div(tA, tA - assets).pow(SELL_CURVE_PARAM)).toUint(); 55 | } 56 | 57 | function previewRedeem(uint256 shares) public view override returns (uint256 assets) { 58 | uint256 tA = totalAssets(); 59 | uint256 tS = totalSupply(); 60 | 61 | return tS == 0 62 | ? shares == 0 ? 0 : type(uint256).max 63 | : tA - tA.fromUint().div(PRBMathUD60x18.div(tS, tS - shares).pow(SELL_CURVE_PARAM.inv())).toUint(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /bounding_curve/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "prepublish": "rimraf artifacts build cache generated", 4 | "compile": "hardhat compile", 5 | "test": "hardhat test" 6 | }, 7 | "dependencies": { 8 | "@openzeppelin/contracts": "git://github.com/OpenZeppelin/openzeppelin-contracts.git", 9 | "@prb/math": "^2.5.0" 10 | }, 11 | "devDependencies": { 12 | "@amxx/hre": "^0.0.6", 13 | "hardhat": "^2.9.6" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true 5 | }, 6 | "parserOptions": { 7 | "ecmaVersion": 8 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | .openzeppelin 3 | 4 | .openzeppelin/.session 5 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | contracts/Migrations.sol 3 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:recommended", 3 | "plugins": [ 4 | "security" 5 | ], 6 | "rules": { 7 | "quotes": [ 8 | "error", 9 | "double" 10 | ], 11 | "indentation": [ 12 | "error", 13 | 4 14 | ], 15 | "linebreak-style": [ 16 | "error", 17 | "unix" 18 | ] 19 | } 20 | } -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/contracts/@openzeppelin/contracts/GSN/ContextUpgradable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | import "@openzeppelin/upgrades/contracts/Initializable.sol"; 3 | 4 | /* 5 | * @dev Provides information about the current execution context, including the 6 | * sender of the transaction and its data. While these are generally available 7 | * via msg.sender and msg.data, they should not be accessed in such a direct 8 | * manner, since when dealing with GSN meta-transactions the account sending and 9 | * paying for execution may not be the actual sender (as far as an application 10 | * is concerned). 11 | * 12 | * This contract is only required for intermediate, library-like contracts. 13 | */ 14 | contract ContextUpgradable is Initializable { 15 | function initialize() external initializer { 16 | __init(true); 17 | } 18 | 19 | function __init(bool callChain) internal { 20 | 21 | 22 | 23 | } 24 | 25 | // Empty internal constructor, to prevent people from mistakenly deploying 26 | // an instance of this contract, which should be used via inheritance. 27 | 28 | // solhint-disable-previous-line no-empty-blocks 29 | 30 | function _msgSender() internal view returns (address payable) { 31 | return msg.sender; 32 | } 33 | 34 | function _msgData() internal view returns (bytes memory) { 35 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 36 | return msg.data; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/contracts/@openzeppelin/contracts/token/ERC20/ERC20DetailedUpgradable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 4 | import "@openzeppelin/upgrades/contracts/Initializable.sol"; 5 | 6 | /** 7 | * @dev Optional functions from the ERC20 standard. 8 | */ 9 | contract ERC20DetailedUpgradable is Initializable, IERC20 { 10 | 11 | function __init(bool callChain, string memory name, string memory symbol, uint8 decimals) internal { 12 | 13 | 14 | 15 | _name = name; 16 | _symbol = symbol; 17 | _decimals = decimals; 18 | 19 | } 20 | 21 | string private _name; 22 | string private _symbol; 23 | uint8 private _decimals; 24 | 25 | /** 26 | * @dev Sets the values for `name`, `symbol`, and `decimals`. All three of 27 | * these values are immutable: they can only be set once during 28 | * construction. 29 | */ 30 | 31 | 32 | /** 33 | * @dev Returns the name of the token. 34 | */ 35 | function name() public view returns (string memory) { 36 | return _name; 37 | } 38 | 39 | /** 40 | * @dev Returns the symbol of the token, usually a shorter version of the 41 | * name. 42 | */ 43 | function symbol() public view returns (string memory) { 44 | return _symbol; 45 | } 46 | 47 | /** 48 | * @dev Returns the number of decimals used to get its user representation. 49 | * For example, if `decimals` equals `2`, a balance of `505` tokens should 50 | * be displayed to a user as `5,05` (`505 / 10 ** 2`). 51 | * 52 | * Tokens usually opt for a value of 18, imitating the relationship between 53 | * Ether and Wei. 54 | * 55 | * NOTE: This information is only used for _display_ purposes: it in 56 | * no way affects any of the arithmetic of the contract, including 57 | * {IERC20-balanceOf} and {IERC20-transfer}. 58 | */ 59 | function decimals() public view returns (uint8) { 60 | return _decimals; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/contracts/GLDToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 4 | import "@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol"; 5 | 6 | contract GLDToken is ERC20, ERC20Detailed { 7 | constructor(uint256 initialSupply) public ERC20Detailed("Gold", "GLD", 18) { 8 | _mint(msg.sender, initialSupply); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/contracts/GLDTokenUpgradable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./@openzeppelin/contracts/token/ERC20/ERC20Upgradable.sol"; 4 | import "./@openzeppelin/contracts/token/ERC20/ERC20DetailedUpgradable.sol"; 5 | import "@openzeppelin/upgrades/contracts/Initializable.sol"; 6 | 7 | contract GLDTokenUpgradable is Initializable, ERC20Upgradable, ERC20DetailedUpgradable { 8 | function initialize(uint256 initialSupply) external initializer { 9 | __init(true, initialSupply); 10 | } 11 | 12 | function __init(bool callChain, uint256 initialSupply) internal { 13 | if(callChain) { 14 | ContextUpgradable.__init(false); 15 | ERC20DetailedUpgradable.__init(false, "Gold", "GLD", 18); 16 | } 17 | 18 | 19 | _mint(msg.sender, initialSupply); 20 | 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/contracts/Simple.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | contract NoInheritance { 4 | 5 | } 6 | 7 | contract Simple { 8 | uint256 public count; 9 | uint256 private local = 564; 10 | string private hello = "hello"; 11 | bool public test = true; 12 | uint256 constant const = 32**22 + 8; 13 | 14 | constructor(uint256 num) public { 15 | count = num; 16 | } 17 | 18 | function getHello() public view returns (string memory) { 19 | return hello; 20 | } 21 | } 22 | 23 | contract SimpleInheritanceA { 24 | uint256 private foo = 42; 25 | constructor() public {} 26 | } 27 | 28 | contract SimpleInheritanceB is SimpleInheritanceA { 29 | bool private bar = false; 30 | constructor() public {} 31 | } 32 | 33 | contract SimpleInheritanceC is SimpleInheritanceB { 34 | bool private foo; 35 | 36 | } 37 | 38 | contract DiamondA { 39 | uint256 private foo = 42; 40 | bool public bar; 41 | constructor(bool _bar) public { 42 | bar = _bar; 43 | } 44 | } 45 | 46 | contract DiamondB1 is DiamondA {} 47 | 48 | contract DiamondB2 is DiamondA { 49 | string public foo; 50 | constructor(string memory _foo) public { 51 | foo = _foo; 52 | } 53 | 54 | } 55 | 56 | contract DiamondC is DiamondB1, DiamondB2 { 57 | constructor() DiamondA(true) DiamondB2("hello") DiamondB1() public { 58 | 59 | } 60 | } 61 | 62 | contract InheritanceWithParamsParent { 63 | constructor(bool foo, uint256 bar) public {} 64 | } 65 | 66 | contract InheritanceWithParamsConstructorChild is InheritanceWithParamsParent { 67 | constructor() public InheritanceWithParamsParent(true, 564) {} 68 | } 69 | 70 | contract InheritanceWithParamsClassChild is 71 | InheritanceWithParamsParent(false, 87) 72 | { 73 | constructor() public {} 74 | } 75 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/networks.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | development: { 4 | protocol: 'http', 5 | host: 'localhost', 6 | port: 8545, 7 | gas: 5000000, 8 | gasPrice: 5e9, 9 | networkId: '*', 10 | }, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contracts-upgradeability-transpiler", 3 | "version": "0.0.1", 4 | "description": "Contracts upgradeability using transpiler", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "compile": "oz compile" 9 | }, 10 | "author": "Igor Yalovoy ", 11 | "license": "MIT", 12 | "dependencies": { 13 | "@openzeppelin/contracts": "^2.4.0", 14 | "eslint-config-prettier": "^6.8.0", 15 | "eslint-plugin-prettier": "^3.1.2", 16 | "fs-extra": "^8.1.0", 17 | "lodash.find": "^4.6.0" 18 | }, 19 | "devDependencies": { 20 | "@openzeppelin/cli": "^2.6.0", 21 | "prettier": "^1.19.1", 22 | "prettier-plugin-solidity": "^1.0.0-alpha.35" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/get-inheritance-chain.js: -------------------------------------------------------------------------------- 1 | const { getContract, getContractById, getConstructor } = require("./ast-utils"); 2 | 3 | function getInheritanceChain(contract, contractsToArtifactsMap) { 4 | const art = contractsToArtifactsMap[contract]; 5 | const contractNode = getContract(art.ast, contract); 6 | 7 | return contractNode.linearizedBaseContracts.map(base => { 8 | return contractsToArtifactsMap[base].contractName; 9 | }); 10 | } 11 | 12 | module.exports = { 13 | getInheritanceChain 14 | }; 15 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/schemas/ast-node.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Schema of an Abstract Syntax Tree Node. 3 | * This schema only describes the properies that are mandatory across all nodes. 4 | * Additional attrs can exist depending on the entity the Node represents. 5 | */ 6 | 7 | "use strict"; 8 | 9 | // A fully qualified, minimal object for this Schema is: 10 | /* 11 | { 12 | "nodeType": "Literal", 13 | } 14 | */ 15 | 16 | let array = { type: "array" }, 17 | number = { type: "number" }, 18 | bool = { type: "boolean" }, 19 | string = { type: "string" }, 20 | object = { type: "object" }, 21 | attrNull = { type: "null" }; 22 | 23 | let Schema = { 24 | type: "object", 25 | 26 | properties: { 27 | nodeType: { type: "string", minLength: 1 } 28 | }, 29 | 30 | patternProperties: { 31 | "^.+$": { 32 | oneOf: [array, string, object, number, attrNull, bool] 33 | } 34 | }, 35 | 36 | required: ["nodeType"], 37 | additionalProperties: false 38 | }; 39 | 40 | module.exports = Schema; 41 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transformations/append-directive.js: -------------------------------------------------------------------------------- 1 | const { 2 | getImportDirectives, 3 | getPragmaDirectives, 4 | getSourceIndices 5 | } = require("../ast-utils"); 6 | 7 | function appendDirective(fileNode, directive) { 8 | const retVal = { 9 | start: 0, 10 | end: 0, 11 | text: directive 12 | }; 13 | const importsAndPragmas = [ 14 | ...getPragmaDirectives(fileNode), 15 | ...getImportDirectives(fileNode) 16 | ]; 17 | if (importsAndPragmas.length) { 18 | const last = importsAndPragmas.slice(-1)[0]; 19 | const [start, len] = getSourceIndices(last); 20 | retVal.start = start + len; 21 | retVal.end = start + len; 22 | } 23 | 24 | return retVal; 25 | } 26 | 27 | module.exports = { 28 | appendDirective 29 | }; 30 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transformations/fix-import-directives.js: -------------------------------------------------------------------------------- 1 | const { getImportDirectives, getSourceIndices } = require("../ast-utils"); 2 | 3 | function fixImportDirectives(artifact, artifacts, contracts) { 4 | const imports = getImportDirectives(artifact.ast); 5 | return imports.map(imp => { 6 | const [start, len] = getSourceIndices(imp); 7 | const isTranspiled = artifacts.some( 8 | art => 9 | art.ast.id === imp.sourceUnit && 10 | contracts.some(contract => contract === art.contractName) 11 | ); 12 | const prefix = !imp.file.startsWith(".") ? "./" : ""; 13 | let fixedPath = `import "${prefix}${imp.file.replace( 14 | ".sol", 15 | "Upgradable.sol" 16 | )}";`; 17 | return { 18 | start, 19 | end: start + len, 20 | text: !isTranspiled ? `import "${imp.absolutePath}";` : fixedPath 21 | }; 22 | }); 23 | } 24 | 25 | module.exports = { fixImportDirectives }; 26 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transformations/index.js: -------------------------------------------------------------------------------- 1 | const { appendDirective } = require("./append-directive"); 2 | const { prependBaseClass } = require("./prepend-base-class"); 3 | const { transformParents } = require("./transform-parents"); 4 | const { transformContractName } = require("./transform-contract-name"); 5 | const { purgeVarInits } = require("./purge-var-inits"); 6 | const { transformConstructor } = require("./transform-constructor"); 7 | const { purgeContracts } = require("./purge-contracts"); 8 | const { fixImportDirectives } = require("./fix-import-directives"); 9 | 10 | module.exports = { 11 | appendDirective, 12 | prependBaseClass, 13 | transformConstructor, 14 | transformContractName, 15 | purgeContracts, 16 | transformParents, 17 | fixImportDirectives, 18 | purgeVarInits 19 | }; 20 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transformations/prepend-base-class.js: -------------------------------------------------------------------------------- 1 | const { getNodeSources } = require("../ast-utils"); 2 | 3 | function prependBaseClass(contractNode, source, cls) { 4 | const hasInheritance = contractNode.baseContracts.length; 5 | 6 | const [start, len, nodeSource] = getNodeSources(contractNode, source); 7 | 8 | const regExp = RegExp(`\\bcontract\\s+${contractNode.name}(\\s+is)?`); 9 | 10 | const match = regExp.exec(nodeSource); 11 | if (!match) 12 | throw new Error(`Can't find ${contractNode.name} in ${nodeSource}`); 13 | 14 | return { 15 | start: start + match.index + match[0].length, 16 | end: start + match.index + match[0].length, 17 | text: hasInheritance ? ` ${cls},` : ` is ${cls}` 18 | }; 19 | } 20 | 21 | module.exports = { 22 | prependBaseClass 23 | }; 24 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transformations/purge-contracts.js: -------------------------------------------------------------------------------- 1 | const { getSourceIndices, getContracts } = require("../ast-utils"); 2 | 3 | function purgeContracts(astNode, contracts) { 4 | const toPurge = getContracts(astNode).filter(node => 5 | contracts.every(c => node.name !== c) 6 | ); 7 | return toPurge.map(contractNode => { 8 | const [start, len] = getSourceIndices(contractNode); 9 | 10 | return { 11 | start, 12 | end: start + len, 13 | text: "" 14 | }; 15 | }); 16 | } 17 | 18 | module.exports = { purgeContracts }; 19 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transformations/purge-var-inits.js: -------------------------------------------------------------------------------- 1 | const { getVarDeclarations, getNodeSources } = require("../ast-utils"); 2 | 3 | function purgeVarInits(contractNode, source) { 4 | const varDeclarations = getVarDeclarations(contractNode); 5 | return varDeclarations 6 | .filter(vr => vr.value && !vr.constant) 7 | .map(vr => { 8 | const [start, len, varSource] = getNodeSources(vr, source); 9 | const match = /(.*)(=.*)/.exec(varSource); 10 | if (!match) throw new Error(`Can't find = in ${varSource}`); 11 | return { 12 | start: start + match[1].length, 13 | end: start + match[1].length + match[2].length, 14 | text: "" 15 | }; 16 | }); 17 | } 18 | 19 | module.exports = { purgeVarInits }; 20 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transformations/transform-contract-name.js: -------------------------------------------------------------------------------- 1 | const { getNodeSources } = require("../ast-utils"); 2 | 3 | function transformContractName(contractNode, source, newName) { 4 | const [start, len, nodeSource] = getNodeSources(contractNode, source); 5 | 6 | const subStart = nodeSource.indexOf(contractNode.name); 7 | if (subStart === -1) 8 | throw new Error(`Can't find ${contractNode.name} in ${nodeSource}`); 9 | 10 | return { 11 | start: start + subStart, 12 | end: start + subStart + contractNode.name.length, 13 | text: newName 14 | }; 15 | } 16 | 17 | module.exports = { 18 | transformContractName 19 | }; 20 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transformations/transform-parents.js: -------------------------------------------------------------------------------- 1 | const { getNodeSources } = require("../ast-utils"); 2 | 3 | function transformParents(contractNode, source, contracts) { 4 | const hasInheritance = contractNode.baseContracts.length; 5 | 6 | if (hasInheritance) { 7 | return contractNode.baseContracts 8 | .filter(base => 9 | contracts.some(contract => base.baseName.name === contract) 10 | ) 11 | .map(base => { 12 | const [start, , baseSource] = getNodeSources(base.baseName, source); 13 | const [, len] = getNodeSources(base, source); 14 | 15 | return { 16 | start: start, 17 | end: start + len, 18 | text: `${baseSource}Upgradable` 19 | }; 20 | }); 21 | } else return []; 22 | } 23 | 24 | module.exports = { transformParents }; 25 | -------------------------------------------------------------------------------- /contracts-upgradeability-transpiler/src/transpiler.js: -------------------------------------------------------------------------------- 1 | function transpile(source, transformations) { 2 | let cursor = 0; 3 | 4 | const sorted = transformations.sort((a, b) => { 5 | return a.start - b.start; 6 | }); 7 | 8 | for (let i = 0; i < sorted.length - 1; i++) { 9 | if (sorted[i].end > sorted[i + 1].start) 10 | throw new Error( 11 | `Transformations ${sorted[i].start}:${sorted[i].end}:${ 12 | sorted[i].text 13 | } and ${sorted[i + 1].start}:${sorted[i + 1].end}:${ 14 | sorted[i + 1].text 15 | } overlap over the source file` 16 | ); 17 | } 18 | 19 | let transpiledCode = sorted.reduce((output, trans) => { 20 | const { start, end, text } = trans; 21 | output += source.slice(cursor, start); 22 | output += text; 23 | cursor = end; 24 | return output; 25 | }, ""); 26 | 27 | transpiledCode += source.slice(cursor); 28 | return transpiledCode; 29 | } 30 | 31 | module.exports = { transpile }; 32 | -------------------------------------------------------------------------------- /crosschain-env/optimism/envs/batches.env: -------------------------------------------------------------------------------- 1 | ADDRESS_MANAGER_ADDRESS= 2 | 3 | DEBUG=info*,error*,warn*,debug* 4 | 5 | MAX_L1_TX_SIZE=90000 6 | MIN_L1_TX_SIZE=32 7 | MAX_TX_BATCH_COUNT=50 8 | MAX_STATE_BATCH_COUNT=50 9 | 10 | POLL_INTERVAL=500 11 | NUM_CONFIRMATIONS=0 12 | RESUBMISSION_TIMEOUT=1 13 | FINALITY_CONFIRMATIONS=0 14 | RUN_TX_BATCH_SUBMITTER=true 15 | RUN_STATE_BATCH_SUBMITTER=true 16 | MAX_BATCH_SUBMISSION_TIME=0 17 | SAFE_MINIMUM_ETHER_BALANCE=0 18 | CLEAR_PENDING_TXS=false 19 | RETRIES=80 20 | -------------------------------------------------------------------------------- /crosschain-env/optimism/envs/dtl.env: -------------------------------------------------------------------------------- 1 | DATA_TRANSPORT_LAYER__SYNC_FROM_L1=true 2 | DATA_TRANSPORT_LAYER__SYNC_FROM_L2=false 3 | DATA_TRANSPORT_LAYER__DB_PATH=/db 4 | DATA_TRANSPORT_LAYER__SERVER_PORT=7878 5 | DATA_TRANSPORT_LAYER__TRANSACTIONS_PER_POLLING_INTERVAL=1000 6 | DATA_TRANSPORT_LAYER__CONFIRMATIONS=0 7 | DATA_TRANSPORT_LAYER__POLLING_INTERVAL=100 8 | DATA_TRANSPORT_LAYER__LOGS_PER_POLLING_INTERVAL=2000 9 | DATA_TRANSPORT_LAYER__DANGEROUSLY_CATCH_ALL_ERRORS=true 10 | DATA_TRANSPORT_LAYER__SERVER_HOSTNAME=0.0.0.0 11 | DATA_TRANSPORT_LAYER__L1_START_HEIGHT=1 12 | 13 | DATA_TRANSPORT_LAYER__ADDRESS_MANAGER= 14 | DATA_TRANSPORT_LAYER__L1_RPC_ENDPOINT= 15 | DATA_TRANSPORT_LAYER__L2_RPC_ENDPOINT= 16 | DATA_TRANSPORT_LAYER__L2_CHAIN_ID= 17 | -------------------------------------------------------------------------------- /crosschain-env/optimism/envs/geth.env: -------------------------------------------------------------------------------- 1 | ETH1_HTTP= 2 | ETH1_CTC_DEPLOYMENT_HEIGHT= 3 | ETH1_SYNC_SERVICE_ENABLE=true 4 | ETH1_CONFIRMATION_DEPTH=0 5 | 6 | ROLLUP_CLIENT_HTTP= 7 | ROLLUP_POLL_INTERVAL_FLAG=500ms 8 | ROLLUP_ENABLE_L2_GAS_POLLING=true 9 | ROLLUP_ENFORCE_FEES=true 10 | 11 | RPC_ENABLE=true 12 | RPC_ADDR=0.0.0.0 13 | RPC_PORT=8545 14 | RPC_API=eth,net,rollup,web3,debug 15 | RPC_CORS_DOMAIN=* 16 | RPC_VHOSTS=* 17 | 18 | WS=true 19 | WS_ADDR=0.0.0.0 20 | WS_PORT=8546 21 | WS_API=eth,net,rollup,web3 22 | WS_ORIGINS=* 23 | 24 | CHAIN_ID=420 25 | DATADIR=/root/.ethereum 26 | GASPRICE=0 27 | GCMODE=archive 28 | IPC_DISABLE=true 29 | NETWORK_ID=420 30 | NO_USB=true 31 | NO_DISCOVER=true 32 | TARGET_GAS_LIMIT=15000000 33 | USING_OVM=true 34 | 35 | BLOCK_SIGNER_KEY=6587ae678cf4fc9a33000cdbf9f35226b71dcc6a4684a31203241f9bcfd55d27 36 | BLOCK_SIGNER_ADDRESS=0x00000398232E2064F896018496b4b44b3D62751F 37 | 38 | L2_BLOCK_GAS_LIMIT=15000000 39 | 40 | ROLLUP_FEE_THRESHOLD_DOWN=0.9 41 | ROLLUP_FEE_THRESHOLD_UP=1.1 42 | -------------------------------------------------------------------------------- /crosschain-env/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@openzeppelin/crosschain-env", 3 | "description": "Test environment for crosschain operations", 4 | "version": "0.0.1", 5 | "author": "OpenZeppelin Community ", 6 | "license": "MIT", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/OpenZeppelin/openzeppelin-labs.git" 10 | }, 11 | "scripts": { 12 | "optimism:start": "docker-compose -f optimism/docker-compose.yml up -d ", 13 | "optimism:stop": "docker-compose -f optimism/docker-compose.yml down" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/A.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import "./Initializable.sol"; 4 | 5 | contract A is Initializable { 6 | uint256 public x; 7 | 8 | function initialize(uint256 value) public payable isInitializer { 9 | x = value; 10 | } 11 | 12 | function setx(uint256 _x) public { 13 | x = _x; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/AStor.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import "./Initializable.sol"; 4 | 5 | contract AStor is Initializable { 6 | uint256 public x; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/A_v2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import "./Initializable.sol"; 4 | 5 | contract A_v2 is Initializable { 6 | uint256 public x; 7 | 8 | function initialize(uint256 value) public payable isInitializer { 9 | x = value; 10 | } 11 | 12 | function setx(uint256 _x) public { 13 | x = 100*_x; 14 | } 15 | 16 | function getDoublex() public view returns(uint256) { 17 | return 2*x; 18 | } 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/B.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./A.sol"; 3 | 4 | contract B is A { 5 | uint256 public y; 6 | 7 | function sety(uint256 _y) public { 8 | y = _y; 9 | } 10 | 11 | function sum() public view returns(uint256) { 12 | return x+y; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/BFacade.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./AStor.sol"; 3 | import "./upgradeability/OwnedUpgradeabilityProxy.sol"; 4 | 5 | contract BFacade is AStor, OwnedUpgradeabilityProxy { 6 | uint256 public y; 7 | 8 | function sety(uint256 _y) public { 9 | y = _y; 10 | } 11 | 12 | function sum() public view returns(uint256) { 13 | return x+y; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/B_Av2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./A_v2.sol"; 3 | 4 | contract B_Av2 is A_v2 { 5 | uint256 public y; 6 | 7 | function sety(uint256 _y) public { 8 | y = _y; 9 | } 10 | 11 | function sum() public view returns(uint256) { 12 | return x+y; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/Initializable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | contract Initializable { 4 | bool private _initialized; 5 | 6 | modifier isInitializer() { 7 | require(!_initialized); 8 | _; 9 | _initialized = true; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/upgradeability/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Gives the possibility to delegate any call to a foreign implementation. 6 | */ 7 | contract Proxy { 8 | /** 9 | * @dev Tells the address of the implementation where every call will be delegated. 10 | * @return address of the implementation to which it will be delegated 11 | */ 12 | function implementation() public view returns (address); 13 | 14 | /** 15 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 16 | * This function will return whatever the implementation call returns 17 | */ 18 | function () payable public { 19 | address _impl = implementation(); 20 | require(_impl != address(0)); 21 | 22 | assembly { 23 | let ptr := mload(0x40) 24 | calldatacopy(ptr, 0, calldatasize) 25 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 26 | let size := returndatasize 27 | returndatacopy(ptr, 0, size) 28 | 29 | switch result 30 | case 0 { revert(ptr, size) } 31 | default { return(ptr, size) } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/upgradeability/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import './Proxy.sol'; 4 | 5 | /** 6 | * @title UpgradeabilityProxy 7 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 8 | */ 9 | contract UpgradeabilityProxy is Proxy { 10 | /** 11 | * @dev This event will be emitted every time the implementation gets upgraded 12 | * @param implementation representing the address of the upgraded implementation 13 | */ 14 | event Upgraded(address indexed implementation); 15 | 16 | // Storage position of the address of the current implementation 17 | bytes32 private constant implementationPosition = keccak256("org.zeppelinos.proxy.implementation"); 18 | 19 | /** 20 | * @dev Constructor function 21 | */ 22 | function UpgradeabilityProxy() public {} 23 | 24 | /** 25 | * @dev Tells the address of the current implementation 26 | * @return address of the current implementation 27 | */ 28 | function implementation() public view returns (address impl) { 29 | bytes32 position = implementationPosition; 30 | assembly { 31 | impl := sload(position) 32 | } 33 | } 34 | 35 | /** 36 | * @dev Sets the address of the current implementation 37 | * @param newImplementation address representing the new implementation to be set 38 | */ 39 | function setImplementation(address newImplementation) internal { 40 | bytes32 position = implementationPosition; 41 | assembly { 42 | sstore(position, newImplementation) 43 | } 44 | } 45 | 46 | /** 47 | * @dev Upgrades the implementation address 48 | * @param newImplementation representing the address of the new implementation to be set 49 | */ 50 | function _upgradeTo(address newImplementation) internal { 51 | require(newImplementation != address(0)); 52 | address currentImplementation = implementation(); 53 | require(currentImplementation != newImplementation); 54 | setImplementation(newImplementation); 55 | emit Upgraded(newImplementation); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/contracts/upgradeability/UpgradeabilityProxyFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import './OwnedUpgradeabilityProxy.sol'; 4 | 5 | /** 6 | * @title UpgradeabilityProxyFactory 7 | * @dev This contracts provides required functionality to create upgradeability proxies 8 | */ 9 | contract UpgradeabilityProxyFactory { 10 | /** 11 | * @dev Constructor function 12 | */ 13 | function UpgradeabilityProxyFactory() public {} 14 | 15 | /** 16 | * @dev This event will be emitted every time a new proxy is created 17 | * @param proxy representing the address of the proxy created 18 | */ 19 | event ProxyCreated(address proxy); 20 | 21 | /** 22 | * @dev Creates an upgradeability proxy upgraded to an initial version 23 | * @param owner representing the owner of the proxy to be set 24 | * @param implementation representing the address of the initial implementation to be set 25 | * @return address of the new proxy created 26 | */ 27 | function createProxy(address owner, address implementation) public returns (OwnedUpgradeabilityProxy) { 28 | OwnedUpgradeabilityProxy proxy = _createProxy(); 29 | proxy.upgradeTo(implementation); 30 | proxy.transferProxyOwnership(owner); 31 | return proxy; 32 | } 33 | 34 | /** 35 | * @dev Creates an upgradeability proxy upgraded to an initial version and call the new implementation 36 | * @param owner representing the owner of the proxy to be set 37 | * @param implementation representing the address of the initial implementation to be set 38 | * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function 39 | * signature of the implementation to be called with the needed payload 40 | * @return address of the new proxy created 41 | */ 42 | function createProxyAndCall(address owner, address implementation, bytes data) public payable returns (OwnedUpgradeabilityProxy) { 43 | OwnedUpgradeabilityProxy proxy = _createProxy(); 44 | proxy.upgradeToAndCall.value(msg.value)(implementation, data); 45 | proxy.transferProxyOwnership(owner); 46 | return proxy; 47 | } 48 | 49 | /** 50 | * @dev Internal function to create an upgradeable proxy 51 | * @return address of the new proxy created 52 | */ 53 | function _createProxy() internal returns (OwnedUpgradeabilityProxy) { 54 | OwnedUpgradeabilityProxy proxy = new OwnedUpgradeabilityProxy(); 55 | emit ProxyCreated(proxy); 56 | return proxy; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extensibility-study", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "truffle-config.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "truffle test" 11 | }, 12 | "author": "Alejo Salles", 13 | "license": "MIT", 14 | "dependencies": { 15 | "ethereumjs-abi": "^0.6.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/test/helpers/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise, invariants = () => {}) { 2 | try { 3 | await promise; 4 | assert.fail('Expected revert not received'); 5 | } catch (error) { 6 | const revertFound = error.message.search('revert') >= 0; 7 | assert(revertFound, `Expected "revert", got ${error} instead`); 8 | invariants.call() 9 | } 10 | } 11 | 12 | module.exports = assertRevert; 13 | -------------------------------------------------------------------------------- /extensibility-study/base-scenario/test/helpers/encodeCall.js: -------------------------------------------------------------------------------- 1 | const abi = require('ethereumjs-abi') 2 | 3 | function encodeCall(name, arguments = [], values = []) { 4 | const methodId = abi.methodID(name, arguments).toString('hex'); 5 | const params = abi.rawEncode(arguments, values).toString('hex'); 6 | return '0x' + methodId + params; 7 | } 8 | 9 | module.exports = encodeCall; -------------------------------------------------------------------------------- /extensibility-study/base-scenario/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | }; 5 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/A.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import "./Initializable.sol"; 4 | 5 | contract A is Initializable { 6 | uint256 public x; 7 | 8 | function initialize(uint256 value) public payable isInitializer { 9 | x = value; 10 | } 11 | 12 | function setx(uint256 _x) public { 13 | x = _x; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/AStor.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import "./Initializable.sol"; 4 | 5 | contract AStor is Initializable { 6 | uint256 public x; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/A_v2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import "./Initializable.sol"; 4 | 5 | contract A_v2 is Initializable { 6 | uint256 public x; 7 | 8 | function initialize(uint256 value) public payable isInitializer { 9 | x = value; 10 | } 11 | 12 | function setx(uint256 _x) public { 13 | x = 100*_x; 14 | } 15 | 16 | function getDoublex() public view returns(uint256) { 17 | return 2*x; 18 | } 19 | 20 | } 21 | 22 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/B.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./A.sol"; 3 | 4 | contract B is A { 5 | uint256 public y; 6 | 7 | function sety(uint256 _y) public { 8 | y = _y; 9 | } 10 | 11 | function sum() public view returns(uint256) { 12 | return x+y; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/BFacade.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./AStor.sol"; 3 | import "./upgradeability/OwnedUpgradeabilityProxy.sol"; 4 | 5 | contract BFacade is AStor, OwnedUpgradeabilityProxy { 6 | uint256 public y; 7 | 8 | function sety(uint256 _y) public { 9 | y = _y; 10 | } 11 | 12 | function sum() public view returns(uint256) { 13 | return x+y; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/B_Av2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./A_v2.sol"; 3 | 4 | contract B_Av2 is A_v2 { 5 | uint256 public y; 6 | 7 | function sety(uint256 _y) public { 8 | y = _y; 9 | } 10 | 11 | function sum() public view returns(uint256) { 12 | return x+y; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/B_v2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./A.sol"; 3 | 4 | contract B_v2 is A { 5 | uint256 public y; 6 | 7 | function sety(uint256 _y) public { 8 | y = _y; 9 | } 10 | 11 | function sum() public view returns(uint256) { 12 | return x+y; 13 | } 14 | 15 | function mult() public view returns(uint256) { 16 | return x*y; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/B_v2Facade.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./AStor.sol"; 3 | import "./upgradeability/OwnedUpgradeabilityProxy.sol"; 4 | 5 | contract B_v2Facade is AStor, OwnedUpgradeabilityProxy { 6 | uint256 public y; 7 | 8 | function sety(uint256 _y) public { 9 | y = _y; 10 | } 11 | 12 | function sum() public view returns(uint256) { 13 | return x+y; 14 | } 15 | 16 | function mult() public view returns(uint256) { 17 | return x*y; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/Initializable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | contract Initializable { 4 | bool private _initialized; 5 | 6 | modifier isInitializer() { 7 | require(!_initialized); 8 | _; 9 | _initialized = true; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/upgradeability/ExtensibilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | import "./OwnedUpgradeabilityProxy.sol"; 3 | 4 | contract ExtensibilityProxy is OwnedUpgradeabilityProxy { 5 | bytes32 private constant facadeImplementationPosition = keccak256("org.zeppelinos.proxy.facadeImplementation"); 6 | 7 | 8 | function facadeImplementation() public view returns (address impl) { 9 | bytes32 position = facadeImplementationPosition; 10 | assembly { 11 | impl := sload(position) 12 | } 13 | } 14 | 15 | function setFacadeImplementation(address newImplementation) internal { 16 | bytes32 position = facadeImplementationPosition; 17 | assembly { 18 | sstore(position, newImplementation) 19 | } 20 | } 21 | 22 | function upgradeFacadeTo(address implementation) public onlyProxyOwner { 23 | _upgradeFacadeTo(implementation); 24 | } 25 | 26 | function upgradeFacadeToAndCall(address implementation, bytes data) payable public onlyProxyOwner { 27 | upgradeFacadeTo(implementation); 28 | require(this.call.value(msg.value)(data)); 29 | } 30 | 31 | function _upgradeFacadeTo(address newImplementation) internal { 32 | require(newImplementation != address(0)); 33 | address currentImplementation = facadeImplementation(); 34 | require(currentImplementation != newImplementation); 35 | setFacadeImplementation(newImplementation); 36 | emit Upgraded(newImplementation); 37 | } 38 | 39 | function () payable public { 40 | address _impl = facadeImplementation(); 41 | require(_impl != address(0)); 42 | 43 | assembly { 44 | let ptr := mload(0x40) 45 | calldatacopy(ptr, 0, calldatasize) 46 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 47 | let size := returndatasize 48 | returndatacopy(ptr, 0, size) 49 | 50 | switch result 51 | case 0 { revert(ptr, size) } 52 | default { return(ptr, size) } 53 | } 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/upgradeability/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Gives the possibility to delegate any call to a foreign implementation. 6 | */ 7 | contract Proxy { 8 | /** 9 | * @dev Tells the address of the implementation where every call will be delegated. 10 | * @return address of the implementation to which it will be delegated 11 | */ 12 | function implementation() public view returns (address); 13 | 14 | /** 15 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 16 | * This function will return whatever the implementation call returns 17 | */ 18 | function () payable public { 19 | address _impl = implementation(); 20 | require(_impl != address(0)); 21 | 22 | assembly { 23 | let ptr := mload(0x40) 24 | calldatacopy(ptr, 0, calldatasize) 25 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 26 | let size := returndatasize 27 | returndatacopy(ptr, 0, size) 28 | 29 | switch result 30 | case 0 { revert(ptr, size) } 31 | default { return(ptr, size) } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/upgradeability/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import './Proxy.sol'; 4 | 5 | /** 6 | * @title UpgradeabilityProxy 7 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 8 | */ 9 | contract UpgradeabilityProxy is Proxy { 10 | /** 11 | * @dev This event will be emitted every time the implementation gets upgraded 12 | * @param implementation representing the address of the upgraded implementation 13 | */ 14 | event Upgraded(address indexed implementation); 15 | 16 | // Storage position of the address of the current implementation 17 | bytes32 private constant implementationPosition = keccak256("org.zeppelinos.proxy.implementation"); 18 | 19 | /** 20 | * @dev Constructor function 21 | */ 22 | function UpgradeabilityProxy() public {} 23 | 24 | /** 25 | * @dev Tells the address of the current implementation 26 | * @return address of the current implementation 27 | */ 28 | function implementation() public view returns (address impl) { 29 | bytes32 position = implementationPosition; 30 | assembly { 31 | impl := sload(position) 32 | } 33 | } 34 | 35 | /** 36 | * @dev Sets the address of the current implementation 37 | * @param newImplementation address representing the new implementation to be set 38 | */ 39 | function setImplementation(address newImplementation) internal { 40 | bytes32 position = implementationPosition; 41 | assembly { 42 | sstore(position, newImplementation) 43 | } 44 | } 45 | 46 | /** 47 | * @dev Upgrades the implementation address 48 | * @param newImplementation representing the address of the new implementation to be set 49 | */ 50 | function _upgradeTo(address newImplementation) internal { 51 | require(newImplementation != address(0)); 52 | address currentImplementation = implementation(); 53 | require(currentImplementation != newImplementation); 54 | setImplementation(newImplementation); 55 | emit Upgraded(newImplementation); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/contracts/upgradeability/UpgradeabilityProxyFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import './OwnedUpgradeabilityProxy.sol'; 4 | 5 | /** 6 | * @title UpgradeabilityProxyFactory 7 | * @dev This contracts provides required functionality to create upgradeability proxies 8 | */ 9 | contract UpgradeabilityProxyFactory { 10 | /** 11 | * @dev Constructor function 12 | */ 13 | function UpgradeabilityProxyFactory() public {} 14 | 15 | /** 16 | * @dev This event will be emitted every time a new proxy is created 17 | * @param proxy representing the address of the proxy created 18 | */ 19 | event ProxyCreated(address proxy); 20 | 21 | /** 22 | * @dev Creates an upgradeability proxy upgraded to an initial version 23 | * @param owner representing the owner of the proxy to be set 24 | * @param implementation representing the address of the initial implementation to be set 25 | * @return address of the new proxy created 26 | */ 27 | function createProxy(address owner, address implementation) public returns (OwnedUpgradeabilityProxy) { 28 | OwnedUpgradeabilityProxy proxy = _createProxy(); 29 | proxy.upgradeTo(implementation); 30 | proxy.transferProxyOwnership(owner); 31 | return proxy; 32 | } 33 | 34 | /** 35 | * @dev Creates an upgradeability proxy upgraded to an initial version and call the new implementation 36 | * @param owner representing the owner of the proxy to be set 37 | * @param implementation representing the address of the initial implementation to be set 38 | * @param data represents the msg.data to bet sent in the low level call. This parameter may include the function 39 | * signature of the implementation to be called with the needed payload 40 | * @return address of the new proxy created 41 | */ 42 | function createProxyAndCall(address owner, address implementation, bytes data) public payable returns (OwnedUpgradeabilityProxy) { 43 | OwnedUpgradeabilityProxy proxy = _createProxy(); 44 | proxy.upgradeToAndCall.value(msg.value)(implementation, data); 45 | proxy.transferProxyOwnership(owner); 46 | return proxy; 47 | } 48 | 49 | /** 50 | * @dev Internal function to create an upgradeable proxy 51 | * @return address of the new proxy created 52 | */ 53 | function _createProxy() internal returns (OwnedUpgradeabilityProxy) { 54 | OwnedUpgradeabilityProxy proxy = new OwnedUpgradeabilityProxy(); 55 | emit ProxyCreated(proxy); 56 | return proxy; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "extensibility-study", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "truffle-config.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "truffle test" 11 | }, 12 | "author": "Alejo Salles", 13 | "license": "MIT", 14 | "dependencies": { 15 | "ethereumjs-abi": "^0.6.5" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/test/helpers/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise, invariants = () => {}) { 2 | try { 3 | await promise; 4 | assert.fail('Expected revert not received'); 5 | } catch (error) { 6 | const revertFound = error.message.search('revert') >= 0; 7 | assert(revertFound, `Expected "revert", got ${error} instead`); 8 | invariants.call() 9 | } 10 | } 11 | 12 | module.exports = assertRevert; 13 | -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/test/helpers/encodeCall.js: -------------------------------------------------------------------------------- 1 | const abi = require('ethereumjs-abi') 2 | 3 | function encodeCall(name, arguments = [], values = []) { 4 | const methodId = abi.methodID(name, arguments).toString('hex'); 5 | const params = abi.rawEncode(arguments, values).toString('hex'); 6 | return '0x' + methodId + params; 7 | } 8 | 9 | module.exports = encodeCall; -------------------------------------------------------------------------------- /extensibility-study/child-upgradeability/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // See 3 | // to customize your Truffle configuration! 4 | }; 5 | -------------------------------------------------------------------------------- /governance-with-multisig/.gitignore: -------------------------------------------------------------------------------- 1 | ganache/* 2 | node_modules 3 | 4 | .zos.session 5 | -------------------------------------------------------------------------------- /governance-with-multisig/change-admin.js: -------------------------------------------------------------------------------- 1 | const App = require('zos-lib').App; 2 | const Proxy = require('zos-lib').Proxy; 3 | const fs = require('fs'); 4 | const process = require('process'); 5 | 6 | global.artifacts = artifacts; 7 | global.web3 = web3; 8 | 9 | async function changeOwner(networkName, contractName, newAdmin) { 10 | if (!contractName) { 11 | throw Error("Contract name of the proxy to change ownership is required"); 12 | } 13 | 14 | if (!newAdmin) { 15 | throw Error("New admin address is required"); 16 | } 17 | 18 | console.log(`Changing admin of proxy ${contractName} to ${newAdmin}`); 19 | 20 | const networkInfo = JSON.parse(fs.readFileSync(`zos.${networkName}.json`)); 21 | const appAddress = networkInfo.app.address; 22 | const proxiesOfContract = networkInfo.proxies[contractName]; 23 | 24 | if (!proxiesOfContract || proxiesOfContract.length === 0) { 25 | throw Error(`No deployed proxies of contract ${contractName} found`); 26 | } else if (proxiesOfContract.length > 1) { 27 | throw Error(`Multiple proxies of contract ${contractName} found`); 28 | } 29 | 30 | const proxyAddress = proxiesOfContract[0].address; 31 | const proxy = await Proxy.at(proxyAddress); 32 | console.log("Previous admin is", (await proxy.admin())); 33 | 34 | const app = await App.fetch(appAddress); 35 | await app.changeProxyAdmin(proxyAddress, newAdmin); 36 | console.log("Successfully changed admin to", newAdmin); 37 | } 38 | 39 | module.exports = function(cb) { 40 | const scriptIndex = process.argv.indexOf('change-admin.js'); 41 | const networkIndex = process.argv.indexOf('--network'); 42 | changeOwner(process.argv[networkIndex+1], process.argv[scriptIndex+1], process.argv[scriptIndex+2]) 43 | .then(() => cb()) 44 | .catch(err => cb(err)); 45 | } 46 | -------------------------------------------------------------------------------- /governance-with-multisig/contracts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-labs/54ad91472fdd0ac4c34aa97d3a3da45c28245510/governance-with-multisig/contracts/.gitkeep -------------------------------------------------------------------------------- /governance-with-multisig/contracts/EthBox.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract EthBox { 4 | mapping(address => uint256) balances; 5 | 6 | function deposit() public payable { 7 | balances[msg.sender] += msg.value; 8 | } 9 | } 10 | 11 | contract EthBoxV2 is EthBox { 12 | function getBalance(address owner) public view returns (uint256) { 13 | return balances[owner]; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /governance-with-multisig/ganache.sh: -------------------------------------------------------------------------------- 1 | mkdir -p ganache 2 | ganache-cli --gasLimit 6000000 --defaultBalanceEther 1000 --networkId 8888 --db ganache --port 9545 --deterministic 3 | -------------------------------------------------------------------------------- /governance-with-multisig/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-labs/54ad91472fdd0ac4c34aa97d3a3da45c28245510/governance-with-multisig/migrations/.gitkeep -------------------------------------------------------------------------------- /governance-with-multisig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "governance-with-multisig", 3 | "version": "1.0.0", 4 | "description": "Sample zOS-powered project with upgrade privileges managed from a multisig", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Santiago Palladino ", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "zos": "1.1.0" 13 | }, 14 | "dependencies": { 15 | "zos-lib": "1.3.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /governance-with-multisig/query.js: -------------------------------------------------------------------------------- 1 | const App = require('zos-lib').App; 2 | const Proxy = require('zos-lib').Proxy; 3 | const fs = require('fs'); 4 | const process = require('process'); 5 | 6 | global.artifacts = artifacts; 7 | global.web3 = web3; 8 | 9 | async function queryProxy(networkName, contractName) { 10 | if (!contractName) { 11 | throw Error("Contract name of the proxy to change ownership is required"); 12 | } 13 | 14 | const networkInfo = JSON.parse(fs.readFileSync(`zos.${networkName}.json`)); 15 | const proxiesOfContract = networkInfo.proxies[contractName]; 16 | 17 | if (!proxiesOfContract || proxiesOfContract.length === 0) { 18 | throw Error(`No deployed proxies of contract ${contractName} found`); 19 | } else if (proxiesOfContract.length > 1) { 20 | throw Error(`Multiple proxies of contract ${contractName} found`); 21 | } 22 | 23 | const proxyAddress = proxiesOfContract[0].address; 24 | const proxy = await Proxy.at(proxyAddress); 25 | const admin = await proxy.admin(); 26 | const implementation = await proxy.implementation(); 27 | console.log("Admin is", pp(admin)); 28 | console.log("Implementation is", pp(implementation)); 29 | } 30 | 31 | function pp(address) { 32 | return address.replace('0x000000000000000000000000', '0x'); 33 | } 34 | 35 | module.exports = function(cb) { 36 | const scriptIndex = process.argv.indexOf('query.js'); 37 | const networkIndex = process.argv.indexOf('--network'); 38 | queryProxy(process.argv[networkIndex+1], process.argv[scriptIndex+1]) 39 | .then(() => cb()) 40 | .catch(err => cb(err)); 41 | } -------------------------------------------------------------------------------- /governance-with-multisig/submit-upgrade.js: -------------------------------------------------------------------------------- 1 | const App = require('zos-lib').App; 2 | const Proxy = require('zos-lib').Proxy; 3 | const encodeCall = require('zos-lib').encodeCall; 4 | const fs = require('fs'); 5 | const process = require('process'); 6 | const MultiSigWallet = artifacts.require('MultiSigWallet') 7 | 8 | global.artifacts = artifacts; 9 | global.web3 = web3; 10 | 11 | async function submitUpgrade(networkName, contractName, multisigAddress) { 12 | if (!contractName) { 13 | throw Error("Contract name of the proxy to change ownership of is required"); 14 | } 15 | 16 | const networkInfo = JSON.parse(fs.readFileSync(`zos.${networkName}.json`)); 17 | 18 | const proxiesOfContract = networkInfo.proxies[contractName]; 19 | if (!proxiesOfContract || proxiesOfContract.length === 0) { 20 | throw Error(`No deployed proxies of contract ${contractName} found`); 21 | } else if (proxiesOfContract.length > 1) { 22 | throw Error(`Multiple proxies of contract ${contractName} found`); 23 | } 24 | 25 | const implementationOfContract = networkInfo.contracts && networkInfo.contracts[contractName]; 26 | if (!implementationOfContract) { 27 | throw Error(`No deployed logic contract for ${contractName}, make sure to call 'zos push --network ${networkName}'`); 28 | } 29 | 30 | const proxyAddress = proxiesOfContract[0].address; 31 | const implementationAddress = implementationOfContract.address; 32 | console.log(`Requesting instance upgrade of ${proxyAddress} of ${contractName} to ${implementationAddress}`); 33 | 34 | const multisig = MultiSigWallet.at(multisigAddress); 35 | const upgradeCallData = encodeCall('upgradeTo', ['address'], [implementationAddress]); 36 | multisig.submitTransaction(proxyAddress, 0, upgradeCallData); 37 | 38 | console.log("Submitted upgrade transaction to multisig"); 39 | } 40 | 41 | module.exports = function(cb) { 42 | const scriptIndex = process.argv.indexOf('submit-upgrade.js'); 43 | const networkIndex = process.argv.indexOf('--network'); 44 | submitUpgrade(process.argv[networkIndex+1], process.argv[scriptIndex+1], process.argv[scriptIndex+2]) 45 | .then(() => cb()) 46 | .catch(err => cb(err)); 47 | } -------------------------------------------------------------------------------- /governance-with-multisig/truffle-config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const ROPSTEN_HOST = null; 4 | const ROPSTEN_PORT = null; 5 | 6 | module.exports = { 7 | networks: { 8 | local: { 9 | host: 'localhost', 10 | port: 9545, 11 | gas: 5000000, 12 | network_id: '*' 13 | }, 14 | ropsten: { 15 | host: ROPSTEN_HOST, 16 | port: ROPSTEN_PORT, 17 | gas: 4000000, 18 | network_id: 3, 19 | gasPrice: 10e9 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance/contracts/Initializable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract Initializable { 4 | bool initialized = false; 5 | 6 | modifier initializer() { 7 | require(!initialized); 8 | _; 9 | initialized = true; 10 | } 11 | } -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance/contracts/test/Inheritance.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "../Initializable.sol"; 4 | 5 | contract ParentA is Initializable { 6 | uint256 public a; 7 | 8 | function initialize(uint256 _a) initializer public { 9 | onInitialize(_a); 10 | } 11 | 12 | function onInitialize(uint256 _a) internal { 13 | a = _a; 14 | } 15 | } 16 | 17 | contract ParentB is Initializable { 18 | uint256 public b; 19 | 20 | function initialize(uint _b) initializer public { 21 | onInitialize(_b); 22 | } 23 | 24 | function onInitialize(uint256 _b) internal { 25 | b = _b; 26 | } 27 | } 28 | 29 | contract Child is Initializable, ParentA, ParentB { 30 | uint256 public c; 31 | 32 | function initialize(uint _a, uint _b, uint _c) initializer public { 33 | onInitialize(_a, _b, _c); 34 | } 35 | 36 | function onInitialize(uint _a, uint _b, uint _c) internal { 37 | ParentA.onInitialize(_a); 38 | ParentB.onInitialize(_b); 39 | c = _c; 40 | } 41 | } -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initializable-with-multiple-inheritance", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Santiago Palladino ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "truffle": "^4.1.8" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance/readme.md: -------------------------------------------------------------------------------- 1 | # Initializable with multiple inheritance support 2 | 3 | _Test using Initializable library in multiple inheritance scenarios._ 4 | 5 | The current implementation of initializable fails when used with multiple inheritance, where each parent defines its own initializer, since all parents depend on a single `initialized` flag. Thus, when the first parent is initialized, the second fails to do so. 6 | 7 | A way to work around this is for every class to define separate `initialize` and `onInitialize` methods, where `initialize` is public, checks and sets the initialized flag, but delegates all actual logic to `onInitialize`, which is internal. See `contracts/test/Inheritance.sol` for an example. 8 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance/test/Initializable.js: -------------------------------------------------------------------------------- 1 | const Child = artifacts.require('Child') 2 | 3 | contract('Child', function ([owner]) { 4 | 5 | it('should work', async function () { 6 | const child = await Child.new({ from: owner }); 7 | await child.initialize(10, 20, 30, { from: owner }); 8 | const a = await child.a(); 9 | const b = await child.b(); 10 | const c = await child.c(); 11 | assert(a.eq(10)); 12 | assert(b.eq(20)); 13 | assert(c.eq(30)); 14 | }) 15 | 16 | }) 17 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_alt/contracts/Initializable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract Initializable { 4 | bool initialized = false; 5 | bool initializing = false; 6 | 7 | modifier initializer() { 8 | require(initializing || !initialized); 9 | 10 | bool wasInitializing = initializing; 11 | initializing = true; 12 | 13 | _; 14 | 15 | initialized = true; 16 | initializing = wasInitializing; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_alt/contracts/test/Inheritance.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "../Initializable.sol"; 4 | 5 | contract ParentA is Initializable { 6 | uint256 public a; 7 | 8 | function initialize(uint256 _a) initializer public { 9 | a = _a; 10 | } 11 | } 12 | 13 | contract ParentB is Initializable { 14 | uint256 public b; 15 | 16 | function initialize(uint _b) initializer public { 17 | b = _b; 18 | } 19 | } 20 | 21 | contract Child is Initializable, ParentA, ParentB { 22 | uint256 public c; 23 | 24 | function initialize(uint _a, uint _b, uint _c) initializer public { 25 | ParentA.initialize(_a); 26 | ParentB.initialize(_b); 27 | c = _c; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_alt/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-labs/54ad91472fdd0ac4c34aa97d3a3da45c28245510/initializable_with_multiple_inheritance_alt/migrations/.gitkeep -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_alt/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initializable-with-multiple-inheritance-alt", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Francisco Giordano ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "truffle": "^4.1.8" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_alt/readme.md: -------------------------------------------------------------------------------- 1 | # Initializable with multiple inheritance support 2 | 3 | _Test using Initializable library in multiple inheritance scenarios._ 4 | 5 | The current implementation of initializable fails when used with multiple inheritance, where each parent defines its own initializer, since all parents depend on a single `initialized` flag. Thus, when the first parent is initialized, the second fails to do so. 6 | 7 | A way to work around this is to add an `initializing` flag to `Initializable`, and to ignore `initialized` if `initializing` is set. 8 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_alt/test/Initializable.js: -------------------------------------------------------------------------------- 1 | const Child = artifacts.require('Child') 2 | 3 | contract('Child', function ([owner]) { 4 | 5 | it('should work', async function () { 6 | const child = await Child.new({ from: owner }); 7 | await child.initialize(10, 20, 30, { from: owner }); 8 | const a = await child.a(); 9 | const b = await child.b(); 10 | const c = await child.c(); 11 | assert(a.eq(10)); 12 | assert(b.eq(20)); 13 | assert(c.eq(30)); 14 | }) 15 | 16 | }) 17 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_alt/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_safer/contracts/Initializable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | contract Initializable { 4 | mapping (uint256 => bool) private initialized; 5 | 6 | modifier initializer() { 7 | uint256 _pc; 8 | assembly { _pc := pc } 9 | 10 | require(!initialized[_pc]); 11 | 12 | _; 13 | 14 | initialized[_pc] = true; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_safer/contracts/test/Inheritance.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.23; 2 | 3 | import "../Initializable.sol"; 4 | 5 | contract ParentA is Initializable { 6 | uint256 public a; 7 | 8 | function initialize(uint256 _a) initializer public { 9 | a = _a; 10 | } 11 | } 12 | 13 | contract ParentB is Initializable { 14 | uint256 public b; 15 | 16 | function initialize(uint _b) initializer public { 17 | b = _b; 18 | } 19 | } 20 | 21 | contract Child is Initializable, ParentA, ParentB { 22 | uint256 public c; 23 | 24 | function initialize(uint _a, uint _b, uint _c) initializer public { 25 | ParentA.initialize(_a); 26 | ParentB.initialize(_b); 27 | c = _c; 28 | } 29 | } 30 | 31 | contract BadChild is Initializable, ParentA, ParentB { 32 | function initialize(uint _a, uint _b, uint _c) initializer public { 33 | ParentA.initialize(_a); 34 | ParentB.initialize(_b); 35 | ParentB.initialize(_b); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_safer/migrations/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-labs/54ad91472fdd0ac4c34aa97d3a3da45c28245510/initializable_with_multiple_inheritance_safer/migrations/.gitkeep -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_safer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initializable-with-multiple-inheritance-safer", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Francisco Giordano ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "truffle": "^4.1.8" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_safer/readme.md: -------------------------------------------------------------------------------- 1 | # Initializable with multiple inheritance support 2 | 3 | _Test using Initializable library in multiple inheritance scenarios._ 4 | 5 | The current implementation of initializable fails when used with multiple inheritance, where each parent defines its own initializer, since all parents depend on a single `initialized` flag. Thus, when the first parent is initialized, the second fails to do so. 6 | 7 | Since modifiers are inlined we can use the PC to identify each separate initializer, and thus check that each of them is run exactly once. This solves the problem with multiple inheritance, and solves the problem with accidentally running an initializer twice in the contract hierarchy (although not statically as with normal constructors). 8 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_safer/test/Initializable.js: -------------------------------------------------------------------------------- 1 | const Child = artifacts.require('Child') 2 | const BadChild = artifacts.require('BadChild') 3 | 4 | async function assertRevert(promise) { 5 | try { 6 | await promise; 7 | assert.fail('Expected revert not received'); 8 | } catch (error) { 9 | const revertFound = error.message.search('revert') >= 0; 10 | assert(revertFound, `Expected "revert", got ${error} instead`); 11 | } 12 | } 13 | 14 | contract('Child', function ([owner]) { 15 | 16 | it('should initialize once', async function () { 17 | const child = await Child.new({ from: owner }); 18 | await child.initialize(10, 20, 30, { from: owner }); 19 | const a = await child.a(); 20 | const b = await child.b(); 21 | const c = await child.c(); 22 | assert(a.eq(10)); 23 | assert(b.eq(20)); 24 | assert(c.eq(30)); 25 | }); 26 | 27 | it('should not initialize twice', async function () { 28 | const child = await Child.new({ from: owner }); 29 | await child.initialize(10, 20, 30, { from: owner }); 30 | await assertRevert(child.initialize(10, 20, 30, { from: owner })); 31 | }); 32 | 33 | }); 34 | 35 | contract('BadChild', function ([owner]) { 36 | 37 | it('should not work', async function () { 38 | const child = await BadChild.new({ from: owner }); 39 | await assertRevert(child.initialize(10, 20, 30, { from: owner })); 40 | }); 41 | 42 | }); 43 | -------------------------------------------------------------------------------- /initializable_with_multiple_inheritance_safer/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /initializer_contracts/.gitignore: -------------------------------------------------------------------------------- 1 | #Buidler files 2 | cache/ 3 | artifacts/ 4 | -------------------------------------------------------------------------------- /initializer_contracts/buidler-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | }; 3 | -------------------------------------------------------------------------------- /initializer_contracts/contracts/Greeter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | contract Greeter { 4 | 5 | string public greeting; 6 | uint256 public created; 7 | 8 | constructor() public { 9 | greeting = "Hello world!"; 10 | created = block.number; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /initializer_contracts/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Implements delegation of calls to other contracts, with proper 6 | * forwarding of return values and bubbling of failures. 7 | * It defines a fallback function that delegates all calls to the address 8 | * returned by the abstract _implementation() internal function. 9 | */ 10 | contract Proxy { 11 | /** 12 | * @dev Fallback function. 13 | * Implemented entirely in `_fallback`. 14 | */ 15 | function () payable external { 16 | _fallback(); 17 | } 18 | 19 | /** 20 | * @return The Address of the implementation. 21 | */ 22 | function _implementation() internal view returns (address); 23 | 24 | /** 25 | * @dev Delegates execution to an implementation contract. 26 | * This is a low level function that doesn't return to its internal call site. 27 | * It will return to the external caller whatever the implementation returns. 28 | * @param implementation Address to delegate. 29 | */ 30 | function _delegate(address implementation) internal { 31 | assembly { 32 | // Copy msg.data. We take full control of memory in this inline assembly 33 | // block because it will not return to Solidity code. We overwrite the 34 | // Solidity scratch pad at memory position 0. 35 | calldatacopy(0, 0, calldatasize) 36 | 37 | // Call the implementation. 38 | // out and outsize are 0 because we don't know the size yet. 39 | let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) 40 | 41 | // Copy the returned data. 42 | returndatacopy(0, 0, returndatasize) 43 | 44 | switch result 45 | // delegatecall returns 0 on error. 46 | case 0 { revert(0, returndatasize) } 47 | default { return(0, returndatasize) } 48 | } 49 | } 50 | 51 | /** 52 | * @dev Function that is run as the first thing in the fallback function. 53 | * Can be redefined in derived contracts to add functionality. 54 | * Redefinitions must call super._willFallback(). 55 | */ 56 | function _willFallback() internal { 57 | } 58 | 59 | /** 60 | * @dev fallback implementation. 61 | * Extracted to enable manual triggering. 62 | */ 63 | function _fallback() internal { 64 | _willFallback(); 65 | _delegate(_implementation()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /initializer_contracts/contracts/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import './Proxy.sol'; 4 | import 'openzeppelin-solidity/contracts/AddressUtils.sol'; 5 | 6 | /** 7 | * @title UpgradeabilityProxy 8 | * @dev This contract implements a proxy that allows to change the 9 | * implementation address to which it will delegate. 10 | * Such a change is called an implementation upgrade. 11 | */ 12 | contract UpgradeabilityProxy is Proxy { 13 | /** 14 | * @dev Emitted when the implementation is upgraded. 15 | * @param implementation Address of the new implementation. 16 | */ 17 | event Upgraded(address implementation); 18 | 19 | /** 20 | * @dev Storage slot with the address of the current implementation. 21 | * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is 22 | * validated in the constructor. 23 | */ 24 | bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3; 25 | 26 | /** 27 | * @dev Contract constructor. 28 | * @param _implementation Address of the initial implementation. 29 | */ 30 | constructor(address _constructor, address _implementation) public { 31 | assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation")); 32 | 33 | require(_constructor.delegatecall()); 34 | 35 | _setImplementation(_implementation); 36 | } 37 | 38 | /** 39 | * @dev Returns the current implementation. 40 | * @return Address of the current implementation 41 | */ 42 | function _implementation() internal view returns (address impl) { 43 | bytes32 slot = IMPLEMENTATION_SLOT; 44 | assembly { 45 | impl := sload(slot) 46 | } 47 | } 48 | 49 | /** 50 | * @dev Upgrades the proxy to a new implementation. 51 | * @param newImplementation Address of the new implementation. 52 | */ 53 | function _upgradeTo(address newImplementation) internal { 54 | _setImplementation(newImplementation); 55 | emit Upgraded(newImplementation); 56 | } 57 | 58 | /** 59 | * @dev Sets the implementation address of the proxy. 60 | * @param newImplementation Address of the new implementation. 61 | */ 62 | function _setImplementation(address newImplementation) private { 63 | require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); 64 | 65 | bytes32 slot = IMPLEMENTATION_SLOT; 66 | 67 | assembly { 68 | sstore(slot, newImplementation) 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /initializer_contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initializer_contracts", 3 | "private": true, 4 | "author": "Francisco Giordano ", 5 | "license": "MIT", 6 | "dependencies": { 7 | "buidler": "^0.1.4", 8 | "openzeppelin-solidity": "^1.11.0-rc.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /initializer_contracts/scripts/poc-1.js: -------------------------------------------------------------------------------- 1 | function sleep(ms) { 2 | return new Promise(resolve => setTimeout(resolve, ms)); 3 | } 4 | 5 | async function getTransactionReceipt(txid) { 6 | while (true) { 7 | const receipt = await pweb3.eth.getTransactionReceipt(txid); 8 | 9 | if (receipt) { 10 | return receipt; 11 | } else { 12 | await sleep(100); 13 | } 14 | } 15 | } 16 | 17 | async function deploy(bytecode) { 18 | const tx = await pweb3.eth.sendTransaction({ 19 | from: (await pweb3.eth.getAccounts())[0], 20 | to: null, 21 | value: 0, 22 | data: bytecode, 23 | gas: (await pweb3.eth.getBlock('latest')).gasLimit, 24 | }); 25 | 26 | const receipt = await getTransactionReceipt(tx); 27 | 28 | return receipt.contractAddress; 29 | } 30 | 31 | // This is EVM assembly that, when used at the beginning of a contract, will 32 | // return all of the code that follows it. 33 | // 34 | // operation | bytecode | stack representation 35 | // ================================================= 36 | // push1 0D | 0x60 0x0D | 0x0D 37 | // dup1 | 0x80 | 0x0D 0x0D 38 | // codesize | 0x38 | 0x0D 0x0D 0xCS 39 | // sub | 0x03 | 0x0D 0xIS 40 | // dup1 | 0x80 | 0x0D 0xIS 0xIS 41 | // swap2 | 0x91 | 0xIS 0xIS 0x0D 42 | // push1 00 | 0x60 0x00 | 0xIS 0xIS 0x0D 0x00 43 | // codecopy | 0x39 | 0xIS 44 | // push1 00 | 0x60 0x00 | 0xIS 0x00 45 | // return | 0xf3 46 | const ASM_RETURN_REST = '0x600d80380380916000396000f3'; 47 | 48 | async function main() { 49 | await run('compile'); 50 | 51 | const Greeter = artifacts.require('Greeter'); 52 | const AdminUpgradeabilityProxy = artifacts.require('AdminUpgradeabilityProxy'); 53 | 54 | const implementation = await deploy(Greeter.bytecode); 55 | const constructor = await deploy(Greeter.bytecode.replace('0x', ASM_RETURN_REST)); 56 | 57 | const admin = (await pweb3.eth.getAccounts())[1]; 58 | const proxy = await AdminUpgradeabilityProxy.new(constructor, implementation, { from: admin }); 59 | 60 | const greeter = new Greeter(proxy.address); 61 | 62 | console.log(`Greeting: ${ await greeter.greeting() }`); 63 | console.log(`Block Nr: ${ (await greeter.created()).toString() }`); 64 | } 65 | 66 | main().catch(console.error); 67 | -------------------------------------------------------------------------------- /initializer_contracts_with_args/.gitignore: -------------------------------------------------------------------------------- 1 | #Buidler files 2 | cache/ 3 | artifacts/ 4 | -------------------------------------------------------------------------------- /initializer_contracts_with_args/README.md: -------------------------------------------------------------------------------- 1 | # Initializer Contracts with arguments 2 | 3 | ## Context 4 | 5 | The previous experiment ([`initializer_contracts`](/initializer_contracts)) demonstrated a way to 6 | store constructors on chain and use them directly, so as to avoid the shortcomings of manually 7 | written initializer functions. The constructors used did not have arguments, and we left that as a 8 | second challenge to be tackled. That is the purpose of this experiment. 9 | 10 | ## Understanding the challenge 11 | 12 | Since transactions have only one data field, it must be used for both code and arguments in the 13 | case of contract creation: arguments must be concatenated after the code, and together are sent as 14 | data. Since during contract creation all transaction data becomes the code of the new contract, the 15 | constructor will read its arguments using `codecopy` (and may also use `codesize`). 16 | 17 | Given this, the simple approach of deploying one reusable initializer contract that we used in the 18 | first experiment will not work for arguments. The contract will not have any arguments where the 19 | constructor code expects them, and those specified by the user cannot be placed in the already 20 | deployed code. 21 | 22 | ## Solution #1: Deploy a new contract 23 | 24 | The simplest approach, and one that doesn't involve modifying code, is to redeploy the initializer 25 | contract every time it needs to be used, concatenated with the arguments specific to the instance 26 | that is being created. 27 | 28 | This is implemented in [`scripts/poc-2.js`](/scripts/poc-2.js). 29 | -------------------------------------------------------------------------------- /initializer_contracts_with_args/buidler-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | }; 3 | -------------------------------------------------------------------------------- /initializer_contracts_with_args/contracts/Greeter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | contract Greeter { 4 | 5 | string public greeting; 6 | uint256 public created; 7 | 8 | constructor(string _greeting) public { 9 | greeting = _greeting; 10 | created = block.number; 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /initializer_contracts_with_args/contracts/MyToken.sol: -------------------------------------------------------------------------------- 1 | import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 2 | 3 | contract MyToken is StandardToken { 4 | string public name; 5 | string public symbol; 6 | uint8 public decimals; 7 | 8 | constructor(uint256 _initialSupply, string _name, string _symbol, uint8 _decimals) public { 9 | name = _name; 10 | symbol = _symbol; 11 | decimals = _decimals; 12 | 13 | totalSupply_ = _initialSupply; 14 | balances[msg.sender] = _initialSupply; 15 | emit Transfer(address(0), msg.sender, _initialSupply); 16 | } 17 | } -------------------------------------------------------------------------------- /initializer_contracts_with_args/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Implements delegation of calls to other contracts, with proper 6 | * forwarding of return values and bubbling of failures. 7 | * It defines a fallback function that delegates all calls to the address 8 | * returned by the abstract _implementation() internal function. 9 | */ 10 | contract Proxy { 11 | /** 12 | * @dev Fallback function. 13 | * Implemented entirely in `_fallback`. 14 | */ 15 | function () payable external { 16 | _fallback(); 17 | } 18 | 19 | /** 20 | * @return The Address of the implementation. 21 | */ 22 | function _implementation() internal view returns (address); 23 | 24 | /** 25 | * @dev Delegates execution to an implementation contract. 26 | * This is a low level function that doesn't return to its internal call site. 27 | * It will return to the external caller whatever the implementation returns. 28 | * @param implementation Address to delegate. 29 | */ 30 | function _delegate(address implementation) internal { 31 | assembly { 32 | // Copy msg.data. We take full control of memory in this inline assembly 33 | // block because it will not return to Solidity code. We overwrite the 34 | // Solidity scratch pad at memory position 0. 35 | calldatacopy(0, 0, calldatasize) 36 | 37 | // Call the implementation. 38 | // out and outsize are 0 because we don't know the size yet. 39 | let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) 40 | 41 | // Copy the returned data. 42 | returndatacopy(0, 0, returndatasize) 43 | 44 | switch result 45 | // delegatecall returns 0 on error. 46 | case 0 { revert(0, returndatasize) } 47 | default { return(0, returndatasize) } 48 | } 49 | } 50 | 51 | /** 52 | * @dev Function that is run as the first thing in the fallback function. 53 | * Can be redefined in derived contracts to add functionality. 54 | * Redefinitions must call super._willFallback(). 55 | */ 56 | function _willFallback() internal { 57 | } 58 | 59 | /** 60 | * @dev fallback implementation. 61 | * Extracted to enable manual triggering. 62 | */ 63 | function _fallback() internal { 64 | _willFallback(); 65 | _delegate(_implementation()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /initializer_contracts_with_args/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initializer_contracts", 3 | "private": true, 4 | "author": "Francisco Giordano ", 5 | "license": "MIT", 6 | "dependencies": { 7 | "buidler": "^0.1.4", 8 | "openzeppelin-solidity": "^1.11.0-rc.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /initializer_contracts_with_args/scripts/poc-2.js: -------------------------------------------------------------------------------- 1 | function sleep(ms) { 2 | return new Promise(resolve => setTimeout(resolve, ms)); 3 | } 4 | 5 | async function getTransactionReceipt(txid) { 6 | while (true) { 7 | const receipt = await pweb3.eth.getTransactionReceipt(txid); 8 | 9 | if (receipt) { 10 | return receipt; 11 | } else { 12 | await sleep(100); 13 | } 14 | } 15 | } 16 | 17 | async function deploy(bytecode) { 18 | const tx = await pweb3.eth.sendTransaction({ 19 | from: (await pweb3.eth.getAccounts())[0], 20 | to: null, 21 | value: 0, 22 | data: bytecode, 23 | gas: (await pweb3.eth.getBlock('latest')).gasLimit, 24 | }); 25 | 26 | const receipt = await getTransactionReceipt(tx); 27 | 28 | return receipt.contractAddress; 29 | } 30 | 31 | // This is EVM assembly that, when used at the beginning of a contract, will 32 | // return all of the code that follows it. 33 | // 34 | // operation | bytecode | stack representation 35 | // ================================================= 36 | // push1 0D | 0x60 0x0D | 0x0D 37 | // dup1 | 0x80 | 0x0D 0x0D 38 | // codesize | 0x38 | 0x0D 0x0D 0xCS 39 | // sub | 0x03 | 0x0D 0xIS 40 | // dup1 | 0x80 | 0x0D 0xIS 0xIS 41 | // swap2 | 0x91 | 0xIS 0xIS 0x0D 42 | // push1 00 | 0x60 0x00 | 0xIS 0xIS 0x0D 0x00 43 | // codecopy | 0x39 | 0xIS 44 | // push1 00 | 0x60 0x00 | 0xIS 0x00 45 | // return | 0xf3 46 | const ASM_RETURN_REST = '600d80380380916000396000f3'; 47 | 48 | function prepareConstructor(bytecode) { 49 | return bytecode.replace('0x', `0x${ASM_RETURN_REST}${ASM_RETURN_REST}`); 50 | } 51 | 52 | function getArgs(contract, args) { 53 | return web3.eth.contract(contract.abi).new.getData(...args, { data: '0x' }); 54 | } 55 | 56 | async function main() { 57 | await run('compile'); 58 | 59 | const Greeter = artifacts.require('Greeter'); 60 | const AdminUpgradeabilityProxy = artifacts.require('AdminUpgradeabilityProxy'); 61 | 62 | const implementation = await deploy(Greeter.bytecode); 63 | const constructor = await deploy(prepareConstructor(Greeter.bytecode)); 64 | 65 | const args = getArgs(Greeter, ["Hello world!"]); 66 | 67 | const admin = (await pweb3.eth.getAccounts())[1]; 68 | const proxy = await AdminUpgradeabilityProxy.new(constructor, implementation, args, { from: admin }); 69 | 70 | const greeter = new Greeter(proxy.address); 71 | 72 | console.log(`Greeting: ${ await greeter.greeting() }`); 73 | console.log(`Block Nr: ${ (await greeter.created()).toString() }`); 74 | } 75 | 76 | main().catch(console.error); 77 | -------------------------------------------------------------------------------- /initializer_with_sol_editing/README.md: -------------------------------------------------------------------------------- 1 | # Initializer contracts with Solidity editing 2 | 3 | This is an alternative implementation for initializer contracts. While the experiment on `initializer_contracts` works at the bytecode level, this one attempts to achieve the same results by modifying the Solidity source code. 4 | 5 | The goal is, given a contract: 6 | - Change its constructor into an `initializer` function 7 | - Repeat for all of its ancestors, manually invoking the initializers from the base classes 8 | - Split the contract into an initializer version, that contains only the initializer (and any internal functions needed), and an implementation version, without the initializer 9 | 10 | Implementation-wise, the idea is to work with the AST, and use the source locations to perform changes directly to the original text, using primitives similar to the [source-code-fixer from Solium](https://github.com/duaraghav8/Solium/tree/master/lib/autofix). 11 | 12 | The upside here is that we don't depend that the compiler outputs bytecode with a certain format, gives us more flexibility for the changes we want to implement, and we know that all contracts can be verified (since they are generated from valid Solidity). 13 | 14 | The downside is that rewriting the source for all recursive dependencies can be cumbersome, and we end up more tied to Solidity as a language (though the bytecode version also depends on the output of the Solidity compiler, as other compilers may generate incompatible bytecode). -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/MyContract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Base1 { 4 | } 5 | 6 | contract Base2 is Base1 { 7 | } 8 | 9 | contract Base3 is Base1 { 10 | } 11 | 12 | contract MyContract is Base2, Base3 { 13 | uint256 public value; 14 | 15 | /** 16 | * @dev This is the constructor 17 | */ 18 | constructor(uint256 _value) { 19 | value = _value; 20 | } 21 | 22 | function set(uint256 _value) { 23 | require (_value != 0); 24 | value = _value; 25 | } 26 | 27 | function version() public pure returns (string) { 28 | return "V1"; 29 | } 30 | } -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/MyContract_implementation.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Base1 { 4 | } 5 | 6 | contract Base2 is Base1 { 7 | } 8 | 9 | contract Base3 is Base1 { 10 | } 11 | 12 | contract MyContract_implementation is Base2, Base3 { 13 | uint256 public value; 14 | 15 | /** 16 | * @dev This is the constructor 17 | */ 18 | 19 | 20 | function set(uint256 _value) { 21 | require (_value != 0); 22 | value = _value; 23 | } 24 | 25 | function version() public pure returns (string) { 26 | return "V1"; 27 | } 28 | } -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/MyContract_initializer.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | contract Base1 { 4 | } 5 | 6 | contract Base2 is Base1 { 7 | } 8 | 9 | contract Base3 is Base1 { 10 | } 11 | 12 | contract MyContract_initializer is Base2, Base3 { 13 | uint256 public value; 14 | 15 | /** 16 | * @dev This is the constructor 17 | */ 18 | function initializer(uint256 _value) { 19 | value = _value; 20 | } 21 | 22 | function set(uint256 _value) { 23 | require (_value != 0); 24 | value = _value; 25 | } 26 | 27 | function version() public pure returns (string) { 28 | return "V1"; 29 | } 30 | } -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/MyToken.sol: -------------------------------------------------------------------------------- 1 | import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 2 | 3 | contract MyToken is StandardToken { 4 | string public name; 5 | string public symbol; 6 | uint8 public decimals; 7 | 8 | constructor(uint256 _initialSupply, string _name, string _symbol, uint8 _decimals) public { 9 | name = _name; 10 | symbol = _symbol; 11 | decimals = _decimals; 12 | 13 | totalSupply_ = _initialSupply; 14 | balances[msg.sender] = _initialSupply; 15 | emit Transfer(address(0), msg.sender, _initialSupply); 16 | } 17 | } -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/MyToken_implementation.sol: -------------------------------------------------------------------------------- 1 | import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 2 | 3 | contract MyToken_implementation is StandardToken { 4 | string public name; 5 | string public symbol; 6 | uint8 public decimals; 7 | 8 | 9 | } -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/MyToken_initializer.sol: -------------------------------------------------------------------------------- 1 | import "openzeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 2 | 3 | contract MyToken_initializer is StandardToken { 4 | string public name; 5 | string public symbol; 6 | uint8 public decimals; 7 | 8 | function initializer(uint256 _initialSupply, string _name, string _symbol, uint8 _decimals) public { 9 | name = _name; 10 | symbol = _symbol; 11 | decimals = _decimals; 12 | 13 | totalSupply_ = _initialSupply; 14 | balances[msg.sender] = _initialSupply; 15 | emit Transfer(address(0), msg.sender, _initialSupply); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/MyToken_initializer_optimized.sol: -------------------------------------------------------------------------------- 1 | contract MyToken_initializer_optimized { 2 | event Transfer(address indexed from, address indexed to, uint256 value); 3 | 4 | mapping(address => uint256) balances; 5 | uint256 totalSupply_; 6 | 7 | string public name; 8 | string public symbol; 9 | uint8 public decimals; 10 | 11 | function initializer(uint256 _initialSupply, string _name, string _symbol, uint8 _decimals) public { 12 | name = _name; 13 | symbol = _symbol; 14 | decimals = _decimals; 15 | 16 | totalSupply_ = _initialSupply; 17 | balances[msg.sender] = _initialSupply; 18 | emit Transfer(address(0), msg.sender, _initialSupply); 19 | } 20 | } -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Implements delegation of calls to other contracts, with proper 6 | * forwarding of return values and bubbling of failures. 7 | * It defines a fallback function that delegates all calls to the address 8 | * returned by the abstract _implementation() internal function. 9 | */ 10 | contract Proxy { 11 | /** 12 | * @dev Fallback function. 13 | * Implemented entirely in `_fallback`. 14 | */ 15 | function () payable external { 16 | _fallback(); 17 | } 18 | 19 | /** 20 | * @return The Address of the implementation. 21 | */ 22 | function _implementation() internal view returns (address); 23 | 24 | /** 25 | * @dev Delegates execution to an implementation contract. 26 | * This is a low level function that doesn't return to its internal call site. 27 | * It will return to the external caller whatever the implementation returns. 28 | * @param implementation Address to delegate. 29 | */ 30 | function _delegate(address implementation) internal { 31 | assembly { 32 | // Copy msg.data. We take full control of memory in this inline assembly 33 | // block because it will not return to Solidity code. We overwrite the 34 | // Solidity scratch pad at memory position 0. 35 | calldatacopy(0, 0, calldatasize) 36 | 37 | // Call the implementation. 38 | // out and outsize are 0 because we don't know the size yet. 39 | let result := delegatecall(gas, implementation, 0, calldatasize, 0, 0) 40 | 41 | // Copy the returned data. 42 | returndatacopy(0, 0, returndatasize) 43 | 44 | switch result 45 | // delegatecall returns 0 on error. 46 | case 0 { revert(0, returndatasize) } 47 | default { return(0, returndatasize) } 48 | } 49 | } 50 | 51 | /** 52 | * @dev Function that is run as the first thing in the fallback function. 53 | * Can be redefined in derived contracts to add functionality. 54 | * Redefinitions must call super._willFallback(). 55 | */ 56 | function _willFallback() internal { 57 | } 58 | 59 | /** 60 | * @dev fallback implementation. 61 | * Extracted to enable manual triggering. 62 | */ 63 | function _fallback() internal { 64 | _willFallback(); 65 | _delegate(_implementation()); 66 | } 67 | } -------------------------------------------------------------------------------- /initializer_with_sol_editing/contracts/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import './Proxy.sol'; 4 | import 'openzeppelin-solidity/contracts/AddressUtils.sol'; 5 | 6 | /** 7 | * @title UpgradeabilityProxy 8 | * @dev This contract implements a proxy that allows to change the 9 | * implementation address to which it will delegate. 10 | * Such a change is called an implementation upgrade. 11 | */ 12 | contract UpgradeabilityProxy is Proxy { 13 | /** 14 | * @dev Emitted when the implementation is upgraded. 15 | * @param implementation Address of the new implementation. 16 | */ 17 | event Upgraded(address implementation); 18 | 19 | /** 20 | * @dev Storage slot with the address of the current implementation. 21 | * This is the keccak-256 hash of "org.zeppelinos.proxy.implementation", and is 22 | * validated in the constructor. 23 | */ 24 | bytes32 private constant IMPLEMENTATION_SLOT = 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3; 25 | 26 | /** 27 | * @dev Contract constructor. 28 | * @param _implementation Address of the initial implementation. 29 | */ 30 | constructor(address _constructor, address _implementation, bytes _args) public { 31 | assert(IMPLEMENTATION_SLOT == keccak256("org.zeppelinos.proxy.implementation")); 32 | 33 | if (_constructor != address(0)) { 34 | require(_constructor.delegatecall(_args)); 35 | } 36 | 37 | _setImplementation(_implementation); 38 | } 39 | 40 | /** 41 | * @dev Returns the current implementation. 42 | * @return Address of the current implementation 43 | */ 44 | function _implementation() internal view returns (address impl) { 45 | bytes32 slot = IMPLEMENTATION_SLOT; 46 | assembly { 47 | impl := sload(slot) 48 | } 49 | } 50 | 51 | /** 52 | * @dev Upgrades the proxy to a new implementation. 53 | * @param newImplementation Address of the new implementation. 54 | */ 55 | function _upgradeTo(address newImplementation) internal { 56 | _setImplementation(newImplementation); 57 | emit Upgraded(newImplementation); 58 | } 59 | 60 | /** 61 | * @dev Sets the implementation address of the proxy. 62 | * @param newImplementation Address of the new implementation. 63 | */ 64 | function _setImplementation(address newImplementation) private { 65 | require(AddressUtils.isContract(newImplementation), "Cannot set a proxy implementation to a non-contract address"); 66 | 67 | bytes32 slot = IMPLEMENTATION_SLOT; 68 | 69 | assembly { 70 | sstore(slot, newImplementation) 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /initializer_with_sol_editing/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { execSync } = require('child_process'); 3 | const _ = require('lodash'); 4 | const process = require('process'); 5 | 6 | function getSrcIndices(node) { 7 | return node.src.split(':').map(_.unary(parseInt)).slice(0, 2); 8 | } 9 | 10 | function extractNodeSource(source, node) { 11 | const [sourceStart, sourceLen] = getSrcIndices(node); 12 | return source.slice(sourceStart, sourceStart + sourceLen); 13 | } 14 | 15 | function getConstructorNode(contractName, contractData) { 16 | const contractNode = _.find(contractData.ast.nodes, ['name', contractName]); 17 | const constructorNode = _.find(contractNode.nodes, 'isConstructor'); 18 | return constructorNode; 19 | } 20 | 21 | function constructorToInitializer(contractData, constructorNode) { 22 | const sourceCode = contractData.source; 23 | const constructorSource = extractNodeSource(sourceCode, constructorNode) 24 | const initializerSource = constructorSource.replace(/\s*constructor/, 'function initializer'); 25 | return sourceCode.replace(constructorSource, initializerSource); 26 | } 27 | 28 | function removeConstructor(contractData, constructorNode) { 29 | const sourceCode = contractData.source; 30 | const [sourceStart, sourceLen] = getSrcIndices(constructorNode); 31 | return sourceCode.slice(0, sourceStart) + sourceCode.slice(sourceStart + sourceLen, sourceCode.length); 32 | } 33 | 34 | function renameContract(sourceCode, contractName, contractData, appendToName) { 35 | const contractNode = _.find(contractData.ast.nodes, ['name', contractName]); 36 | const contractSource = extractNodeSource(sourceCode, contractNode); 37 | const renamedContractSource = contractSource.replace(contractName, `${contractName}_${appendToName}`) 38 | return sourceCode.replace(contractSource, renamedContractSource); 39 | } 40 | 41 | function generateZosContractsFor(contractName) { 42 | const contractData = JSON.parse(fs.readFileSync(`./build/contracts/${contractName}.json`)); 43 | const constructorNode = getConstructorNode(contractName, contractData); 44 | const contractWithInitializer = renameContract(constructorToInitializer(contractData, constructorNode), contractName, contractData, 'initializer'); 45 | const contractWithoutConstructor = renameContract(removeConstructor(contractData, constructorNode), contractName, contractData, 'implementation'); 46 | 47 | fs.writeFileSync(`./contracts/${contractName}_initializer.sol`, contractWithInitializer); 48 | fs.writeFileSync(`./contracts/${contractName}_implementation.sol`, contractWithoutConstructor); 49 | } 50 | 51 | function compile() { 52 | execSync('npm run compile'); 53 | } 54 | 55 | function main(contractName) { 56 | console.log("Processing", contractName); 57 | generateZosContractsFor(contractName); 58 | } 59 | 60 | main(process.argv[2]); -------------------------------------------------------------------------------- /initializer_with_sol_editing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initializer_with_sol_editing", 3 | "version": "0.0.1", 4 | "description": "Experiments on initializers by editing Solidity source code", 5 | "main": "index.js", 6 | "scripts": { 7 | "compile": "truffle compile", 8 | "ganache": "ganache-cli -l 6000000", 9 | "test": "truffle test", 10 | "transform": "node ./index.js" 11 | }, 12 | "author": "spalladino@gmail.com", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "chai": "^4.1.2", 16 | "ganache-cli": "^6.1.6", 17 | "mocha": "^5.2.0", 18 | "truffle": "^4.1.13" 19 | }, 20 | "dependencies": { 21 | "lodash": "^4.17.13", 22 | "openzeppelin-solidity": "^1.11.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /initializer_with_sol_editing/test/Deploy.test.js: -------------------------------------------------------------------------------- 1 | const AdminUpgradeabilityProxy = artifacts.require('AdminUpgradeabilityProxy'); 2 | 3 | require('chai').should(); 4 | 5 | const log = function (text) { 6 | console.log(' ' + text); 7 | }; 8 | 9 | function getGas(contract) { 10 | return web3.eth.getTransactionReceipt(contract.transactionHash).gasUsed; 11 | } 12 | 13 | contract('Contracts', function ([owner, user]) { 14 | const shouldDeploy = function(contractName) { 15 | describe(contractName, function () { 16 | beforeEach(function () { 17 | this.Original = artifacts.require(contractName); 18 | this.Initializer = artifacts.require(contractName + '_initializer'); 19 | this.Implementation = artifacts.require(contractName + '_implementation'); 20 | }) 21 | 22 | describe('without zOS', function () { 23 | it('should initialize the contract', async function () { 24 | log('Deploying original contract...') 25 | const instance = await this.Original.new(100, 'FooToken', 'FOO', 8); 26 | log(' Gas used: ' + getGas(instance)) 27 | }); 28 | }); 29 | 30 | describe('via zOS', function () { 31 | it('should initialize the contract', async function () { 32 | log('Deploying initializer version...') 33 | const initializer = await this.Initializer.new(); 34 | log(' Gas used: ' + getGas(initializer)); 35 | log('Deploying implementation...') 36 | const implementation = await this.Implementation.new(); 37 | log(' Gas used: ' + getGas(implementation)); 38 | const initData = initializer.initializer.request(100, 'FooToken', 'FOO', 8).params[0].data; 39 | log('Deploying proxy...') 40 | const proxy = await AdminUpgradeabilityProxy.new(initializer.address, implementation.address, initData); 41 | log(' Gas used: ' + getGas(proxy)); 42 | const instance = this.Original.at(proxy.address); 43 | }); 44 | }); 45 | }); 46 | }; 47 | 48 | shouldDeploy('MyToken'); 49 | shouldDeploy('MyNFT'); 50 | }); -------------------------------------------------------------------------------- /initializer_with_sol_editing/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | local: { 4 | host: 'localhost', 5 | port: 9545, 6 | network_id: '*', 7 | gas: 6000000 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/README.md: -------------------------------------------------------------------------------- 1 | # Migrating legacy (non-upgradeable) token to upgradeability with managed strategy 2 | 3 | This idea builds on _upgradeability using unstructured storage_, and shows how one would migrate an existing non-upgradeable token into zOS for upgradeability support. This approach uses the managed strategy. For an alternative approach, see the `migrating_legacy_token_opt_in/` folder. 4 | 5 | With the managed strategy, the migration is fully controlled and paid for by the development team. In this case, the dev team needs to freeze or pause the legacy token. 6 | Then, they deploy an upgradeable plain `MintableToken` and `PausableToken` instance, and migrate the token balances of all users in a single event, centralized way. 7 | During said migration event, the new upgradeable token will not have any additional functionality the legacy token might have had, as it's just a plain vanilla `MintableToken` and `PausableToken`. The developers should pause 8 | Once the balance migration is over, the devs can upgrade the token to a new implementation, which contains the (optionally modified) full behavior of the legacy token. At this point, the token is migrated to an upgradeable version with the same functionality as the legacy token. 9 | 10 | 11 | Pros: 12 | - The upside of this approach is that users don't need to pay the migration gas costs 13 | - Migration to new token code happens in a single event, meaning users need notworry about if/when/how to upgrade their balances. 14 | 15 | Cons: 16 | - Requires the legacy contract to be pausable or freezable. 17 | - Requires more control from the devs, and may not be desirable for all projects (for legal, community concerns, or project management reasons). 18 | - Requires coordinating with exchanges, wallets and other software providers and ecosystem players to update their references to the token address on the switch date. 19 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/contracts/BurnContract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | //empty contract for burning tokens 4 | contract BurnContract { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/contracts/LegacyToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 4 | import "zeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; 5 | 6 | contract LegacyToken is StandardToken, MintableToken { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/contracts/ManagedMigrationToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import 'zeppelin-solidity/contracts/token/ERC20/MintableToken.sol'; 4 | import 'zeppelin-solidity/contracts/token/ERC20/PausableToken.sol'; 5 | import 'zeppelin-solidity/contracts/token/ERC20/ERC20.sol'; 6 | 7 | /** 8 | * @title ManagedMigrationToken 9 | * @dev migration version of the token, used for migrating balances 10 | * Uses the managed migration strategy 11 | */ 12 | contract ManagedMigrationToken is MintableToken, PausableToken { 13 | 14 | // Tells whether the token has been initialized or not 15 | bool internal initialized; 16 | 17 | //Address of old token contract 18 | ERC20 internal legacyToken; 19 | 20 | function initialize(address _owner, address _legacyToken) public { 21 | require(!initialized); 22 | owner = this; 23 | this.pause(); 24 | owner = _owner; 25 | initialized = true; 26 | legacyToken = ERC20(_legacyToken); 27 | } 28 | 29 | /** 30 | * @dev Copies the balance of a batch of addresses from the legacy contract 31 | * @param _holders Array of addresses to migrate balance 32 | */ 33 | function migrateBalances(address[] _holders) onlyOwner public { 34 | for (uint256 i = 0; i < _holders.length; i++) { 35 | migrateBalance(_holders[i]); 36 | } 37 | } 38 | 39 | /** 40 | * @dev Copies the balance of a single addresses from the legacy contract 41 | * @param _holder Address to migrate balance 42 | */ 43 | function migrateBalance(address _holder) onlyOwner public { 44 | require(balances[_holder] == 0); 45 | 46 | uint256 amount = legacyToken.balanceOf(_holder); 47 | require(amount > 0); 48 | 49 | mint(_holder, amount); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/contracts/TestingImports.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | // NOTE: The contracts imported here are for testing purposes only, required 4 | // by truffle's artifacts.require(). 5 | 6 | import "zos-upgradeability/contracts/upgradeability/OwnedUpgradeabilityProxy.sol"; 7 | 8 | contract TestingImports {} 9 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/contracts/Token_V1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import 'zeppelin-solidity/contracts/token/ERC20/BurnableToken.sol'; 4 | 5 | /** 6 | * @title Token_V1 7 | * @dev Version 1 of the token. 8 | * The idea here is to extend a token behaviour providing, 9 | * as an example, burnable functionalities, 10 | * and removing the minting capabilities. 11 | * Note that the V1 implementation needs to share the same 12 | * storage structure as the MigrationToken 13 | */ 14 | contract Token_V1 is BurnableToken { 15 | } 16 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "migrating_legacy_token_managed", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Manuel Araoz ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "ethereumjs-abi": "^0.6.5", 12 | "truffle": "^4.1.5", 13 | "zeppelin-solidity": "^1.8.0", 14 | "zos-upgradeability": "git://github.com/zeppelinos/upgradeability-lib.git#68cd61fb48d0e4eec7142241448efa40c395bf33" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/test/migrateFromERC20.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const abi = require('ethereumjs-abi'); 4 | 5 | var LegacyToken = artifacts.require('./LegacyToken.sol') 6 | const ManagedMigrationToken = artifacts.require('ManagedMigrationToken') 7 | const OwnedUpgradeabilityProxy = artifacts.require('zos-upgradeability/contracts/upgradeability/OwnedUpgradeabilityProxy.sol') 8 | 9 | 10 | contract('LegacyToken migration', function (accounts) { 11 | var legacyToken, newToken, holders; 12 | 13 | const owner = accounts[9]; 14 | 15 | 16 | before(async function () { 17 | 18 | //Deploy LegacyToken and mint tokens 19 | legacyToken = await LegacyToken.new(); 20 | holders = []; 21 | for(var i = 1; i < 5; i++) { 22 | holders.push(accounts[i]) 23 | await legacyToken.mint(accounts[i], i * 10) 24 | } 25 | 26 | //Deploy new upgradeable token 27 | const proxy = await OwnedUpgradeabilityProxy.new() 28 | const migration = await ManagedMigrationToken.new() 29 | const methodId = abi.methodID('initialize', ['address', 'address']).toString('hex') 30 | const params = abi.rawEncode(['address','address'], 31 | [owner, legacyToken.address]).toString('hex') 32 | const initializeData = '0x' + methodId + params 33 | await proxy.upgradeToAndCall(migration.address, initializeData) 34 | 35 | newToken = await ManagedMigrationToken.at(proxy.address) 36 | }); 37 | 38 | it('maintains correct balances after migrating', async function () { 39 | await newToken.migrateBalances(holders, {from: owner}) 40 | for(var i = 1; i < 5; i++) { 41 | let origBalance = await legacyToken.balanceOf(accounts[i]); 42 | let newTokenBalance = await newToken.balanceOf(accounts[i]); 43 | assert(origBalance.eq(newTokenBalance)); 44 | } 45 | }); 46 | 47 | it('total supply of the old token and new token should be equal', async function() { 48 | let totalSupplyLegacy = await legacyToken.totalSupply(); 49 | let totalSupplyUpgraded = await newToken.totalSupply(); 50 | assert(totalSupplyLegacy.eq(totalSupplyUpgraded)); 51 | }) 52 | 53 | }); 54 | -------------------------------------------------------------------------------- /migrating_legacy_token_managed/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/README.md: -------------------------------------------------------------------------------- 1 | # Migrating legacy (non-upgradeable) token to upgradeability with opt-in strategy 2 | 3 | This idea builds on _upgradeability using unstructured storage_, and shows how one would migrate an existing non-upgradeable token into zOS for upgradeability support. This approach uses the opt-in strategy. For an alternative approach, see the `migrating_legacy_token_managed/` folder. 4 | 5 | With the opt-in strategy, the migration of the token balances is optional and performed and paid for by the token holders. The new token contract starts with no initial supply and no balances. The only way to "mint" the new tokens is for users to "turn in" their old ones. This is done by first approving the amount they want to migrate via `OldERC20.approve(newTokenAddress, amountToMigrate)` and then calling a function in the new token called `migrateTokens`. The old tokens are sent to a burn address, and the holder receives an equal amount in the new contract. 6 | 7 | Pros: 8 | - Exchange of the legacy token can continue as before, so exchanges don't have to scramble to support the new token version 9 | - Users (probably speculators) who don't need/aren't interested in the new functionality don't have to upgrade 10 | - If the demand for the upgraded token becomes higher than the legacy token, the market will incentivize speculators to upgrade and exchanges to support the new token/drop support for the old one. 11 | Onboarding becomes a more gradual and organic process 12 | 13 | Cons: 14 | 15 | - Assumes the old token contract is not buggy. Reason for upgrading is to add new functionality, not to fix critical issues in the old token. 16 | - The token's supply is split amongst two contracts. 17 | - Requires token users pay for migration gas costs. 18 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/TestingImports.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | // NOTE: The contracts imported here are for testing purposes only, required 4 | // by truffle's artifacts.require(). 5 | 6 | import "zos-upgradeability/contracts/upgradeability/OwnedUpgradeabilityProxy.sol"; 7 | 8 | contract TestingImports {} 9 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/latest_erc20_token/BurnContract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | //empty contract for burning tokens 4 | contract BurnContract { 5 | 6 | } 7 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/latest_erc20_token/LegacyToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import "zeppelin-solidity/contracts/token/ERC20/StandardToken.sol"; 4 | import "zeppelin-solidity/contracts/token/ERC20/MintableToken.sol"; 5 | 6 | contract LegacyToken is StandardToken, MintableToken { 7 | 8 | } 9 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/latest_erc20_token/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/latest_erc20_token/OptInMigrationToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import 'zeppelin-solidity/contracts/token/ERC20/MintableToken.sol'; 4 | import 'zeppelin-solidity/contracts/token/ERC20/ERC20.sol'; 5 | 6 | /** 7 | * @title OptInMigrationToken 8 | * @dev migration version of the token, used for migrating balances 9 | * Uses the opt-in migration strategy 10 | */ 11 | contract OptInMigrationToken is MintableToken { 12 | 13 | // Tells whether the token has been initialized or not 14 | bool internal initialized; 15 | 16 | //Address of old token contract 17 | ERC20 internal legacyToken; 18 | 19 | //Address where we're sending old tokens to burn them 20 | address internal burnAddress; 21 | 22 | function initialize(address _legacyToken, address _burnAddress) public { 23 | require(!initialized); 24 | owner = this; 25 | initialized = true; 26 | legacyToken = ERC20(_legacyToken); 27 | burnAddress = _burnAddress; 28 | } 29 | 30 | function migrateToken(uint _amount) public { 31 | migrateTokenTo(_amount, msg.sender); 32 | } 33 | 34 | function migrateTokenTo(uint _amount, address _to) public { 35 | require(legacyToken.transferFrom(msg.sender, burnAddress, _amount)); 36 | this.mint(_to, _amount); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/latest_erc20_token/Token_V1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import 'zeppelin-solidity/contracts/token/ERC20/BurnableToken.sol'; 4 | 5 | /** 6 | * @title Token_V1 7 | * @dev Version 1 of the token. 8 | * The idea here is to extend a token behaviour providing, 9 | * as an example, burnable functionalities, 10 | * and removing the minting capabilities. 11 | * Note that the V1 implementation needs to share the same 12 | * storage structure as the MigrationToken 13 | */ 14 | contract Token_V1 is BurnableToken { 15 | } 16 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/MyToken_V0.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./zeppelin-solidity-1.1.0/math/SafeMath.sol"; 4 | import "./zeppelin-solidity-1.1.0/token/PausableToken.sol"; 5 | import "./zeppelin-solidity-1.1.0/token/MintableToken.sol"; 6 | import "./zeppelin-solidity-1.1.0/token/TokenTimelock.sol"; 7 | 8 | /** 9 | * @title MyToken V0 10 | */ 11 | contract MyToken_V0 is PausableToken, MintableToken { 12 | using SafeMath for uint256; 13 | 14 | string public name = "MyToken"; 15 | string public symbol = "MTK"; 16 | uint public decimals = 18; 17 | 18 | function mintTimelocked(address _to, uint256 _amount, uint256 _releaseTime) onlyOwner canMint returns (TokenTimelock) { 19 | TokenTimelock timelock = new TokenTimelock(this, _to, _releaseTime); 20 | mint(timelock, _amount); 21 | return timelock; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/MyToken_V1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.22; 2 | 3 | import "openzeppelin-zos/contracts/math/SafeMath.sol"; 4 | import "openzeppelin-zos/contracts/token/ERC20/MintableToken.sol"; 5 | 6 | contract OldERC20 { 7 | function totalSupply() public returns (uint); 8 | function balanceOf(address who) constant returns (uint); 9 | function transfer(address to, uint value); 10 | function allowance(address owner, address spender) constant returns (uint); 11 | function transferFrom(address from, address to, uint value); 12 | function approve(address spender, uint value); 13 | } 14 | 15 | /** 16 | * @title MyToken V1 17 | */ 18 | contract MyToken_V1 is MintableToken { 19 | using SafeMath for uint256; 20 | 21 | string public name = "MyToken"; 22 | string public symbol = "MTK"; 23 | uint256 public decimals = 18; 24 | 25 | address public burnAddress; 26 | OldERC20 public legacyToken; 27 | 28 | function initialize(address _legacyToken, address _burnAddress) isInitializer("MyToken", "1.0.0") public { 29 | MintableToken.initialize(this); 30 | burnAddress = _burnAddress; 31 | legacyToken = OldERC20(_legacyToken); 32 | } 33 | 34 | function migrateToken(uint _amount) public { 35 | migrateTokenTo(_amount, msg.sender); 36 | } 37 | 38 | function migrateTokenTo(uint _amount, address _to) public { 39 | legacyToken.transferFrom(msg.sender, burnAddress, _amount); 40 | require(this.mint(_to, _amount)); 41 | } 42 | 43 | // these methods are just for testing purposes 44 | function safeApprove(address _spender, uint256 _value) public { 45 | require(super.approve(_spender, _value)); 46 | } 47 | 48 | function safeTransfer(address _to, uint256 _value) public { 49 | require(super.transfer(_to, _value)); 50 | } 51 | 52 | function safeTransferFrom(address _from, address _to, uint256 _value) public { 53 | require(super.transferFrom(_from, _to, _value)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/lifecycle/Pausable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "../ownership/Ownable.sol"; 4 | 5 | /** 6 | * @title Pausable 7 | * @dev Base contract which allows children to implement an emergency stop mechanism. 8 | */ 9 | contract Pausable is Ownable { 10 | event Pause(); 11 | event Unpause(); 12 | 13 | bool public paused = false; 14 | 15 | modifier whenNotPaused() { 16 | if (paused) throw; 17 | _; 18 | } 19 | 20 | modifier whenPaused { 21 | if (!paused) throw; 22 | _; 23 | } 24 | 25 | function pause() onlyOwner whenNotPaused returns (bool) { 26 | paused = true; 27 | Pause(); 28 | return true; 29 | } 30 | 31 | function unpause() onlyOwner whenPaused returns (bool) { 32 | paused = false; 33 | Unpause(); 34 | return true; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/math/SafeMath.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /** 4 | * Math operations with safety checks 5 | */ 6 | library SafeMath { 7 | function mul(uint a, uint b) internal returns (uint) { 8 | uint c = a * b; 9 | assert(a == 0 || c / a == b); 10 | return c; 11 | } 12 | 13 | function div(uint a, uint b) internal returns (uint) { 14 | // assert(b > 0); // Solidity automatically throws when dividing by 0 15 | uint c = a / b; 16 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 17 | return c; 18 | } 19 | 20 | function sub(uint a, uint b) internal returns (uint) { 21 | assert(b <= a); 22 | return a - b; 23 | } 24 | 25 | function add(uint a, uint b) internal returns (uint) { 26 | uint c = a + b; 27 | assert(c >= a); 28 | return c; 29 | } 30 | 31 | function max64(uint64 a, uint64 b) internal constant returns (uint64) { 32 | return a >= b ? a : b; 33 | } 34 | 35 | function min64(uint64 a, uint64 b) internal constant returns (uint64) { 36 | return a < b ? a : b; 37 | } 38 | 39 | function max256(uint256 a, uint256 b) internal constant returns (uint256) { 40 | return a >= b ? a : b; 41 | } 42 | 43 | function min256(uint256 a, uint256 b) internal constant returns (uint256) { 44 | return a < b ? a : b; 45 | } 46 | 47 | function assert(bool assertion) internal { 48 | if (!assertion) { 49 | throw; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/ownership/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /** 4 | * @title Ownable 5 | * @dev The Ownable contract has an owner address, and provides basic authorization control 6 | * functions, this simplifies the implementation of "user permissions". 7 | */ 8 | contract Ownable { 9 | address public owner; 10 | 11 | function Ownable() { 12 | owner = msg.sender; 13 | } 14 | 15 | modifier onlyOwner() { 16 | if (msg.sender != owner) { 17 | throw; 18 | } 19 | _; 20 | } 21 | 22 | function transferOwnership(address newOwner) onlyOwner { 23 | if (newOwner != address(0)) { 24 | owner = newOwner; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/token/BasicToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./ERC20Basic.sol"; 4 | import "../math/SafeMath.sol"; 5 | 6 | /** 7 | * @title Basic token 8 | * @dev Basic version of StandardToken, with no allowances. 9 | */ 10 | contract BasicToken is ERC20Basic { 11 | using SafeMath for uint; 12 | 13 | mapping(address => uint) balances; 14 | 15 | modifier onlyPayloadSize(uint size) { 16 | if(msg.data.length < size + 4) { 17 | throw; 18 | } 19 | _; 20 | } 21 | 22 | function transfer(address _to, uint _value) onlyPayloadSize(2 * 32) { 23 | balances[msg.sender] = balances[msg.sender].sub(_value); 24 | balances[_to] = balances[_to].add(_value); 25 | Transfer(msg.sender, _to, _value); 26 | } 27 | 28 | function balanceOf(address _owner) constant returns (uint balance) { 29 | return balances[_owner]; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/token/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./ERC20Basic.sol"; 4 | 5 | /** 6 | * @title ERC20 interface 7 | * @dev see https://github.com/ethereum/EIPs/issues/20 8 | */ 9 | contract ERC20 is ERC20Basic { 10 | function allowance(address owner, address spender) constant returns (uint); 11 | function transferFrom(address from, address to, uint value); 12 | function approve(address spender, uint value); 13 | event Approval(address indexed owner, address indexed spender, uint value); 14 | } 15 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/token/ERC20Basic.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | /** 4 | * @title ERC20Basic 5 | * @dev Simpler version of ERC20 interface 6 | * @dev see https://github.com/ethereum/EIPs/issues/20 7 | */ 8 | contract ERC20Basic { 9 | uint public totalSupply; 10 | function balanceOf(address who) constant returns (uint); 11 | function transfer(address to, uint value); 12 | event Transfer(address indexed from, address indexed to, uint value); 13 | } 14 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/token/MintableToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./StandardToken.sol"; 4 | import "../ownership/Ownable.sol"; 5 | 6 | /** 7 | * @title Mintable token 8 | * @dev Simple ERC20 Token example, with mintable token creation 9 | * Based on code by TokenMarketNet: https://github.com/TokenMarketNet/ico/blob/master/contracts/MintableToken.sol 10 | */ 11 | contract MintableToken is StandardToken, Ownable { 12 | event Mint(address indexed to, uint value); 13 | event MintFinished(); 14 | 15 | bool public mintingFinished = false; 16 | uint public totalSupply = 0; 17 | 18 | modifier canMint() { 19 | if(mintingFinished) throw; 20 | _; 21 | } 22 | 23 | function mint(address _to, uint _amount) onlyOwner canMint returns (bool) { 24 | totalSupply = totalSupply.add(_amount); 25 | balances[_to] = balances[_to].add(_amount); 26 | Mint(_to, _amount); 27 | return true; 28 | } 29 | 30 | function finishMinting() onlyOwner returns (bool) { 31 | mintingFinished = true; 32 | MintFinished(); 33 | return true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/token/PausableToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./StandardToken.sol"; 4 | import "../lifecycle/Pausable.sol"; 5 | 6 | /** 7 | * Pausable token 8 | * Simple ERC20 Token example, with pausable token creation 9 | **/ 10 | contract PausableToken is StandardToken, Pausable { 11 | 12 | function transfer(address _to, uint _value) whenNotPaused { 13 | super.transfer(_to, _value); 14 | } 15 | 16 | function transferFrom(address _from, address _to, uint _value) whenNotPaused { 17 | super.transferFrom(_from, _to, _value); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/token/StandardToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./ERC20.sol"; 4 | import "./BasicToken.sol"; 5 | 6 | /** 7 | * @title Standard ERC20 token 8 | * @dev Implemantation of the basic standart token. 9 | * @dev Based on code by FirstBlood: https://github.com/Firstbloodio/token/blob/master/smart_contract/FirstBloodToken.sol 10 | */ 11 | contract StandardToken is BasicToken, ERC20 { 12 | 13 | mapping (address => mapping (address => uint)) allowed; 14 | 15 | function transferFrom(address _from, address _to, uint _value) onlyPayloadSize(3 * 32) { 16 | var _allowance = allowed[_from][msg.sender]; 17 | 18 | balances[_to] = balances[_to].add(_value); 19 | balances[_from] = balances[_from].sub(_value); 20 | allowed[_from][msg.sender] = _allowance.sub(_value); 21 | Transfer(_from, _to, _value); 22 | } 23 | 24 | function approve(address _spender, uint _value) { 25 | if ((_value != 0) && (allowed[msg.sender][_spender] != 0)) throw; 26 | allowed[msg.sender][_spender] = _value; 27 | Approval(msg.sender, _spender, _value); 28 | } 29 | 30 | function allowance(address _owner, address _spender) constant returns (uint remaining) { 31 | return allowed[_owner][_spender]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/contracts/old_erc20_token/zeppelin-solidity-1.1.0/token/TokenTimelock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.11; 2 | 3 | import "./ERC20Basic.sol"; 4 | 5 | /** 6 | * @title TokenTimelock 7 | * @dev TokenTimelock is a token holder contract that will allow a 8 | * beneficiary to extract the tokens after a time has passed 9 | */ 10 | contract TokenTimelock { 11 | 12 | ERC20Basic token; 13 | address beneficiary; 14 | uint releaseTime; 15 | 16 | function TokenTimelock(ERC20Basic _token, address _beneficiary, uint _releaseTime) { 17 | require(_releaseTime > now); 18 | token = _token; 19 | beneficiary = _beneficiary; 20 | releaseTime = _releaseTime; 21 | } 22 | 23 | function claim() { 24 | require(msg.sender == beneficiary); 25 | require(now >= releaseTime); 26 | 27 | uint amount = token.balanceOf(this); 28 | require(amount > 0); 29 | 30 | token.transfer(beneficiary, amount); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "migrating_legacy_token_opt_in", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Facu Spagnuolo ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "ethereumjs-abi": "^0.6.5", 12 | "truffle": "^4.1.7", 13 | "openzeppelin-zos": "^1.9.1", 14 | "zeppelin-solidity": "^1.8.0", 15 | "zos-upgradeability": "git://github.com/zeppelinos/upgradeability-lib.git#68cd61fb48d0e4eec7142241448efa40c395bf33" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /migrating_legacy_token_opt_in/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "zos-lab", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Francisco Giordano ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "scripts/test.sh", 9 | "install-all": "npx recursive-install" 10 | }, 11 | "devDependencies": { 12 | "recursive-install": "^1.3.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | for D in `find . -maxdepth 1 -type d \( ! -name . \)` 4 | do 5 | cd $D 6 | if [ -f package.json ]; then 7 | echo "Running tests from $D" 8 | npm test 9 | [[ $? -ne 0 ]] && exit 10 | fi 11 | cd .. 12 | done 13 | -------------------------------------------------------------------------------- /upgradeability_ownership/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /upgradeability_ownership/contracts/OwnedUpgradeabilityStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import "./UpgradeabilityStorage.sol"; 4 | 5 | /** 6 | * @title OwnedUpgradeabilityStorage 7 | * @dev This contract keeps track of the upgradeability owner 8 | */ 9 | contract OwnedUpgradeabilityStorage is UpgradeabilityStorage { 10 | // Owner of the contract 11 | address private _upgradeabilityOwner; 12 | 13 | /** 14 | * @dev Tells the address of the owner 15 | * @return the address of the owner 16 | */ 17 | function upgradeabilityOwner() public view returns (address) { 18 | return _upgradeabilityOwner; 19 | } 20 | 21 | /** 22 | * @dev Sets the address of the owner 23 | */ 24 | function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal { 25 | _upgradeabilityOwner = newUpgradeabilityOwner; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /upgradeability_ownership/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Gives the possibility to delegate any call to a foreign implementation. 6 | */ 7 | contract Proxy { 8 | 9 | /** 10 | * @dev Tells the address of the implementation where every call will be delegated. 11 | * @return address of the implementation to which it will be delegated 12 | */ 13 | function implementation() public view returns (address); 14 | 15 | /** 16 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 17 | * This function will return whatever the implementation call returns 18 | */ 19 | function () payable public { 20 | address _impl = implementation(); 21 | require(_impl != address(0)); 22 | 23 | assembly { 24 | let ptr := mload(0x40) 25 | calldatacopy(ptr, 0, calldatasize) 26 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 27 | let size := returndatasize 28 | returndatacopy(ptr, 0, size) 29 | 30 | switch result 31 | case 0 { revert(ptr, size) } 32 | default { return(ptr, size) } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /upgradeability_ownership/contracts/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './Proxy.sol'; 4 | import './UpgradeabilityStorage.sol'; 5 | 6 | /** 7 | * @title UpgradeabilityProxy 8 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 9 | */ 10 | contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage { 11 | /** 12 | * @dev This event will be emitted every time the implementation gets upgraded 13 | * @param version representing the version name of the upgraded implementation 14 | * @param implementation representing the address of the upgraded implementation 15 | */ 16 | event Upgraded(string version, address indexed implementation); 17 | 18 | /** 19 | * @dev Upgrades the implementation address 20 | * @param version representing the version name of the new implementation to be set 21 | * @param implementation representing the address of the new implementation to be set 22 | */ 23 | function _upgradeTo(string version, address implementation) internal { 24 | require(_implementation != implementation); 25 | _version = version; 26 | _implementation = implementation; 27 | Upgraded(version, implementation); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /upgradeability_ownership/contracts/UpgradeabilityStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title UpgradeabilityStorage 5 | * @dev This contract holds all the necessary state variables to support the upgrade functionality 6 | */ 7 | contract UpgradeabilityStorage { 8 | // Version name of the current implementation 9 | string internal _version; 10 | 11 | // Address of the current implementation 12 | address internal _implementation; 13 | 14 | /** 15 | * @dev Tells the version name of the current implementation 16 | * @return string representing the name of the current version 17 | */ 18 | function version() public view returns (string) { 19 | return _version; 20 | } 21 | 22 | /** 23 | * @dev Tells the address of the current implementation 24 | * @return address of the current implementation 25 | */ 26 | function implementation() public view returns (address) { 27 | return _implementation; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /upgradeability_ownership/contracts/ownership/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './OwnableStorage.sol'; 4 | 5 | /** 6 | * @title Ownable 7 | * @dev This contract has the owner address providing basic authorization control 8 | */ 9 | contract Ownable is OwnableStorage { 10 | /** 11 | * @dev Event to show ownership has been transferred 12 | * @param previousOwner representing the address of the previous owner 13 | * @param newOwner representing the address of the new owner 14 | */ 15 | event OwnershipTransferred(address previousOwner, address newOwner); 16 | 17 | /** 18 | * @dev The constructor sets the original owner of the contract to the sender account. 19 | */ 20 | function Ownable() public { 21 | setOwner(msg.sender); 22 | } 23 | 24 | /** 25 | * @dev Throws if called by any account other than the owner. 26 | */ 27 | modifier onlyOwner() { 28 | require(msg.sender == owner()); 29 | _; 30 | } 31 | 32 | /** 33 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 34 | * @param newOwner The address to transfer ownership to. 35 | */ 36 | function transferOwnership(address newOwner) public onlyOwner { 37 | require(newOwner != address(0)); 38 | OwnershipTransferred(owner(), newOwner); 39 | setOwner(newOwner); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /upgradeability_ownership/contracts/ownership/OwnableStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title OwnableStorage 5 | * @dev This contract keeps track of the owner's address 6 | */ 7 | contract OwnableStorage { 8 | // Owner of the contract 9 | address private _owner; 10 | 11 | /** 12 | * @dev Tells the address of the owner 13 | * @return the address of the owner 14 | */ 15 | function owner() public view returns (address) { 16 | return _owner; 17 | } 18 | 19 | /** 20 | * @dev Sets a new owner address 21 | */ 22 | function setOwner(address newOwner) internal { 23 | _owner = newOwner; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /upgradeability_ownership/contracts/test/Token_V1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './Token_V0.sol'; 4 | 5 | /** 6 | * @title Token_V1 7 | * @dev Version 1 of a token to show upgradeability. 8 | * The idea here is to extend a token behaviour providing burnable functionalities 9 | * in addition to what's provided in version 0 10 | */ 11 | contract Token_V1 is Token_V0 { 12 | event Burn(address indexed burner, uint256 value); 13 | 14 | function burn(uint256 value) public { 15 | require(balanceOf(msg.sender) >= value); 16 | _balances[msg.sender] = _balances[msg.sender].sub(value); 17 | _totalSupply = _totalSupply.sub(value); 18 | Burn(msg.sender, value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /upgradeability_ownership/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /upgradeability_ownership/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upgradeability-ownership", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Facu Spagnuolo ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "ethereumjs-abi": "^0.6.5", 12 | "truffle": "^4.0.4", 13 | "zeppelin-solidity": "^1.5.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /upgradeability_ownership/readme.md: -------------------------------------------------------------------------------- 1 | # Upgradeability ownership 2 | 3 | This idea builds on _upgradeability using inherited storage_. In addition, it proposes an authorization control flow to 4 | restrict the access of the contract upgradeability mechanism. 5 | 6 | The approach consists in having a proxy that delegates calls to specific implementations which can be upgraded, without 7 | changing the storage structure of the previous implementations, but having the chance to add new state variables. Given 8 | the proxy uses `delegatecall` to resolve the requested behaviors, the upgradeable contract's state will be stored in 9 | the proxy contract itself. 10 | 11 | Since we have three really different kinds of data, one related to the upgradeability mechanism, another for the 12 | authorization process and the one strictly related to the token contract domain, naming was really important here to 13 | expressed correctly what's going on. This is the proposed model: 14 | 15 | ------- ======================= ============================ 16 | | Proxy | ║ UpgradeabilityStorage ║ ← ║ OwnedUpgradeabilityStorage ║ 17 | ------- ======================= ============================ 18 | ↑ ↑ ↑ ↑ ↑ 19 | --------------------- | ---------- ---------- 20 | | UpgradeabilityProxy | | | Token_V0 | ← | Token_V1 | 21 | --------------------- | ---------- ---------- 22 | ↑ | 23 | -------------------------- 24 | | OwnedUpgradeabilityProxy | 25 | -------------------------- 26 | 27 | `Proxy`, `UpgradeabilityProxy` and `UpgradeabilityStorage` are generic contracts that can be used to implement 28 | upgradeability through proxies. In this example we use all these contracts to implement an upgradeable ERC20 token. 29 | 30 | The `UpgradeabilityStorage` contract holds data needed for upgradeability, while the `OwnedUpgradeabilityStorage` 31 | provides the required state variables to track upgradeability ownership. 32 | 33 | The `OwnedUpgradeabilityProxy` combines proxy, upgradeability and ownable functionalities restricting version upgrade 34 | functions to be accessible just from the declared proxy owner. This contract will delegate calls to specific 35 | implementations of the ERC20 token behavior. These behaviors are the code that can be upgraded by the token developer 36 | (e.g. `Token_V0` and `Token_V1`). `OwnedUpgradeabilityProxy` extends from `UpgradeabilityProxy` (which in turn extends 37 | from `UpgradeabilityStorage` and `OwnedUpgradeabilityStorage`). Notice that `Token_V0` and `Token_V1` must respect 38 | this storage structure (given we are using `delegatecall`). 39 | -------------------------------------------------------------------------------- /upgradeability_ownership/test/Token_V0.js: -------------------------------------------------------------------------------- 1 | const Token_V0 = artifacts.require('Token_V0') 2 | const encodeCall = require('./helpers/encodeCall') 3 | const shouldBehaveLikeTokenV0 = require('./behaviors/token_v0') 4 | const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy') 5 | 6 | contract('Token_V0', function ([_, proxyOwner, tokenOwner, owner, recipient, anotherAccount]) { 7 | beforeEach(async function () { 8 | const impl_v0 = await Token_V0.new() 9 | const proxy = await OwnedUpgradeabilityProxy.new({ from: proxyOwner }) 10 | const initializeData = encodeCall('initialize', ['address'], [tokenOwner]) 11 | await proxy.upgradeToAndCall('0', impl_v0.address, initializeData, { from: proxyOwner }) 12 | 13 | this.token = await Token_V0.at(proxy.address) 14 | }) 15 | 16 | shouldBehaveLikeTokenV0(proxyOwner, tokenOwner, owner, recipient, anotherAccount) 17 | }) 18 | -------------------------------------------------------------------------------- /upgradeability_ownership/test/Token_V1.js: -------------------------------------------------------------------------------- 1 | const Token_V0 = artifacts.require('Token_V0') 2 | const Token_V1 = artifacts.require('Token_V1') 3 | const encodeCall = require('./helpers/encodeCall') 4 | const assertRevert = require('./helpers/assertRevert') 5 | const shouldBehaveLikeTokenV0 = require('./behaviors/token_v0') 6 | const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy') 7 | 8 | contract('Token_V1', ([_, proxyOwner, tokenOwner, owner, recipient, anotherAccount]) => { 9 | 10 | beforeEach(async function () { 11 | const impl_v0 = await Token_V0.new() 12 | const proxy = await OwnedUpgradeabilityProxy.new({ from: proxyOwner }) 13 | const initializeData = encodeCall('initialize', ['address'], [tokenOwner]) 14 | await proxy.upgradeToAndCall('0', impl_v0.address, initializeData, { from: proxyOwner }) 15 | 16 | const impl_v1 = await Token_V1.new() 17 | await proxy.upgradeTo('1', impl_v1.address, { from: proxyOwner }) 18 | 19 | this.token = await Token_V1.at(proxy.address) 20 | }) 21 | 22 | shouldBehaveLikeTokenV0(proxyOwner, tokenOwner, owner, recipient, anotherAccount) 23 | 24 | describe('burn', function () { 25 | const from = owner 26 | 27 | beforeEach(async function () { 28 | await this.token.mint(owner, 100, { from: tokenOwner }) 29 | }) 30 | 31 | describe('when the given amount is not greater than balance of the sender', function () { 32 | const amount = 100 33 | 34 | it('burns the requested amount', async function () { 35 | await this.token.burn(amount, { from }) 36 | 37 | const balance = await this.token.balanceOf(from) 38 | assert.equal(balance, 0) 39 | }) 40 | 41 | it('emits a burn event', async function () { 42 | const { logs } = await this.token.burn(amount, { from }) 43 | 44 | assert.equal(logs.length, 1) 45 | assert.equal(logs[0].event, 'Burn') 46 | assert.equal(logs[0].args.burner, owner) 47 | assert.equal(logs[0].args.value, amount) 48 | }) 49 | }) 50 | 51 | describe('when the given amount is greater than the balance of the sender', function () { 52 | const amount = 101 53 | 54 | it('reverts', async function () { 55 | await assertRevert(this.token.burn(amount, { from })) 56 | }) 57 | }) 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /upgradeability_ownership/test/helpers/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise) { 2 | try { 3 | await promise; 4 | assert.fail('Expected revert not received'); 5 | } catch (error) { 6 | const revertFound = error.message.search('revert') >= 0; 7 | assert(revertFound, `Expected "revert", got ${error} instead`); 8 | } 9 | } 10 | 11 | module.exports = assertRevert; -------------------------------------------------------------------------------- /upgradeability_ownership/test/helpers/encodeCall.js: -------------------------------------------------------------------------------- 1 | const abi = require('ethereumjs-abi') 2 | 3 | function encodeCall(name, arguments, values) { 4 | const methodId = abi.methodID(name, arguments).toString('hex'); 5 | const params = abi.rawEncode(arguments, values).toString('hex'); 6 | return '0x' + methodId + params; 7 | } 8 | 9 | module.exports = encodeCall; -------------------------------------------------------------------------------- /upgradeability_ownership/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/EternalStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title EternalStorage 5 | * @dev This contract holds all the necessary state variables to carry out the storage of any contract. 6 | */ 7 | contract EternalStorage { 8 | 9 | mapping(bytes32 => uint256) internal uintStorage; 10 | mapping(bytes32 => string) internal stringStorage; 11 | mapping(bytes32 => address) internal addressStorage; 12 | mapping(bytes32 => bytes) internal bytesStorage; 13 | mapping(bytes32 => bool) internal boolStorage; 14 | mapping(bytes32 => int256) internal intStorage; 15 | 16 | } 17 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/EternalStorageProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import "./EternalStorage.sol"; 4 | import "./OwnedUpgradeabilityProxy.sol"; 5 | 6 | 7 | /** 8 | * @title EternalStorageProxy 9 | * @dev This proxy holds the storage of the token contract and delegates every call to the current implementation set. 10 | * Besides, it allows to upgrade the token's behaviour towards further implementations, and provides basic 11 | * authorization control functionalities 12 | */ 13 | contract EternalStorageProxy is EternalStorage, OwnedUpgradeabilityProxy {} 14 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Gives the possibility to delegate any call to a foreign implementation. 6 | */ 7 | contract Proxy { 8 | 9 | /** 10 | * @dev Tells the address of the implementation where every call will be delegated. 11 | * @return address of the implementation to which it will be delegated 12 | */ 13 | function implementation() public view returns (address); 14 | 15 | /** 16 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 17 | * This function will return whatever the implementation call returns 18 | */ 19 | function () payable public { 20 | address _impl = implementation(); 21 | require(_impl != address(0)); 22 | 23 | assembly { 24 | let ptr := mload(0x40) 25 | calldatacopy(ptr, 0, calldatasize) 26 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 27 | let size := returndatasize 28 | returndatacopy(ptr, 0, size) 29 | 30 | switch result 31 | case 0 { revert(ptr, size) } 32 | default { return(ptr, size) } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/UpgradeabilityOwnerStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | 4 | /** 5 | * @title UpgradeabilityOwnerStorage 6 | * @dev This contract keeps track of the upgradeability owner 7 | */ 8 | contract UpgradeabilityOwnerStorage { 9 | // Owner of the contract 10 | address private _upgradeabilityOwner; 11 | 12 | /** 13 | * @dev Tells the address of the owner 14 | * @return the address of the owner 15 | */ 16 | function upgradeabilityOwner() public view returns (address) { 17 | return _upgradeabilityOwner; 18 | } 19 | 20 | /** 21 | * @dev Sets the address of the owner 22 | */ 23 | function setUpgradeabilityOwner(address newUpgradeabilityOwner) internal { 24 | _upgradeabilityOwner = newUpgradeabilityOwner; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './Proxy.sol'; 4 | import './UpgradeabilityStorage.sol'; 5 | 6 | /** 7 | * @title UpgradeabilityProxy 8 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 9 | */ 10 | contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage { 11 | /** 12 | * @dev This event will be emitted every time the implementation gets upgraded 13 | * @param version representing the version name of the upgraded implementation 14 | * @param implementation representing the address of the upgraded implementation 15 | */ 16 | event Upgraded(string version, address indexed implementation); 17 | 18 | /** 19 | * @dev Upgrades the implementation address 20 | * @param version representing the version name of the new implementation to be set 21 | * @param implementation representing the address of the new implementation to be set 22 | */ 23 | function _upgradeTo(string version, address implementation) internal { 24 | require(_implementation != implementation); 25 | _version = version; 26 | _implementation = implementation; 27 | Upgraded(version, implementation); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/UpgradeabilityStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title UpgradeabilityStorage 5 | * @dev This contract holds all the necessary state variables to support the upgrade functionality 6 | */ 7 | contract UpgradeabilityStorage { 8 | // Version name of the current implementation 9 | string internal _version; 10 | 11 | // Address of the current implementation 12 | address internal _implementation; 13 | 14 | /** 15 | * @dev Tells the version name of the current implementation 16 | * @return string representing the name of the current version 17 | */ 18 | function version() public view returns (string) { 19 | return _version; 20 | } 21 | 22 | /** 23 | * @dev Tells the address of the current implementation 24 | * @return address of the current implementation 25 | */ 26 | function implementation() public view returns (address) { 27 | return _implementation; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/test/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import '../EternalStorage.sol'; 4 | 5 | /** 6 | * @title Ownable 7 | * @dev This contract has an owner address providing basic authorization control 8 | */ 9 | contract Ownable is EternalStorage { 10 | /** 11 | * @dev Event to show ownership has been transferred 12 | * @param previousOwner representing the address of the previous owner 13 | * @param newOwner representing the address of the new owner 14 | */ 15 | event OwnershipTransferred(address previousOwner, address newOwner); 16 | 17 | /** 18 | * @dev Throws if called by any account other than the owner. 19 | */ 20 | modifier onlyOwner() { 21 | require(msg.sender == owner()); 22 | _; 23 | } 24 | 25 | /** 26 | * @dev Tells the address of the owner 27 | * @return the address of the owner 28 | */ 29 | function owner() public view returns (address) { 30 | return addressStorage[keccak256("owner")]; 31 | } 32 | 33 | /** 34 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 35 | * @param newOwner the address to transfer ownership to. 36 | */ 37 | function transferOwnership(address newOwner) public onlyOwner { 38 | require(newOwner != address(0)); 39 | setOwner(newOwner); 40 | } 41 | 42 | /** 43 | * @dev Sets a new owner address 44 | */ 45 | function setOwner(address newOwner) internal { 46 | OwnershipTransferred(owner(), newOwner); 47 | addressStorage[keccak256("owner")] = newOwner; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/test/Token_V1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './Token_V0.sol'; 4 | import './Ownable.sol'; 5 | 6 | /** 7 | * @title Token_V1 8 | * @dev Version 1 of a token to show upgradeability. 9 | * The idea here is to extend a token behaviour providing mintable token functionalities 10 | * in addition to what's provided in version 0 11 | */ 12 | contract Token_V1 is Token_V0, Ownable { 13 | event Mint(address indexed to, uint256 amount); 14 | event MintFinished(); 15 | 16 | modifier canMint() { 17 | require(!mintingFinished()); 18 | _; 19 | } 20 | 21 | function initialize(address owner) public { 22 | require(!initialized()); 23 | setOwner(owner); 24 | boolStorage[keccak256('token_v1_initialized')] = true; 25 | } 26 | 27 | function initialized() public view returns (bool) { 28 | return boolStorage[keccak256('token_v1_initialized')]; 29 | } 30 | 31 | function mintingFinished() public view returns (bool) { 32 | return boolStorage[keccak256('mintingFinished')]; 33 | } 34 | 35 | function mint(address to, uint256 value) public onlyOwner canMint { 36 | super.mint(to, value); 37 | Mint(to, value); 38 | } 39 | 40 | function finishMinting() public onlyOwner canMint { 41 | boolStorage[keccak256('mintingFinished')] = true; 42 | MintFinished(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/test/Token_V2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './Token_V1.sol'; 4 | 5 | /** 6 | * @title Token_V2 7 | * @dev Version 2 of a token to show upgradeability. 8 | * The idea here is to extend a token behaviour providing burnable functionalities 9 | * in addition to what's provided in version 1 10 | */ 11 | contract Token_V2 is Token_V1 { 12 | event Burn(address indexed burner, uint256 value); 13 | 14 | function burn(uint256 value) public { 15 | require(balanceOf(msg.sender) >= value); 16 | bytes32 totalSupplyHash = keccak256("totalSupply"); 17 | bytes32 balanceSenderHash = keccak256("balance", msg.sender); 18 | uintStorage[totalSupplyHash] = totalSupply().sub(value); 19 | uintStorage[balanceSenderHash] = balanceOf(msg.sender).sub(value); 20 | Burn(msg.sender, value); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/contracts/test/Token_V3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './Token_V2.sol'; 4 | 5 | /** 6 | * @title Token_V2 7 | * @dev Version 2 of a token to show upgradeability. 8 | * The idea here is to extend a token behaviour providing pausable functionalities 9 | * in addition to what's provided in version 2 10 | */ 11 | contract Token_V3 is Token_V2 { 12 | event Pause(); 13 | event Unpause(); 14 | 15 | /** 16 | * @dev Modifier to make a function callable only when the contract is not paused. 17 | */ 18 | modifier whenNotPaused() { 19 | require(!paused()); 20 | _; 21 | } 22 | 23 | /** 24 | * @dev Modifier to make a function callable only when the contract is paused. 25 | */ 26 | modifier whenPaused() { 27 | require(paused()); 28 | _; 29 | } 30 | 31 | /** 32 | * @dev tells whether the token is paused or not 33 | */ 34 | function paused() public view returns (bool) { 35 | return boolStorage['paused']; 36 | } 37 | 38 | /** 39 | * @dev called by the owner to pause the token, triggers stopped state 40 | */ 41 | function pause() public onlyOwner whenNotPaused { 42 | boolStorage['paused'] = true; 43 | Pause(); 44 | } 45 | 46 | /** 47 | * @dev called by the owner to unpause the tokn, returns to normal state 48 | */ 49 | function unpause() public onlyOwner whenPaused { 50 | boolStorage['paused'] = false; 51 | Unpause(); 52 | } 53 | 54 | /** 55 | * @dev transfer function with not-paused guard 56 | */ 57 | function transfer(address to, uint256 value) public whenNotPaused { 58 | return super.transfer(to, value); 59 | } 60 | 61 | /** 62 | * @dev transferFrom function with not-paused guard 63 | */ 64 | function transferFrom(address from, address to, uint256 value) public whenNotPaused { 65 | return super.transferFrom(from, to, value); 66 | } 67 | 68 | /** 69 | * @dev approve function with not-paused guard 70 | */ 71 | function approve(address spender, uint256 value) public whenNotPaused { 72 | return super.approve(spender, value); 73 | } 74 | 75 | /** 76 | * @dev increaseApproval function with not-paused guard 77 | */ 78 | function increaseApproval(address spender, uint256 addedValue) public whenNotPaused { 79 | return super.increaseApproval(spender, addedValue); 80 | } 81 | 82 | /** 83 | * @dev decreaseApproval function with not-paused guard 84 | */ 85 | function decreaseApproval(address spender, uint256 subtractedValue) public whenNotPaused { 86 | return super.decreaseApproval(spender, subtractedValue); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upgradeability-using-eternal-storage", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Facu Spagnuolo ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "ethereumjs-abi": "^0.6.5", 12 | "truffle": "^4.0.4", 13 | "zeppelin-solidity": "^1.5.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/test/OwnedUpgradeabilityProxy.js: -------------------------------------------------------------------------------- 1 | const assertRevert = require('./helpers/assertRevert') 2 | const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy') 3 | 4 | contract('OwnedUpgradeabilityProxy', ([owner, anotherAccount]) => { 5 | let ownedUpgradeabilityToken 6 | 7 | beforeEach(async function () { 8 | ownedUpgradeabilityToken = await OwnedUpgradeabilityProxy.new({ from: owner }) 9 | }) 10 | 11 | describe('owner', function () { 12 | it('has an owner', async function () { 13 | const proxyOwner = await ownedUpgradeabilityToken.proxyOwner() 14 | 15 | assert.equal(proxyOwner, owner) 16 | }) 17 | }) 18 | 19 | describe('transfer ownership', function () { 20 | describe('when the new proposed owner is not the zero address', function () { 21 | const newOwner = anotherAccount 22 | 23 | describe('when the sender is the owner', function () { 24 | const from = owner 25 | 26 | it('transfers the ownership', async function () { 27 | await ownedUpgradeabilityToken.transferProxyOwnership(newOwner, { from }) 28 | 29 | const proxyOwner = await ownedUpgradeabilityToken.proxyOwner() 30 | assert.equal(proxyOwner, anotherAccount) 31 | }) 32 | 33 | it('emits an event', async function () { 34 | const { logs } = await ownedUpgradeabilityToken.transferProxyOwnership(newOwner, { from }) 35 | 36 | assert.equal(logs.length, 1) 37 | assert.equal(logs[0].event, 'ProxyOwnershipTransferred') 38 | assert.equal(logs[0].args.previousOwner, owner) 39 | assert.equal(logs[0].args.newOwner, newOwner) 40 | }) 41 | }) 42 | 43 | describe('when the sender is not the owner', function () { 44 | const from = anotherAccount 45 | 46 | it('reverts', async function () { 47 | await assertRevert(ownedUpgradeabilityToken.transferProxyOwnership(newOwner, { from })) 48 | }) 49 | }) 50 | }) 51 | 52 | describe('when the new proposed owner is the zero address', function () { 53 | const newOwner = 0x0 54 | 55 | it('reverts', async function () { 56 | await assertRevert(ownedUpgradeabilityToken.transferProxyOwnership(newOwner, { from: owner })) 57 | }) 58 | }) 59 | }) 60 | }) 61 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/test/Token_V0.js: -------------------------------------------------------------------------------- 1 | const Token_V0 = artifacts.require('Token_V0') 2 | const EternalStorageProxy = artifacts.require('EternalStorageProxy') 3 | const shouldBehaveLikeTokenV0 = require('./behaviors/token_v0') 4 | 5 | contract('Token_V0', function ([_, proxyOwner, owner, recipient, anotherAccount]) { 6 | beforeEach(async function () { 7 | const proxy = await EternalStorageProxy.new({ from: proxyOwner }) 8 | const impl_v0 = await Token_V0.new() 9 | await proxy.upgradeTo('0', impl_v0.address, { from: proxyOwner }) 10 | this.token = await Token_V0.at(proxy.address) 11 | }) 12 | 13 | shouldBehaveLikeTokenV0(owner, owner, recipient, anotherAccount) 14 | }) 15 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/test/Token_V1.js: -------------------------------------------------------------------------------- 1 | const abi = require('ethereumjs-abi'); 2 | const Token_V0 = artifacts.require('Token_V0') 3 | const Token_V1 = artifacts.require('Token_V1') 4 | const EternalStorageProxy = artifacts.require('EternalStorageProxy') 5 | const shouldBehaveLikeTokenV0 = require('./behaviors/token_v0') 6 | const shouldBehaveLikeTokenV1 = require('./behaviors/token_v1') 7 | 8 | contract('Token_V1', ([_, proxyOwner, tokenOwner, owner, recipient, anotherAccount]) => { 9 | 10 | beforeEach(async function () { 11 | const proxy = await EternalStorageProxy.new({ from: proxyOwner }) 12 | 13 | const impl_v0 = await Token_V0.new() 14 | await proxy.upgradeTo('0', impl_v0.address, { from: proxyOwner }) 15 | 16 | const impl_v1 = await Token_V1.new() 17 | const methodId = abi.methodID('initialize', ['address']).toString('hex'); 18 | const params = abi.rawEncode(['address'], [tokenOwner]).toString('hex'); 19 | const initializeData = '0x' + methodId + params; 20 | await proxy.upgradeToAndCall('1', impl_v1.address, initializeData, { from: proxyOwner }) 21 | 22 | this.token = await Token_V1.at(proxy.address) 23 | }) 24 | 25 | shouldBehaveLikeTokenV0(tokenOwner, owner, recipient, anotherAccount) 26 | 27 | shouldBehaveLikeTokenV1(proxyOwner, tokenOwner, owner, anotherAccount) 28 | }) 29 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/test/Token_V2.js: -------------------------------------------------------------------------------- 1 | const abi = require('ethereumjs-abi'); 2 | const Token_V0 = artifacts.require('Token_V0') 3 | const Token_V1 = artifacts.require('Token_V1') 4 | const Token_V2 = artifacts.require('Token_V2') 5 | const EternalStorageProxy = artifacts.require('EternalStorageProxy') 6 | const shouldBehaveLikeTokenV0 = require('./behaviors/token_v0') 7 | const shouldBehaveLikeTokenV1 = require('./behaviors/token_v1') 8 | const shouldBehaveLikeTokenV2 = require('./behaviors/token_v2') 9 | 10 | contract('Token_V2', ([_, proxyOwner, tokenOwner, owner, recipient, anotherAccount]) => { 11 | 12 | beforeEach(async function () { 13 | const proxy = await EternalStorageProxy.new({ from: proxyOwner }) 14 | 15 | const impl_v0 = await Token_V0.new() 16 | await proxy.upgradeTo('0', impl_v0.address, { from: proxyOwner }) 17 | 18 | const impl_v1 = await Token_V1.new() 19 | const methodId = abi.methodID('initialize', ['address']).toString('hex'); 20 | const params = abi.rawEncode(['address'], [tokenOwner]).toString('hex'); 21 | const initializeData = '0x' + methodId + params; 22 | await proxy.upgradeToAndCall('1', impl_v1.address, initializeData, { from: proxyOwner }) 23 | 24 | const impl_v2 = await Token_V2.new() 25 | await proxy.upgradeTo('2', impl_v2.address, { from: proxyOwner }) 26 | 27 | this.token = await Token_V2.at(proxy.address) 28 | }) 29 | 30 | shouldBehaveLikeTokenV0(tokenOwner, owner, recipient, anotherAccount) 31 | 32 | shouldBehaveLikeTokenV1(proxyOwner, tokenOwner, owner, anotherAccount) 33 | 34 | shouldBehaveLikeTokenV2(tokenOwner, owner) 35 | }) 36 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/test/behaviors/token_v2.js: -------------------------------------------------------------------------------- 1 | function shouldBehaveLikeTokenV2(tokenOwner, owner) { 2 | const assertRevert = require('../helpers/assertRevert') 3 | 4 | describe('burn', function () { 5 | const from = owner 6 | 7 | beforeEach(async function () { 8 | await this.token.mint(owner, 100, { from: tokenOwner }) 9 | }) 10 | 11 | describe('when the given amount is not greater than balance of the sender', function () { 12 | const amount = 100 13 | 14 | it('burns the requested amount', async function () { 15 | await this.token.burn(amount, { from }) 16 | 17 | const balance = await this.token.balanceOf(from) 18 | assert.equal(balance, 0) 19 | }) 20 | 21 | it('emits a burn event', async function () { 22 | const { logs } = await this.token.burn(amount, { from }) 23 | 24 | assert.equal(logs.length, 1) 25 | assert.equal(logs[0].event, 'Burn') 26 | assert.equal(logs[0].args.burner, owner) 27 | assert.equal(logs[0].args.value, amount) 28 | }) 29 | }) 30 | 31 | describe('when the given amount is greater than the balance of the sender', function () { 32 | const amount = 101 33 | 34 | it('reverts', async function () { 35 | await assertRevert(this.token.burn(amount, { from })) 36 | }) 37 | }) 38 | }) 39 | } 40 | 41 | module.exports = shouldBehaveLikeTokenV2 42 | -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/test/helpers/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise) { 2 | try { 3 | await promise; 4 | assert.fail('Expected revert not received'); 5 | } catch (error) { 6 | const revertFound = error.message.search('revert') >= 0; 7 | assert(revertFound, `Expected "revert", got ${error} instead`); 8 | } 9 | } 10 | 11 | module.exports = assertRevert; -------------------------------------------------------------------------------- /upgradeability_using_eternal_storage/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/contracts/IRegistry.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title IRegistry 5 | * @dev This contract represents the interface of a registry contract 6 | */ 7 | interface IRegistry { 8 | /** 9 | * @dev This event will be emitted every time a new proxy is created 10 | * @param proxy representing the address of the proxy created 11 | */ 12 | event ProxyCreated(address proxy); 13 | 14 | /** 15 | * @dev This event will be emitted every time a new implementation is registered 16 | * @param version representing the version name of the registered implementation 17 | * @param implementation representing the address of the registered implementation 18 | */ 19 | event VersionAdded(string version, address implementation); 20 | 21 | /** 22 | * @dev Registers a new version with its implementation address 23 | * @param version representing the version name of the new implementation to be registered 24 | * @param implementation representing the address of the new implementation to be registered 25 | */ 26 | function addVersion(string version, address implementation) public; 27 | 28 | /** 29 | * @dev Tells the address of the implementation for a given version 30 | * @param version to query the implementation of 31 | * @return address of the implementation registered for the given version 32 | */ 33 | function getVersion(string version) public view returns (address); 34 | } 35 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Gives the possibility to delegate any call to a foreign implementation. 6 | */ 7 | contract Proxy { 8 | 9 | /** 10 | * @dev Tells the address of the implementation where every call will be delegated. 11 | * @return address of the implementation to which it will be delegated 12 | */ 13 | function implementation() public view returns (address); 14 | 15 | /** 16 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 17 | * This function will return whatever the implementation call returns 18 | */ 19 | function () payable public { 20 | address _impl = implementation(); 21 | require(_impl != address(0)); 22 | 23 | assembly { 24 | let ptr := mload(0x40) 25 | calldatacopy(ptr, 0, calldatasize) 26 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 27 | let size := returndatasize 28 | returndatacopy(ptr, 0, size) 29 | 30 | switch result 31 | case 0 { revert(ptr, size) } 32 | default { return(ptr, size) } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/contracts/Registry.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './IRegistry.sol'; 4 | import './Upgradeable.sol'; 5 | import './UpgradeabilityProxy.sol'; 6 | 7 | /** 8 | * @title Registry 9 | * @dev This contract works as a registry of versions, it holds the implementations for the registered versions. 10 | */ 11 | contract Registry is IRegistry { 12 | // Mapping of versions to implementations of different functions 13 | mapping (string => address) internal versions; 14 | 15 | /** 16 | * @dev Registers a new version with its implementation address 17 | * @param version representing the version name of the new implementation to be registered 18 | * @param implementation representing the address of the new implementation to be registered 19 | */ 20 | function addVersion(string version, address implementation) public { 21 | require(versions[version] == 0x0); 22 | versions[version] = implementation; 23 | VersionAdded(version, implementation); 24 | } 25 | 26 | /** 27 | * @dev Tells the address of the implementation for a given version 28 | * @param version to query the implementation of 29 | * @return address of the implementation registered for the given version 30 | */ 31 | function getVersion(string version) public view returns (address) { 32 | return versions[version]; 33 | } 34 | 35 | /** 36 | * @dev Creates an upgradeable proxy 37 | * @param version representing the first version to be set for the proxy 38 | * @return address of the new proxy created 39 | */ 40 | function createProxy(string version) public payable returns (UpgradeabilityProxy) { 41 | UpgradeabilityProxy proxy = new UpgradeabilityProxy(version); 42 | Upgradeable(proxy).initialize.value(msg.value)(msg.sender); 43 | ProxyCreated(proxy); 44 | return proxy; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/contracts/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './Proxy.sol'; 4 | import './IRegistry.sol'; 5 | import './UpgradeabilityStorage.sol'; 6 | 7 | /** 8 | * @title UpgradeabilityProxy 9 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 10 | */ 11 | contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage { 12 | 13 | /** 14 | * @dev Constructor function 15 | */ 16 | function UpgradeabilityProxy(string _version) public { 17 | registry = IRegistry(msg.sender); 18 | upgradeTo(_version); 19 | } 20 | 21 | /** 22 | * @dev Upgrades the implementation to the requested version 23 | * @param _version representing the version name of the new implementation to be set 24 | */ 25 | function upgradeTo(string _version) public { 26 | _implementation = registry.getVersion(_version); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/contracts/UpgradeabilityStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './IRegistry.sol'; 4 | 5 | /** 6 | * @title UpgradeabilityStorage 7 | * @dev This contract holds all the necessary state variables to support the upgrade functionality 8 | */ 9 | contract UpgradeabilityStorage { 10 | // Versions registry 11 | IRegistry internal registry; 12 | 13 | // Address of the current implementation 14 | address internal _implementation; 15 | 16 | /** 17 | * @dev Tells the address of the current implementation 18 | * @return address of the current implementation 19 | */ 20 | function implementation() public view returns (address) { 21 | return _implementation; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/contracts/Upgradeable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './UpgradeabilityStorage.sol'; 4 | 5 | /** 6 | * @title Upgradeable 7 | * @dev This contract holds all the minimum required functionality for a behavior to be upgradeable. 8 | * This means, required state variables for owned upgradeability purpose and simple initialization validation. 9 | */ 10 | contract Upgradeable is UpgradeabilityStorage { 11 | /** 12 | * @dev Validates the caller is the versions registry. 13 | * THIS FUNCTION SHOULD BE OVERRIDDEN CALLING SUPER 14 | * @param sender representing the address deploying the initial behavior of the contract 15 | */ 16 | function initialize(address sender) public payable { 17 | require(msg.sender == address(registry)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/contracts/test/Token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import '../Upgradeable.sol'; 4 | 5 | contract TokenV1_0 is Upgradeable { 6 | mapping (address => uint) balances; 7 | 8 | event Transfer(address indexed from, address indexed to, uint256 _value); 9 | 10 | function initialize(address sender) public payable { 11 | super.initialize(sender); 12 | mint(sender, 10000); 13 | } 14 | 15 | function balanceOf(address addr) public view returns (uint) { 16 | return balances[addr]; 17 | } 18 | 19 | function transfer(address to, uint256 value) public { 20 | require(balances[msg.sender] >= value); 21 | balances[msg.sender] -= value; 22 | balances[to] += value; 23 | Transfer(msg.sender, to, value); 24 | } 25 | 26 | function mint(address to, uint256 value) public { 27 | balances[to] += value; 28 | Transfer(0x0, to, value); 29 | } 30 | 31 | } 32 | 33 | contract TokenV1_1 is TokenV1_0 { 34 | mapping (address => mapping (address => uint)) allowances; 35 | 36 | function transferFrom(address from, address to, uint256 value) public { 37 | require(allowances[from][msg.sender] >= value); 38 | allowances[from][msg.sender] -= value; 39 | balances[from] -= value; 40 | balances[to] += value; 41 | Transfer(from, to, value); 42 | } 43 | 44 | function approve(address spender, uint256 value) public { 45 | allowances[msg.sender][spender] = value; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upgradeability-using-inherited-storage", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Francisco Giordano ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "truffle": "^4.0.4" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/readme.md: -------------------------------------------------------------------------------- 1 | # Upgradeability using Inherited Storage 2 | 3 | The idea of this approach is to allow us to upgrade a contract's behavior, assuming that each version will follow the 4 | storage structure of the previous one. 5 | 6 | The approach consists in having a proxy that delegates calls to specific implementations which can be upgraded, without 7 | changing the storage structure of the previous implementations, but having the chance to add new state variables. Given 8 | the proxy uses `delegatecall` to resolve the requested behaviors, the upgradeable contract's state will be stored in 9 | the proxy contract itself. 10 | 11 | Since we have two really different kinds of data, one related to the upgradeability mechanism and another 12 | strictly related to the token contract domain, naming was really important here to expressed correctly what's 13 | going on. This is the proposed model: 14 | 15 | ------- ========================= 16 | | Proxy | ║ UpgradeabilityStorage ║ 17 | ------- ========================= 18 | ↑ ↑ ↑ 19 | --------------------- ------------- 20 | | UpgradeabilityProxy | | Upgradeable | 21 | --------------------- ------------- 22 | ↑ ↑ 23 | ---------- ---------- 24 | | Token_V0 | ← | Token_V1 | 25 | ---------- ---------- 26 | 27 | 28 | `Proxy`, `UpgradeabilityProxy` and `UpgradeabilityStorage` are generic contracts that can be used to implement 29 | upgradeability through proxies. In this example we use all these contracts to implement an upgradeable ERC20 token. 30 | 31 | `UpgradeabilityProxy` is the contract that will delegate calls to specific implementations of the ERC20 token behavior. 32 | These behaviors are the code that can be upgraded by the token developer (e.g. `Token_V0` and `Token_V1`). 33 | 34 | The `UpgradeabilityStorage` contract holds data needed for upgradeability, which will be inherited from each token 35 | behavior though `Upgradeable`. Then, each token behavior defines all the necessary state variables to 36 | carry out their storage. Notice that `Token_V1` inherits the same storage structure defined in `Token_V0`. 37 | This is a requirement of the proposed approach to ensure the proxy storage is not messed up. 38 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/test/Upgradeable.js: -------------------------------------------------------------------------------- 1 | const TokenV1_0 = artifacts.require('TokenV1_0') 2 | const TokenV1_1 = artifacts.require('TokenV1_1') 3 | 4 | const Registry = artifacts.require('Registry') 5 | const Proxy = artifacts.require('UpgradeabilityProxy') 6 | 7 | contract('Upgradeable', function ([sender, receiver]) { 8 | 9 | it('should work', async function () { 10 | const impl_v1_0 = await TokenV1_0.new() 11 | const impl_v1_1 = await TokenV1_1.new() 12 | 13 | const registry = await Registry.new() 14 | await registry.addVersion("1.0", impl_v1_0.address) 15 | await registry.addVersion("1.1", impl_v1_1.address) 16 | 17 | const {logs} = await registry.createProxy("1.0") 18 | 19 | const {proxy} = logs.find(l => l.event === 'ProxyCreated').args 20 | 21 | await TokenV1_0.at(proxy).mint(sender, 100) 22 | 23 | await Proxy.at(proxy).upgradeTo("1.1") 24 | 25 | await TokenV1_1.at(proxy).mint(sender, 100) 26 | 27 | const transferTx = await TokenV1_1.at(proxy).transfer(receiver, 10, { from: sender }) 28 | 29 | console.log("Transfer TX gas cost using Inherited Storage Proxy", transferTx.receipt.gasUsed); 30 | 31 | const balance = await TokenV1_1.at(proxy).balanceOf(sender) 32 | assert(balance.eq(10190)) 33 | 34 | }) 35 | 36 | }) 37 | -------------------------------------------------------------------------------- /upgradeability_using_inherited_storage/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Gives the possibility to delegate any call to a foreign implementation. 6 | */ 7 | contract Proxy { 8 | /** 9 | * @dev Tells the address of the implementation where every call will be delegated. 10 | * @return address of the implementation to which it will be delegated 11 | */ 12 | function implementation() public view returns (address); 13 | 14 | /** 15 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 16 | * This function will return whatever the implementation call returns 17 | */ 18 | function () payable public { 19 | address _impl = implementation(); 20 | require(_impl != address(0)); 21 | 22 | assembly { 23 | let ptr := mload(0x40) 24 | calldatacopy(ptr, 0, calldatasize) 25 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 26 | let size := returndatasize 27 | returndatacopy(ptr, 0, size) 28 | 29 | switch result 30 | case 0 { revert(ptr, size) } 31 | default { return(ptr, size) } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/contracts/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import './Proxy.sol'; 4 | 5 | /** 6 | * @title UpgradeabilityProxy 7 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 8 | */ 9 | contract UpgradeabilityProxy is Proxy { 10 | /** 11 | * @dev This event will be emitted every time the implementation gets upgraded 12 | * @param implementation representing the address of the upgraded implementation 13 | */ 14 | event Upgraded(address indexed implementation); 15 | 16 | // Storage position of the address of the current implementation 17 | bytes32 private constant implementationPosition = keccak256("org.zeppelinos.proxy.implementation"); 18 | 19 | /** 20 | * @dev Constructor function 21 | */ 22 | function UpgradeabilityProxy() public {} 23 | 24 | /** 25 | * @dev Tells the address of the current implementation 26 | * @return address of the current implementation 27 | */ 28 | function implementation() public view returns (address impl) { 29 | bytes32 position = implementationPosition; 30 | assembly { 31 | impl := sload(position) 32 | } 33 | } 34 | 35 | /** 36 | * @dev Sets the address of the current implementation 37 | * @param newImplementation address representing the new implementation to be set 38 | */ 39 | function setImplementation(address newImplementation) internal { 40 | bytes32 position = implementationPosition; 41 | assembly { 42 | sstore(position, newImplementation) 43 | } 44 | } 45 | 46 | /** 47 | * @dev Upgrades the implementation address 48 | * @param newImplementation representing the address of the new implementation to be set 49 | */ 50 | function _upgradeTo(address newImplementation) internal { 51 | address currentImplementation = implementation(); 52 | require(currentImplementation != newImplementation); 53 | setImplementation(newImplementation); 54 | emit Upgraded(newImplementation); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/contracts/test/Ownable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | 4 | /** 5 | * @title Ownable 6 | * @dev This contract has the owner address providing basic authorization control 7 | */ 8 | contract Ownable { 9 | /** 10 | * @dev Event to show ownership has been transferred 11 | * @param previousOwner representing the address of the previous owner 12 | * @param newOwner representing the address of the new owner 13 | */ 14 | event OwnershipTransferred(address previousOwner, address newOwner); 15 | 16 | // Owner of the contract 17 | address private _owner; 18 | 19 | /** 20 | * @dev Throws if called by any account other than the owner. 21 | */ 22 | modifier onlyOwner() { 23 | require(msg.sender == owner()); 24 | _; 25 | } 26 | 27 | /** 28 | * @dev The constructor sets the original owner of the contract to the sender account. 29 | */ 30 | function Ownable() public { 31 | setOwner(msg.sender); 32 | } 33 | 34 | /** 35 | * @dev Tells the address of the owner 36 | * @return the address of the owner 37 | */ 38 | function owner() public view returns (address) { 39 | return _owner; 40 | } 41 | 42 | /** 43 | * @dev Sets a new owner address 44 | */ 45 | function setOwner(address newOwner) internal { 46 | _owner = newOwner; 47 | } 48 | 49 | /** 50 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 51 | * @param newOwner The address to transfer ownership to. 52 | */ 53 | function transferOwnership(address newOwner) public onlyOwner { 54 | require(newOwner != address(0)); 55 | emit OwnershipTransferred(owner(), newOwner); 56 | setOwner(newOwner); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/contracts/test/Token_V1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.21; 2 | 3 | import './Token_V0.sol'; 4 | 5 | /** 6 | * @title Token_V1 7 | * @dev Version 1 of a token to show upgradeability. 8 | * The idea here is to extend a token behaviour providing burnable functionalities 9 | * in addition to what's provided in version 0 10 | */ 11 | contract Token_V1 is Token_V0 { 12 | event Burn(address indexed burner, uint256 value); 13 | 14 | function burn(uint256 value) public { 15 | require(balanceOf(msg.sender) >= value); 16 | _balances[msg.sender] = _balances[msg.sender].sub(value); 17 | _totalSupply = _totalSupply.sub(value); 18 | emit Burn(msg.sender, value); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upgraeability-using-unstructured-storage", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Facundo Spagnuolo ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "ethereumjs-abi": "^0.6.5", 12 | "truffle": "^4.0.4", 13 | "zeppelin-solidity": "^1.5.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/readme.md: -------------------------------------------------------------------------------- 1 | # Upgradeability using unstructured storage 2 | 3 | This idea builds on _upgradeability using inherited storage_ but redefining the storage structure of the contracts 4 | required for upgradeability purpose. The idea here is to use fixed storage slots to store the required data for 5 | upgradeability purpose, this is the upgradeability owner and the implementation address. 6 | 7 | We are using inline assembly to store and access mentioned variables in fixed storage positions indexing them 8 | with custom keys using `keccak256`. Please take a look at the implementation provided in `UpgradeabilityProxy` 9 | and `OwnedUpgradeabilityProxy`. 10 | 11 | This is the proposed model: 12 | 13 | ------- ---------- ---------- 14 | | Proxy | | Token_V0 | ← | Token_V1 | 15 | ------- ---------- ---------- 16 | ↑ 17 | --------------------- 18 | | UpgradeabilityProxy | 19 | --------------------- 20 | ↑ 21 | -------------------------- 22 | | OwnedUpgradeabilityProxy | 23 | -------------------------- 24 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/test/Storage.js: -------------------------------------------------------------------------------- 1 | 2 | const encodeCall = require('./helpers/encodeCall') 3 | const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy') 4 | const Token_V0 = artifacts.require('Token_V0') 5 | 6 | contract('OwnedUpgradeabilityProxy', ([_, proxyOwner, tokenOwner, anotherAccount]) => { 7 | let proxy 8 | let impl_v0 9 | let token_v0 10 | const initializeData = encodeCall('initialize', ['address'], [tokenOwner]); 11 | 12 | beforeEach(async function () { 13 | proxy = await OwnedUpgradeabilityProxy.new({ from: proxyOwner }) 14 | impl_v0 = await Token_V0.new() 15 | token_v0 = Token_V0.at(proxy.address) 16 | const initializeData = encodeCall('initialize', ['address'], [tokenOwner]) 17 | await proxy.upgradeToAndCall(impl_v0.address, initializeData, { from: proxyOwner }) 18 | }); 19 | 20 | it('should store implementation in specified location', async function () { 21 | const position = web3.sha3("org.zeppelinos.proxy.implementation"); 22 | const storage = await web3.eth.getStorageAt(proxy.address, position); 23 | assert.equal(storage, impl_v0.address); 24 | }); 25 | 26 | }); -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/test/Token_V0.js: -------------------------------------------------------------------------------- 1 | const Token_V0 = artifacts.require('Token_V0') 2 | const encodeCall = require('./helpers/encodeCall') 3 | const shouldBehaveLikeTokenV0 = require('./behaviors/token_v0') 4 | const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy') 5 | 6 | contract('Token_V0', function ([_, proxyOwner, tokenOwner, owner, recipient, anotherAccount]) { 7 | beforeEach(async function () { 8 | const impl_v0 = await Token_V0.new() 9 | const proxy = await OwnedUpgradeabilityProxy.new({ from: proxyOwner }) 10 | const initializeData = encodeCall('initialize', ['address'], [tokenOwner]) 11 | await proxy.upgradeToAndCall(impl_v0.address, initializeData, { from: proxyOwner }) 12 | 13 | this.token = await Token_V0.at(proxy.address) 14 | }) 15 | 16 | shouldBehaveLikeTokenV0(proxyOwner, tokenOwner, owner, recipient, anotherAccount) 17 | }) 18 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/test/Token_V1.js: -------------------------------------------------------------------------------- 1 | const Token_V0 = artifacts.require('Token_V0') 2 | const Token_V1 = artifacts.require('Token_V1') 3 | const encodeCall = require('./helpers/encodeCall') 4 | const assertRevert = require('./helpers/assertRevert') 5 | const shouldBehaveLikeTokenV0 = require('./behaviors/token_v0') 6 | const OwnedUpgradeabilityProxy = artifacts.require('OwnedUpgradeabilityProxy') 7 | 8 | contract('Token_V1', ([_, proxyOwner, tokenOwner, owner, recipient, anotherAccount]) => { 9 | 10 | beforeEach(async function () { 11 | const impl_v0 = await Token_V0.new() 12 | const proxy = await OwnedUpgradeabilityProxy.new({ from: proxyOwner }) 13 | const initializeData = encodeCall('initialize', ['address'], [tokenOwner]) 14 | await proxy.upgradeToAndCall(impl_v0.address, initializeData, { from: proxyOwner }) 15 | 16 | const impl_v1 = await Token_V1.new() 17 | await proxy.upgradeTo(impl_v1.address, { from: proxyOwner }) 18 | 19 | this.token = await Token_V1.at(proxy.address) 20 | }) 21 | 22 | shouldBehaveLikeTokenV0(proxyOwner, tokenOwner, owner, recipient, anotherAccount) 23 | 24 | describe('burn', function () { 25 | const from = owner 26 | 27 | beforeEach(async function () { 28 | await this.token.mint(owner, 100, { from: tokenOwner }) 29 | }) 30 | 31 | describe('when the given amount is not greater than balance of the sender', function () { 32 | const amount = 100 33 | 34 | it('burns the requested amount', async function () { 35 | await this.token.burn(amount, { from }) 36 | 37 | const balance = await this.token.balanceOf(from) 38 | assert.equal(balance, 0) 39 | }) 40 | 41 | it('emits a burn event', async function () { 42 | const { logs } = await this.token.burn(amount, { from }) 43 | 44 | assert.equal(logs.length, 1) 45 | assert.equal(logs[0].event, 'Burn') 46 | assert.equal(logs[0].args.burner, owner) 47 | assert.equal(logs[0].args.value, amount) 48 | }) 49 | }) 50 | 51 | describe('when the given amount is greater than the balance of the sender', function () { 52 | const amount = 101 53 | 54 | it('reverts', async function () { 55 | await assertRevert(this.token.burn(amount, { from })) 56 | }) 57 | }) 58 | }) 59 | }) 60 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/test/helpers/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise) { 2 | try { 3 | await promise; 4 | assert.fail('Expected revert not received'); 5 | } catch (error) { 6 | const revertFound = error.message.search('revert') >= 0; 7 | assert(revertFound, `Expected "revert", got ${error} instead`); 8 | } 9 | } 10 | 11 | module.exports = assertRevert; 12 | -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/test/helpers/encodeCall.js: -------------------------------------------------------------------------------- 1 | const abi = require('ethereumjs-abi') 2 | 3 | function encodeCall(name, arguments, values) { 4 | const methodId = abi.methodID(name, arguments).toString('hex'); 5 | const params = abi.rawEncode(arguments, values).toString('hex'); 6 | return '0x' + methodId + params; 7 | } 8 | 9 | module.exports = encodeCall; -------------------------------------------------------------------------------- /upgradeability_using_unstructured_storage/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /upgradeability_with_vtable/README.md: -------------------------------------------------------------------------------- 1 | # Upgradeability with vtables 2 | 3 | This idea builds on _upgradeability using inherited storage_, but breaks the implementation contract in multiple contracts. The `Registry` contract keeps track of which contract satisfies _each function_ for a version, similar to a vtable, that maps function signatures to addresses with the actual implementations. 4 | 5 | This allows to make a minor modification to a function in a contract without having to re-upload the entire code for all the contract, and just upgrade the contract that backs that particular function (see the `should upgrade single function test`). It also drops the limitation of a single contract having to fit within a block, since the actual implementation is spread among multiple contracts. 6 | 7 | The main drawback is gas consumption, since now every call requires an additional call to the registry to retrieve the address that implements a particular function. Nevertheless, this could be solved by caching the vtable for a particular version from the registry to each proxy upon initialization. 8 | 9 | This approach requires to define a base contract for the base storage and another for the full interface. All implementation contracts must inherit from the storage contract, and whenever a call needs to be made, `this` should be casted to the interface contract, which will go back to the proxy and re-routed via the vtable. -------------------------------------------------------------------------------- /upgradeability_with_vtable/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.17; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) _; 9 | } 10 | 11 | function Migrations() public { 12 | owner = msg.sender; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /upgradeability_with_vtable/contracts/Proxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | /** 4 | * @title Proxy 5 | * @dev Gives the possibility to delegate any call to a foreign implementation. 6 | */ 7 | contract Proxy { 8 | /** 9 | * @dev Tells the address of the implementation where every call will be delegated. 10 | * @return address of the implementation to which it will be delegated 11 | */ 12 | function implementation(bytes4 func) public view returns (address); 13 | 14 | /** 15 | * @dev Fallback function allowing to perform a delegatecall to the given implementation. 16 | * This function will return whatever the implementation call returns 17 | */ 18 | function () payable public { 19 | address _impl = implementation(msg.sig); 20 | require(_impl != address(0)); 21 | 22 | assembly { 23 | let ptr := mload(0x40) 24 | calldatacopy(ptr, 0, calldatasize) 25 | let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) 26 | let size := returndatasize 27 | returndatacopy(ptr, 0, size) 28 | 29 | switch result 30 | case 0 { revert(ptr, size) } 31 | default { return(ptr, size) } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /upgradeability_with_vtable/contracts/UpgradeabilityProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './Proxy.sol'; 4 | import './IRegistry.sol'; 5 | import './UpgradeabilityStorage.sol'; 6 | 7 | /** 8 | * @title UpgradeabilityProxy 9 | * @dev This contract represents a proxy where the implementation address to which it will delegate can be upgraded 10 | */ 11 | contract UpgradeabilityProxy is Proxy, UpgradeabilityStorage { 12 | 13 | /** 14 | * @dev Constructor function 15 | */ 16 | function UpgradeabilityProxy(string _version) public { 17 | registry = IRegistry(msg.sender); 18 | version_ = _version; 19 | loadVersion(); 20 | } 21 | 22 | /** 23 | * @dev Upgrades the implementation of a given function to the requested version 24 | * @param targetVersion representing the version name of the new implementation to be set 25 | */ 26 | function upgradeTo(string targetVersion) public { 27 | clearVersion(); 28 | version_ = targetVersion; 29 | loadVersion(); 30 | } 31 | 32 | /** 33 | * @dev Clears from the implementation cache all functions from the current version 34 | */ 35 | function clearVersion() internal { 36 | bytes4 func; 37 | address impl; 38 | uint256 i; 39 | 40 | for (i = 0; i < registry.getFunctionCount(version_); i++) { 41 | (func, impl) = registry.getFunctionByIndex(version_, i); 42 | implementations_[func] = 0; 43 | } 44 | 45 | fallback_ = address(0); 46 | } 47 | 48 | /** 49 | * @dev Adds to the implementation cache all functions from the current version 50 | */ 51 | function loadVersion() internal { 52 | bytes4 func; 53 | address impl; 54 | uint256 i; 55 | 56 | for (i = 0; i < registry.getFunctionCount(version_); i++) { 57 | (func, impl) = registry.getFunctionByIndex(version_, i); 58 | implementations_[func] = impl; 59 | } 60 | 61 | fallback_ = registry.getFallback(version_); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /upgradeability_with_vtable/contracts/UpgradeabilityStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './IRegistry.sol'; 4 | 5 | /** 6 | * @title UpgradeabilityStorage 7 | * @dev This contract holds all the necessary state variables to support the upgrade functionality 8 | */ 9 | contract UpgradeabilityStorage { 10 | // Registry of versions 11 | IRegistry internal registry; 12 | 13 | // Current version 14 | string internal version_; 15 | 16 | // Optional fallback function 17 | address internal fallback_; 18 | 19 | // Cache of functions to current implementations 20 | mapping (bytes4 => address) internal implementations_; 21 | 22 | /** 23 | * @dev Returns the address of the current implementation for a given function signature 24 | * @param func representing the signature of the function to query the implementation of 25 | * @return address of the current implementation of the given function 26 | */ 27 | function implementation(bytes4 func) public view returns (address) { 28 | address impl = implementations_[func]; 29 | return impl == address(0) ? fallback_ : impl; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /upgradeability_with_vtable/contracts/Upgradeable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.18; 2 | 3 | import './UpgradeabilityStorage.sol'; 4 | 5 | /** 6 | * @title Upgradeable 7 | * @dev This contract holds all the minimum required functionality for a behavior to be upgradeable. 8 | * This means, required state variables for owned upgradeability purpose and simple initialization validation. 9 | */ 10 | contract Upgradeable is UpgradeabilityStorage { 11 | /** 12 | * @dev Validates the caller is the versions registry. 13 | * THIS FUNCTION SHOULD BE OVERRIDDEN CALLING SUPER 14 | * @param sender representing the address deploying the initial behavior of the contract 15 | */ 16 | function initialize(address sender) public payable { 17 | require(msg.sender == address(registry)); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /upgradeability_with_vtable/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /upgradeability_with_vtable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "upgradeability-with-vtable", 3 | "version": "0.1.0", 4 | "private": true, 5 | "author": "Santiago Palladino ", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "truffle test" 9 | }, 10 | "devDependencies": { 11 | "ethereumjs-abi": "^0.6.5", 12 | "truffle": "^4.1.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /upgradeability_with_vtable/test/helpers/assertRevert.js: -------------------------------------------------------------------------------- 1 | async function assertRevert(promise) { 2 | try { 3 | await promise; 4 | assert.fail('Expected revert not received'); 5 | } catch (error) { 6 | const revertFound = error.message.search('revert') >= 0; 7 | assert(revertFound, `Expected "revert", got ${error} instead`); 8 | } 9 | } 10 | 11 | module.exports = assertRevert; -------------------------------------------------------------------------------- /upgradeability_with_vtable/test/helpers/signature.js: -------------------------------------------------------------------------------- 1 | const abi = require('ethereumjs-abi') 2 | 3 | function signature(name, arguments) { 4 | return '0x' + abi.methodID(name, arguments).toString('hex'); 5 | } 6 | 7 | module.exports = signature; -------------------------------------------------------------------------------- /upgradeability_with_vtable/truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // add network configurations here 4 | } 5 | } 6 | --------------------------------------------------------------------------------