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?

2
  • 1
    deploy Foo then use EXTCODECOPY on its address? ethereum.stackexchange.com/questions/1906/…
    – eth
    Commented Dec 7, 2016 at 9:10
  • Thanks. So it's looks possible but non-trivial. One day I'll get enough time to study EVM assembly in depth...
    – o0ragman0o
    Commented Dec 7, 2016 at 11:31

2 Answers 2

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;
}
1
  • It does not run what inside the constructor, is there a new way to do this?
    – Majd TL
    Commented May 15, 2019 at 17:21
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();
}
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.