Usar new
para crear un contrato hace que el contrato de fábrica se compile con el código de bytes del contrato del producto adjunto al final...
contract Foo
{
function Foo() {}
}
bytecode: 60606040525b600056
contract FooFactory
{
function newFoo() returns (Foo) {
return new Foo();
}
}
bytecode: 6060604052346000575b6092806100166000396000f3606060405260e060020a60003504639a67a7068114601c575b6000565b346000576026604f565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6000604051602080607283396040519101819003906000f0801560005790505b90566060604052346000575b5b5b60098060176000396000f360606040525b600056
^ Foo bytecode starts here
Para contratos de grandes fábricas/productos, esto puede convertirse en un problema de límite de gas en bloque al momento de la implementación.
¿Hay alguna forma de que un contrato de fábrica clone el código de bytes de un contrato existente?
¡Martin Holst Swende publicó una implementación de un contrato de clonación aquí! https://gist.github.com/holiman/069de8d056a531575d2b786df3345665
Extracto:
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;
}
este código tiene una desventaja importante. No ejecuta el constructor del contrato fuente si presenta solidez de pragma ^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();
}
}
ética
o0ragman0o