├── .gitignore ├── .jshintrc ├── Dockerfile ├── contracts ├── Dispatcher.sol ├── DispatcherStorage.sol ├── LibInterface.sol ├── Migrations.sol └── TheContract.sol ├── docker-build.sh ├── docker-compose.yml ├── migrations └── 1_migrations.js ├── package.json ├── test ├── Example.sol ├── Example2.sol ├── Example2WithoutProxy.sol ├── ExampleReverts.sol └── test.es6 └── truffle.js /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | build/ 4 | .idea/ 5 | node_modules/ 6 | /package-lock.json 7 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "bitwise": false, // Prohibit bitwise operators (&, |, ^, etc.). 3 | "browser": true, // Standard browser globals e.g. `window`, `document`. 4 | "camelcase": false, // Permit only camelcase for `var` and `object indexes`. 5 | "curly": true, // Require {} for every new block or scope. 6 | "devel": false, // Allow development statements e.g. `console.log();`. 7 | "eqeqeq": true, // Require triple equals i.e. `===`. 8 | "esnext": true, // Allow ES.next specific features such as `const` and `let`. 9 | "freeze": true, // Forbid overwriting prototypes of native objects such as Array, Date and so on. 10 | "immed": true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` 11 | "indent": 2, // Specify indentation spacing 12 | "latedef": true, // Prohibit variable use before definition. 13 | "newcap": false, // Require capitalization of all constructor functions e.g. `new F()`. 14 | "noarg": true, // Prohibit use of `arguments.caller` and `arguments.callee`. 15 | "node": true, // Enable globals available when code is running inside of the NodeJS runtime environment. 16 | "noempty": true, // Prohibit use of empty blocks. 17 | "nonew": true, // Prohibits the use of constructor functions for side-effects 18 | "quotmark": "single", // Define quotes to string values. 19 | "regexp": true, // Prohibit `.` and `[^...]` in regular expressions. 20 | "smarttabs": false, // Supress warnings about mixed tabs and spaces 21 | "strict": true, // Require `use strict` pragma in every file. 22 | "trailing": true, // Prohibit trailing whitespaces. 23 | "undef": true, // Require all non-global variables be declared before they are used. 24 | "unused": true, // Warn unused variables. 25 | 26 | "maxparams": 4, // Maximum number of parameters for a function 27 | "maxstatements": 15, // Maximum number of statements in a function 28 | "maxcomplexity": 10, // Cyclomatic complexity (http://en.wikipedia.org/wiki/Cyclomatic_complexity) 29 | "maxdepth": 4, // Maximum depth of nested control structures 30 | "maxlen": 120, // Maximum number of cols in a line 31 | "multistr": true, // Allow use of multiline EOL escaping 32 | "experimental": ["asyncawait", "asyncreqawait"], 33 | 34 | "predef": [ // Extra globals. 35 | "after", 36 | "afterEach", 37 | "before", 38 | "beforeEach", 39 | "define", 40 | "describe", 41 | "exports", 42 | "it", 43 | "web3", 44 | "artifacts", 45 | "contract", 46 | "assert" 47 | ] 48 | } 49 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8.7.0 2 | ARG USER_NAME 3 | RUN apt-get update && apt-get install -y sudo && npm install -g truffle 4 | RUN useradd --user-group --create-home --shell /bin/false ${USER_NAME} &&\ 5 | echo "${USER_NAME} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers.d/${USER_NAME} 6 | RUN mkdir /opt/work && \ 7 | chown ${USER_NAME}:${USER_NAME} /opt/work 8 | USER ${USER_NAME} 9 | WORKDIR /opt/work 10 | -------------------------------------------------------------------------------- /contracts/Dispatcher.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "./DispatcherStorage.sol"; 4 | 5 | contract Dispatcher { 6 | function() public { 7 | DispatcherStorage dispatcherStorage = DispatcherStorage(0x1111222233334444555566667777888899990000); 8 | address target = dispatcherStorage.lib(); 9 | 10 | assembly { 11 | calldatacopy(0x0, 0x0, calldatasize) 12 | let success := delegatecall(sub(gas, 10000), target, 0x0, calldatasize, 0, 0) 13 | let retSz := returndatasize 14 | returndatacopy(0, 0, retSz) 15 | switch success 16 | case 0 { 17 | revert(0, retSz) 18 | } 19 | default { 20 | return(0, retSz) 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /contracts/DispatcherStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "zeppelin-solidity/contracts/ownership/Ownable.sol"; 4 | contract DispatcherStorage is Ownable { 5 | address public lib; 6 | 7 | function DispatcherStorage(address newLib) public { 8 | replace(newLib); 9 | } 10 | 11 | function replace(address newLib) public onlyOwner /* onlyDAO */ { 12 | lib = newLib; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/LibInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | library LibInterface { 4 | struct S { uint i; } 5 | 6 | function getUint(S storage s) public constant returns (uint); 7 | function setUint(S storage s, uint i) public; 8 | } 9 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.4; 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 | -------------------------------------------------------------------------------- /contracts/TheContract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "./LibInterface.sol"; 4 | 5 | contract TheContract { 6 | LibInterface.S s; 7 | 8 | using LibInterface for LibInterface.S; 9 | 10 | function get() public constant returns (uint) { 11 | return s.getUint(); 12 | } 13 | 14 | function set(uint i) public { 15 | return s.setUint(i); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /docker-build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | docker-compose build --build-arg USER_NAME=$(whoami) solidity-proxy 3 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | solidity-proxy: 4 | build: 5 | context: . 6 | args: 7 | USER_NAME: app 8 | volumes: 9 | - .:/opt/work 10 | 11 | -------------------------------------------------------------------------------- /migrations/1_migrations.js: -------------------------------------------------------------------------------- 1 | module.exports = (deployer) => { 2 | deployer.deploy(artifacts.require('Migrations.sol')) 3 | } 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidity-proxy", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "truffle.js", 6 | "directories": { 7 | "test": "truffle test" 8 | }, 9 | "dependencies": { 10 | "babel-polyfill": "^6.26.0", 11 | "lk-test-helpers": "^0.1.3", 12 | "zeppelin-solidity": "^1.4.0" 13 | }, 14 | "devDependencies": {}, 15 | "scripts": { 16 | "test": "truffle test" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/nakajo2011/solidity-proxy.git" 21 | }, 22 | "author": "nakajo", 23 | "license": "ISC", 24 | "bugs": { 25 | "url": "https://github.com/nakajo2011/solidity-proxy/issues" 26 | }, 27 | "homepage": "https://github.com/nakajo2011/solidity-proxy#readme" 28 | } 29 | -------------------------------------------------------------------------------- /test/Example.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "../contracts/LibInterface.sol"; 4 | 5 | library Example { 6 | function getUint(LibInterface.S storage s) public constant returns (uint) { 7 | return s.i; 8 | } 9 | function setUint(LibInterface.S storage s, uint i) public { 10 | s.i = i; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/Example2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "../contracts/LibInterface.sol"; 4 | 5 | library Example2 { 6 | function getUint(LibInterface.S storage s) public constant returns (uint) { 7 | return s.i * 10; 8 | } 9 | 10 | function setUint(LibInterface.S storage s, uint i) public { 11 | s.i = i; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/Example2WithoutProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract Example2WithoutProxy { 4 | uint i; 5 | 6 | function getUint() public constant returns (uint) { 7 | return i * 10; 8 | } 9 | 10 | function setUint(uint _i) public { 11 | i = _i; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/ExampleReverts.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | import "../contracts/LibInterface.sol"; 4 | 5 | library ExampleReverts { 6 | function getUint(LibInterface.S storage s) public constant returns (uint) { 7 | revert(); 8 | } 9 | function setUint(LibInterface.S storage s, uint i) public { 10 | s.i = i; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/test.es6: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const lkTestHelpers = require('lk-test-helpers') 4 | const { 5 | expectThrow 6 | } = lkTestHelpers(web3) 7 | 8 | const Example = artifacts.require('./Example.sol') 9 | const ExampleReverts = artifacts.require('./ExampleReverts.sol') 10 | const Example2 = artifacts.require('./Example2.sol') 11 | const Dispatcher = artifacts.require('Dispatcher.sol') 12 | const DispatcherStorage = artifacts.require('DispatcherStorage.sol') 13 | const TheContract = artifacts.require('TheContract.sol') 14 | 15 | contract('TestProxyLibrary', (accounts) => { 16 | describe('test', () => { 17 | it('works', async () => { 18 | const example = await Example.new() 19 | const dispatcherStorage = await DispatcherStorage.new(example.address) 20 | Dispatcher.unlinked_binary = Dispatcher.unlinked_binary 21 | .replace('1111222233334444555566667777888899990000', 22 | dispatcherStorage.address.slice(2)) 23 | const dispatcher = await Dispatcher.new() 24 | TheContract.link('LibInterface', dispatcher.address); 25 | const thecontract = await TheContract.new() 26 | await thecontract.set(10) 27 | const x = await thecontract.get() 28 | assert.equal(x.toNumber(), 10)// Example return not multiplies 29 | 30 | const example2 = await Example2.new() 31 | await dispatcherStorage.replace(example2.address) 32 | const x2 = await thecontract.get() 33 | assert.equal(x2.toNumber(), 10 * 10);// Example2 return 10 multiplies 34 | 35 | const exampleReverts = await ExampleReverts.new() 36 | await dispatcherStorage.replace(exampleReverts.address) 37 | await expectThrow(thecontract.get()) 38 | }); 39 | it('measure gas costs', (done) => { 40 | done(); 41 | }); 42 | 43 | context('can call only owner', () => { 44 | let example, example2, dispatcherStorage, subject 45 | beforeEach(async () => { 46 | example = await Example.new() 47 | example2 = await Example2.new() 48 | dispatcherStorage = await DispatcherStorage.new(example.address, {from: accounts[0]}) 49 | subject = (account) => dispatcherStorage.replace(example2.address, {from: account}) 50 | }) 51 | 52 | it('fail', async () => { 53 | await expectThrow(subject(accounts[1])) 54 | }) 55 | it('success', async () => { 56 | const result = await subject(accounts[0]) 57 | assert.isOk(result) 58 | }) 59 | }) 60 | }); 61 | }) 62 | ; 63 | -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | require('babel-polyfill'); 2 | 3 | module.exports = { 4 | networks: { 5 | sample: { 6 | host: "localhost", 7 | port: 8545, 8 | network_id: "*" // Match any network id 9 | } 10 | } 11 | }; 12 | --------------------------------------------------------------------------------