8

Using new to create a contract causes the factory contract to compile with the product contract's bytecode appended to the end...

contract Foo
{
    function Foo() {}
}

bytecode: 60606040525b600056

contract FooFactory
{
    function newFoo() returns (Foo) {
       return new Foo();
    }
}

bytecode: 6060604052346000575b6092806100166000396000f3606060405260e060020a60003504639a67a7068114601c575b6000565b346000576026604f565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6000604051602080607283396040519101819003906000f0801560005790505b90566060604052346000575b5b5b60098060176000396000f360606040525b600056                                                                        
                                                                                                                                                                                                                                                                                                                                        ^ Foo bytecode starts here

For large factory/product contracts this can become a block gas limit issue upon deployment.

Is there a way for a factory contract to clone the bytecode of an existing contract?

eth
  • 85,679
  • 53
  • 285
  • 406
o0ragman0o
  • 4,320
  • 18
  • 35
  • 1
    deploy Foo then use EXTCODECOPY on its address? http://ethereum.stackexchange.com/questions/1906/can-a-contract-access-the-code-of-another-contract – eth Dec 07 '16 at 09:10
  • Thanks. So it's looks possible but non-trivial. One day I'll get enough time to study EVM assembly in depth... – o0ragman0o Dec 07 '16 at 11:31

2 Answers2

8

Martin Holst Swende published an implementation of a cloner contract here! https://gist.github.com/holiman/069de8d056a531575d2b786df3345665

Excerpt:

function clone(address a) returns(address){

    /*

    Assembly of the code that we want to use as init-code in the new contract, 
    along with stack values:
                    # bottom [ STACK ] top
     PUSH1 00       # [ 0 ]
     DUP1           # [ 0, 0 ]
     PUSH20         
     <address>      # [0,0, address] 
     DUP1       # [0,0, address ,address]
     EXTCODESIZE    # [0,0, address, size ]
     DUP1           # [0,0, address, size, size]
     SWAP4          # [ size, 0, address, size, 0]
     DUP1           # [ size, 0, address ,size, 0,0]
     SWAP2          # [ size, 0, address, 0, 0, size]
     SWAP3          # [ size, 0, size, 0, 0, address]
     EXTCODECOPY    # [ size, 0]
     RETURN 

    The code above weighs in at 33 bytes, which is _just_ above fitting into a uint. 
    So a modified version is used, where the initial PUSH1 00 is replaced by `PC`. 
    This is one byte smaller, and also a bit cheaper Wbase instead of Wverylow. It only costs 2 gas.

     PC             # [ 0 ]
     DUP1           # [ 0, 0 ]
     PUSH20         
     <address>      # [0,0, address] 
     DUP1       # [0,0, address ,address]
     EXTCODESIZE    # [0,0, address, size ]
     DUP1           # [0,0, address, size, size]
     SWAP4          # [ size, 0, address, size, 0]
     DUP1           # [ size, 0, address ,size, 0,0]
     SWAP2          # [ size, 0, address, 0, 0, size]
     SWAP3          # [ size, 0, size, 0, 0, address]
     EXTCODECOPY    # [ size, 0]
     RETURN 

    The opcodes are:
    58 80 73 <address> 80 3b 80 93 80 91 92 3c F3
    We get <address> in there by OR:ing the upshifted address into the 0-filled space. 
      5880730000000000000000000000000000000000000000803b80938091923cF3 
     +000000xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx000000000000000000
     -----------------------------------------------------------------
      588073xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx00000803b80938091923cF3

    This is simply stored at memory position 0, and create is invoked. 

    */
    address retval;
    assembly{
        mstore(0x0, or (0x5880730000000000000000000000000000000000000000803b80938091923cF3 ,mul(a,0x1000000000000000000)))
        retval := create(0,0, 32)
    }
    return retval;
}
eth
  • 85,679
  • 53
  • 285
  • 406
Raine Revere
  • 3,600
  • 2
  • 23
  • 34
0

this code has one important disadvantage. It does not run source contract constructor if present pragma solidity ^0.4.25;

contract Factory {

function at(address _addr) private view returns (bytes memory o_code) {
    assembly {
        // retrieve the size of the code, this needs assembly
        let size := extcodesize(_addr)
        // allocate output byte array - this could also be done without assembly
        // by using o_code = new bytes(size)
        o_code := mload(0x40)
        // new "memory end" including padding
        mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
        // store length in memory
        mstore(o_code, size)
        // actually retrieve the code, this needs assembly
        extcodecopy(_addr, add(o_code, 0x20), 0, size)
    }
}

function create(address _addrOfCode) returns (address){
    address retval;
    assembly{
        mstore(0x0, or (0x5880730000000000000000000000000000000000000000803b80938091923cF3 ,mul(_addrOfCode,0x1000000000000000000)))
        retval := create(0,0, 32)
    }
    return retval;
}
}

contract Adder {
uint256 public param;

constructor(){
    param = 5;
}

function add(uint a, uint b) returns (uint){
    return a+b;
}
}

contract Tester {

Adder a;

function Tester(address factory,address adder){
    address cAdr = Factory(factory).create(adder);
    a = Adder(cAdr);
    if(address(a) == 0) throw;
}

function test(uint x, uint y) constant returns (uint){
    return a.add(x,y);
}
/* Reasonable expectation is 5, but it is 0 */
function getParam() constant returns (uint){
    return a.param();
}
}