Die Verwendung new
zum Erstellen eines Vertrags bewirkt, dass der Fabrikvertrag kompiliert wird, wobei der Bytecode des Produktvertrags am Ende angehängt wird ...
contract Foo
{
function Foo() {}
}
bytecode: 60606040525b600056
contract FooFactory
{
function newFoo() returns (Foo) {
return new Foo();
}
}
bytecode: 6060604052346000575b6092806100166000396000f3606060405260e060020a60003504639a67a7068114601c575b6000565b346000576026604f565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b6000604051602080607283396040519101819003906000f0801560005790505b90566060604052346000575b5b5b60098060176000396000f360606040525b600056
^ Foo bytecode starts here
Bei großen Werks-/Produktverträgen kann dies bei der Bereitstellung zu einem Problem mit der Blockgasgrenze werden.
Gibt es eine Möglichkeit für einen Fabrikvertrag, den Bytecode eines bestehenden Vertrags zu klonen?
Martin Holst Swende hat hier eine Umsetzung eines Klonervertrages veröffentlicht! https://gist.github.com/holiman/069de8d056a531575d2b786df3345665
Auszug:
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;
}
Dieser Code hat einen wichtigen Nachteil. Es führt den Quellvertragskonstruktor nicht aus, wenn die pragma solidity vorhanden ist ^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();
}
}
eth
o0ragman0o