From 6d135a464222cda45036d4ac9cbe712cd4b03c0a Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Thu, 7 Mar 2024 17:37:58 +0100 Subject: [PATCH 1/5] revert_on_failure for contract creation Fixes #3744 --- .../builtins/codegen/test_create_functions.py | 74 +++++++++++++------ vyper/builtins/functions.py | 45 +++++++---- 2 files changed, 83 insertions(+), 36 deletions(-) diff --git a/tests/functional/builtins/codegen/test_create_functions.py b/tests/functional/builtins/codegen/test_create_functions.py index c99942d99d..d010c03de0 100644 --- a/tests/functional/builtins/codegen/test_create_functions.py +++ b/tests/functional/builtins/codegen/test_create_functions.py @@ -110,13 +110,17 @@ def test2(a: uint256) -> Bytes[100]: assert receipt["gasUsed"] < GAS_SENT -def test_create_minimal_proxy_to_create2(get_contract, create2_address_of, keccak, tx_failed): - code = """ +@pytest.mark.parametrize("revert_on_failure", [True, False, None]) +def test_create_minimal_proxy_to_create2( + get_contract, create2_address_of, keccak, tx_failed, revert_on_failure +): + revert_arg = "" if revert_on_failure is None else f", revert_on_failure={revert_on_failure}" + code = f""" main: address @external def test(_salt: bytes32) -> address: - self.main = create_minimal_proxy_to(self, salt=_salt) + self.main = create_minimal_proxy_to(self, salt=_salt{revert_arg}) return self.main """ @@ -129,16 +133,28 @@ def test(_salt: bytes32) -> address: c.test(salt, transact={}) # revert on collision - with tx_failed(): - c.test(salt, transact={}) + if revert_on_failure is False: + assert not c.test(salt) + else: + with tx_failed(): + c.test(salt, transact={}) # test blueprints with various prefixes - 0xfe would block calls to the blueprint # contract, and 0xfe7100 is ERC5202 magic @pytest.mark.parametrize("blueprint_prefix", [b"", b"\xfe", ERC5202_PREFIX]) +@pytest.mark.parametrize("revert_on_failure", [True, False, None]) def test_create_from_blueprint( - get_contract, deploy_blueprint_for, w3, keccak, create2_address_of, tx_failed, blueprint_prefix + get_contract, + deploy_blueprint_for, + w3, + keccak, + create2_address_of, + tx_failed, + blueprint_prefix, + revert_on_failure, ): + revert_arg = "" if revert_on_failure is None else f", revert_on_failure={revert_on_failure}" code = """ @external def foo() -> uint256: @@ -151,14 +167,16 @@ def foo() -> uint256: @external def test(target: address): - self.created_address = create_from_blueprint(target, code_offset={prefix_len}) + self.created_address = create_from_blueprint(target, code_offset={prefix_len}{revert_arg}) @external def test2(target: address, salt: bytes32): - self.created_address = create_from_blueprint(target, code_offset={prefix_len}, salt=salt) + self.created_address = create_from_blueprint( + target, code_offset={prefix_len}, salt=salt{revert_arg} + ) """ - # deploy a foo so we can compare its bytecode with factory deployed version + # deploy a foo, so we can compare its bytecode with factory deployed version foo_contract = get_contract(code) expected_runtime_code = w3.eth.get_code(foo_contract.address) @@ -174,8 +192,11 @@ def test2(target: address, salt: bytes32): # extcodesize check zero_address = "0x" + "00" * 20 - with tx_failed(): - d.test(zero_address) + if revert_on_failure is False: + assert not d.test(zero_address) + else: + with tx_failed(): + d.test(zero_address) # now same thing but with create2 salt = keccak(b"vyper") @@ -191,8 +212,11 @@ def test2(target: address, salt: bytes32): assert HexBytes(test.address) == create2_address_of(d.address, salt, initcode) # can't collide addresses - with tx_failed(): - d.test2(f.address, salt) + if revert_on_failure is False: + assert not d.test2(f.address, salt) + else: + with tx_failed(): + d.test2(f.address, salt) # test blueprints with 0xfe7100 prefix, which is the EIP 5202 standard. @@ -425,16 +449,18 @@ def should_fail(target: address, arg1: String[129], arg2: Bar): w3.eth.send_transaction({"to": d.address, "data": f"{sig}{encoded}"}) -def test_create_copy_of(get_contract, w3, keccak, create2_address_of, tx_failed): - code = """ +@pytest.mark.parametrize("revert_on_failure", [True, False, None]) +def test_create_copy_of(get_contract, w3, keccak, create2_address_of, tx_failed, revert_on_failure): + revert_arg = "" if revert_on_failure is None else f", revert_on_failure={revert_on_failure}" + code = f""" created_address: public(address) @internal def _create_copy_of(target: address): - self.created_address = create_copy_of(target) + self.created_address = create_copy_of(target{revert_arg}) @internal def _create_copy_of2(target: address, salt: bytes32): - self.created_address = create_copy_of(target, salt=salt) + self.created_address = create_copy_of(target, salt=salt{revert_arg}) @external def test(target: address) -> address: @@ -459,8 +485,11 @@ def test2(target: address, salt: bytes32) -> address: assert w3.eth.get_code(test1) == bytecode # extcodesize check - with tx_failed(): - c.test("0x" + "00" * 20) + if revert_on_failure is False: + assert not c.test("0x" + "00" * 20) + else: + with tx_failed(): + c.test("0x" + "00" * 20) # test1 = c.test(b"\x01") # assert w3.eth.get_code(test1) == b"\x01" @@ -473,8 +502,11 @@ def test2(target: address, salt: bytes32) -> address: assert HexBytes(test2) == create2_address_of(c.address, salt, vyper_initcode(bytecode)) # can't create2 where contract already exists - with tx_failed(): - c.test2(c.address, salt, transact={}) + if revert_on_failure is False: + assert not c.test2(c.address, salt, transact={}) + else: + with tx_failed(): + c.test2(c.address, salt, transact={}) # test single byte contract # test2 = c.test2(b"\x01", salt) diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py index b11b9174e6..9ce999eff6 100644 --- a/vyper/builtins/functions.py +++ b/vyper/builtins/functions.py @@ -1696,6 +1696,7 @@ class _CreateBase(BuiltinFunctionT): _kwargs = { "value": KwargSettings(UINT256_T, zero_value), "salt": KwargSettings(BYTES32_T, empty_value), + "revert_on_failure": KwargSettings(BoolT(), True, require_literal=True), } _return_type = AddressT() @@ -1729,7 +1730,7 @@ def _add_gas_estimate(self, args, should_use_create2): bytecode_len = 20 + len(b) + len(c) return _create_addl_gas_estimate(bytecode_len, should_use_create2) - def _build_create_IR(self, expr, args, context, value, salt): + def _build_create_IR(self, expr, args, context, value, salt, revert_on_failure): target_address = args[0] buf = context.new_internal_variable(BytesT(96)) @@ -1757,7 +1758,7 @@ def _build_create_IR(self, expr, args, context, value, salt): ["mstore", buf, forwarder_preamble], ["mstore", ["add", buf, preamble_length], aligned_target], ["mstore", ["add", buf, preamble_length + 20], forwarder_post], - _create_ir(value, buf, buf_len, salt=salt), + _create_ir(value, buf, buf_len, salt=salt, checked=revert_on_failure), ] @@ -1786,7 +1787,7 @@ def _add_gas_estimate(self, args, should_use_create2): # max possible runtime length + preamble length return _create_addl_gas_estimate(EIP_170_LIMIT + self._preamble_len, should_use_create2) - def _build_create_IR(self, expr, args, context, value, salt): + def _build_create_IR(self, expr, args, context, value, salt, revert_on_failure): target = args[0] # something we can pass to scope_multi @@ -1802,10 +1803,13 @@ def _build_create_IR(self, expr, args, context, value, salt): ir = ["seq"] # make sure there is actually code at the target - check_codesize = ["assert", codesize] - ir.append( - IRnode.from_list(check_codesize, error_msg="empty target (create_copy_of)") - ) + code_exists = ["sgt", codesize, 0] + if revert_on_failure: + ir.append( + IRnode.from_list( + ["assert", code_exists], error_msg="empty target (create_copy_of)" + ) + ) # store the preamble at msize + 22 (zero padding) preamble, preamble_len = _create_preamble(codesize) @@ -1820,7 +1824,10 @@ def _build_create_IR(self, expr, args, context, value, salt): buf = add_ofst(mem_ofst, 32 - preamble_len) buf_len = ["add", codesize, preamble_len] - ir.append(_create_ir(value, buf, buf_len, salt)) + ir.append(_create_ir(value, buf, buf_len, salt, checked=revert_on_failure)) + + if not revert_on_failure: + ir = ["if", code_exists, ir, 0] return b1.resolve(b2.resolve(ir)) @@ -1833,6 +1840,7 @@ class CreateFromBlueprint(_CreateBase): "salt": KwargSettings(BYTES32_T, empty_value), "raw_args": KwargSettings(BoolT(), False, require_literal=True), "code_offset": KwargSettings(UINT256_T, IRnode.from_list(3, typ=UINT256_T)), + "revert_on_failure": KwargSettings(BoolT(), True, require_literal=True), } _has_varargs = True @@ -1842,7 +1850,9 @@ def _add_gas_estimate(self, args, should_use_create2): maxlen = EIP_170_LIMIT + ctor_args.typ.abi_type.size_bound() return _create_addl_gas_estimate(maxlen, should_use_create2) - def _build_create_IR(self, expr, args, context, value, salt, code_offset, raw_args): + def _build_create_IR( + self, expr, args, context, value, salt, code_offset, raw_args, revert_on_failure + ): target = args[0] ctor_args = args[1:] @@ -1896,12 +1906,14 @@ def _build_create_IR(self, expr, args, context, value, salt, code_offset, raw_ar # (code_ofst == (extcodesize target) would be empty # initcode, which we disallow for hygiene reasons - # same as `create_copy_of` on an empty target). - check_codesize = ["assert", ["sgt", codesize, 0]] - ir.append( - IRnode.from_list( - check_codesize, error_msg="empty target (create_from_blueprint)" + code_exists = ["sgt", codesize, 0] + if revert_on_failure: + ir.append( + IRnode.from_list( + ["assert", code_exists], + error_msg="empty target (create_from_blueprint)", + ) ) - ) # copy the target code into memory. # layout starting from mem_ofst: @@ -1918,7 +1930,10 @@ def _build_create_IR(self, expr, args, context, value, salt, code_offset, raw_ar length = ["add", codesize, encoded_args_len] - ir.append(_create_ir(value, mem_ofst, length, salt)) + ir.append(_create_ir(value, mem_ofst, length, salt, checked=revert_on_failure)) + + if not revert_on_failure: + ir = ["if", code_exists, ir, 0] return b1.resolve(b2.resolve(ir)) From 792a5a5afe4b03f8c707579972eed42f77c7c3c1 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Thu, 7 Mar 2024 17:40:52 +0100 Subject: [PATCH 2/5] Restore extcodesize check --- .../builtins/codegen/test_create_functions.py | 14 +++------ vyper/builtins/functions.py | 29 ++++++------------- 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/tests/functional/builtins/codegen/test_create_functions.py b/tests/functional/builtins/codegen/test_create_functions.py index d010c03de0..bb2cda702e 100644 --- a/tests/functional/builtins/codegen/test_create_functions.py +++ b/tests/functional/builtins/codegen/test_create_functions.py @@ -192,11 +192,8 @@ def test2(target: address, salt: bytes32): # extcodesize check zero_address = "0x" + "00" * 20 - if revert_on_failure is False: - assert not d.test(zero_address) - else: - with tx_failed(): - d.test(zero_address) + with tx_failed(): + d.test(zero_address) # now same thing but with create2 salt = keccak(b"vyper") @@ -485,11 +482,8 @@ def test2(target: address, salt: bytes32) -> address: assert w3.eth.get_code(test1) == bytecode # extcodesize check - if revert_on_failure is False: - assert not c.test("0x" + "00" * 20) - else: - with tx_failed(): - c.test("0x" + "00" * 20) + with tx_failed(): + c.test("0x" + "00" * 20) # test1 = c.test(b"\x01") # assert w3.eth.get_code(test1) == b"\x01" diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py index 9ce999eff6..de03ef88c0 100644 --- a/vyper/builtins/functions.py +++ b/vyper/builtins/functions.py @@ -1803,13 +1803,10 @@ def _build_create_IR(self, expr, args, context, value, salt, revert_on_failure): ir = ["seq"] # make sure there is actually code at the target - code_exists = ["sgt", codesize, 0] - if revert_on_failure: - ir.append( - IRnode.from_list( - ["assert", code_exists], error_msg="empty target (create_copy_of)" - ) - ) + check_codesize = ["assert", codesize] + ir.append( + IRnode.from_list(check_codesize, error_msg="empty target (create_copy_of)") + ) # store the preamble at msize + 22 (zero padding) preamble, preamble_len = _create_preamble(codesize) @@ -1826,9 +1823,6 @@ def _build_create_IR(self, expr, args, context, value, salt, revert_on_failure): ir.append(_create_ir(value, buf, buf_len, salt, checked=revert_on_failure)) - if not revert_on_failure: - ir = ["if", code_exists, ir, 0] - return b1.resolve(b2.resolve(ir)) @@ -1906,14 +1900,12 @@ def _build_create_IR( # (code_ofst == (extcodesize target) would be empty # initcode, which we disallow for hygiene reasons - # same as `create_copy_of` on an empty target). - code_exists = ["sgt", codesize, 0] - if revert_on_failure: - ir.append( - IRnode.from_list( - ["assert", code_exists], - error_msg="empty target (create_from_blueprint)", - ) + check_codesize = ["assert", ["sgt", codesize, 0]] + ir.append( + IRnode.from_list( + check_codesize, error_msg="empty target (create_from_blueprint)" ) + ) # copy the target code into memory. # layout starting from mem_ofst: @@ -1932,9 +1924,6 @@ def _build_create_IR( ir.append(_create_ir(value, mem_ofst, length, salt, checked=revert_on_failure)) - if not revert_on_failure: - ir = ["if", code_exists, ir, 0] - return b1.resolve(b2.resolve(ir)) From 3a4b4dd9f58f9726548c33735f0c3cea969e0be9 Mon Sep 17 00:00:00 2001 From: Daniel Schiavini Date: Thu, 7 Mar 2024 18:13:13 +0100 Subject: [PATCH 3/5] Fix test --- .../builtins/codegen/test_create_functions.py | 10 ++-------- vyper/builtins/functions.py | 10 +++++----- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/tests/functional/builtins/codegen/test_create_functions.py b/tests/functional/builtins/codegen/test_create_functions.py index bb2cda702e..ce832cd3cb 100644 --- a/tests/functional/builtins/codegen/test_create_functions.py +++ b/tests/functional/builtins/codegen/test_create_functions.py @@ -497,16 +497,10 @@ def test2(target: address, salt: bytes32) -> address: # can't create2 where contract already exists if revert_on_failure is False: - assert not c.test2(c.address, salt, transact={}) + assert not c.test2(c.address, salt) else: with tx_failed(): - c.test2(c.address, salt, transact={}) - - # test single byte contract - # test2 = c.test2(b"\x01", salt) - # assert HexBytes(test2) == create2_address_of(c.address, salt, vyper_initcode(b"\x01")) - # with tx_failed(): - # c.test2(bytecode, salt) + c.test2(c.address, salt) # XXX: these various tests to check the msize allocator for diff --git a/vyper/builtins/functions.py b/vyper/builtins/functions.py index de03ef88c0..a3a7267aa1 100644 --- a/vyper/builtins/functions.py +++ b/vyper/builtins/functions.py @@ -1584,7 +1584,7 @@ def build_IR(self, expr, context): # create helper functions # generates CREATE op sequence + zero check for result -def _create_ir(value, buf, length, salt, checked=True): +def _create_ir(value, buf, length, salt, revert_on_failure=True): args = [value, buf, length] create_op = "create" if salt is not CREATE2_SENTINEL: @@ -1595,7 +1595,7 @@ def _create_ir(value, buf, length, salt, checked=True): ["seq", eval_once_check(_freshname("create_builtin")), [create_op, *args]] ) - if not checked: + if not revert_on_failure: return ret ret = clamp_nonzero(ret) @@ -1758,7 +1758,7 @@ def _build_create_IR(self, expr, args, context, value, salt, revert_on_failure): ["mstore", buf, forwarder_preamble], ["mstore", ["add", buf, preamble_length], aligned_target], ["mstore", ["add", buf, preamble_length + 20], forwarder_post], - _create_ir(value, buf, buf_len, salt=salt, checked=revert_on_failure), + _create_ir(value, buf, buf_len, salt, revert_on_failure), ] @@ -1821,7 +1821,7 @@ def _build_create_IR(self, expr, args, context, value, salt, revert_on_failure): buf = add_ofst(mem_ofst, 32 - preamble_len) buf_len = ["add", codesize, preamble_len] - ir.append(_create_ir(value, buf, buf_len, salt, checked=revert_on_failure)) + ir.append(_create_ir(value, buf, buf_len, salt, revert_on_failure)) return b1.resolve(b2.resolve(ir)) @@ -1922,7 +1922,7 @@ def _build_create_IR( length = ["add", codesize, encoded_args_len] - ir.append(_create_ir(value, mem_ofst, length, salt, checked=revert_on_failure)) + ir.append(_create_ir(value, mem_ofst, length, salt, revert_on_failure)) return b1.resolve(b2.resolve(ir)) From 1009083114e728b9d3a9a9a5fd4c62c6365d2fcb Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Sun, 7 Apr 2024 19:22:42 -0400 Subject: [PATCH 4/5] update docs --- docs/built-in-functions.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/built-in-functions.rst b/docs/built-in-functions.rst index f2f6632906..5ba6cdad8a 100644 --- a/docs/built-in-functions.rst +++ b/docs/built-in-functions.rst @@ -133,13 +133,14 @@ Vyper has three built-ins for contract creation; all three contract creation bui * Invokes constructor, requires a special "blueprint" contract to be deployed * Performs an ``EXTCODESIZE`` check to check there is code at ``target`` -.. py:function:: create_minimal_proxy_to(target: address, value: uint256 = 0[, salt: bytes32]) -> address +.. py:function:: create_minimal_proxy_to(target: address, value: uint256 = 0, revert_on_failure: bool = True, [, salt: bytes32]) -> address Deploys a small, EIP1167-compliant "minimal proxy contract" that duplicates the logic of the contract at ``target``, but has its own state since every call to ``target`` is made using ``DELEGATECALL`` to ``target``. To the end user, this should be indistinguishable from an independently deployed contract with the same code as ``target``. * ``target``: Address of the contract to proxy to * ``value``: The wei value to send to the new contract address (Optional, default 0) + * ``revert_on_failure``: If ``False``, instead of reverting in the cases below, return the null address. * ``salt``: A ``bytes32`` value utilized by the deterministic ``CREATE2`` opcode (Optional, if not supplied, ``CREATE`` is used) Returns the address of the newly created proxy contract. If the create operation fails (for instance, in the case of a ``CREATE2`` collision), execution will revert. @@ -163,12 +164,13 @@ Vyper has three built-ins for contract creation; all three contract creation bui Before version 0.3.4, this function was named ``create_forwarder_to``. -.. py:function:: create_copy_of(target: address, value: uint256 = 0[, salt: bytes32]) -> address +.. py:function:: create_copy_of(target: address, value: uint256 = 0, revert_on_failure: bool = True, [, salt: bytes32]) -> address Create a physical copy of the runtime code at ``target``. The code at ``target`` is byte-for-byte copied into a newly deployed contract. * ``target``: Address of the contract to copy * ``value``: The wei value to send to the new contract address (Optional, default 0) + * ``revert_on_failure``: If ``False``, instead of reverting in the cases below, return the null address. * ``salt``: A ``bytes32`` value utilized by the deterministic ``CREATE2`` opcode (Optional, if not supplied, ``CREATE`` is used) Returns the address of the created contract. If the create operation fails (for instance, in the case of a ``CREATE2`` collision), execution will revert. If there is no code at ``target``, execution will revert. @@ -184,7 +186,7 @@ Vyper has three built-ins for contract creation; all three contract creation bui The implementation of ``create_copy_of`` assumes that the code at ``target`` is smaller than 16MB. While this is much larger than the EIP-170 constraint of 24KB, it is a conservative size limit intended to future-proof deployer contracts in case the EIP-170 constraint is lifted. If the code at ``target`` is larger than 16MB, the behavior of ``create_copy_of`` is undefined. -.. py:function:: create_from_blueprint(target: address, *args, value: uint256 = 0, raw_args: bool = False, code_offset: int = 3, [, salt: bytes32]) -> address +.. py:function:: create_from_blueprint(target: address, *args, value: uint256 = 0, raw_args: bool = False, code_offset: int = 3, revert_on_failure: bool = True, [, salt: bytes32]) -> address Copy the code of ``target`` into memory and execute it as initcode. In other words, this operation interprets the code at ``target`` not as regular runtime code, but directly as initcode. The ``*args`` are interpreted as constructor arguments, and are ABI-encoded and included when executing the initcode. @@ -193,6 +195,7 @@ Vyper has three built-ins for contract creation; all three contract creation bui * ``value``: The wei value to send to the new contract address (Optional, default 0) * ``raw_args``: If ``True``, ``*args`` must be a single ``Bytes[...]`` argument, which will be interpreted as a raw bytes buffer to forward to the create operation (which is useful for instance, if pre- ABI-encoded data is passed in from elsewhere). (Optional, default ``False``) * ``code_offset``: The offset to start the ``EXTCODECOPY`` from (Optional, default 3) + * ``revert_on_failure``: If ``False``, instead of reverting in the cases below, return the null address. * ``salt``: A ``bytes32`` value utilized by the deterministic ``CREATE2`` opcode (Optional, if not supplied, ``CREATE`` is used) Returns the address of the created contract. If the create operation fails (for instance, in the case of a ``CREATE2`` collision), execution will revert. If ``code_offset >= target.codesize`` (ex. if there is no code at ``target``), execution will revert. From 44769c64646b0793b5c4946c933ddb4471117d93 Mon Sep 17 00:00:00 2001 From: Charles Cooper Date: Sun, 7 Apr 2024 19:28:22 -0400 Subject: [PATCH 5/5] update again --- docs/built-in-functions.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/built-in-functions.rst b/docs/built-in-functions.rst index 5ba6cdad8a..0d2ad0c9e1 100644 --- a/docs/built-in-functions.rst +++ b/docs/built-in-functions.rst @@ -133,14 +133,14 @@ Vyper has three built-ins for contract creation; all three contract creation bui * Invokes constructor, requires a special "blueprint" contract to be deployed * Performs an ``EXTCODESIZE`` check to check there is code at ``target`` -.. py:function:: create_minimal_proxy_to(target: address, value: uint256 = 0, revert_on_failure: bool = True, [, salt: bytes32]) -> address +.. py:function:: create_minimal_proxy_to(target: address, value: uint256 = 0, revert_on_failure: bool = True[, salt: bytes32]) -> address Deploys a small, EIP1167-compliant "minimal proxy contract" that duplicates the logic of the contract at ``target``, but has its own state since every call to ``target`` is made using ``DELEGATECALL`` to ``target``. To the end user, this should be indistinguishable from an independently deployed contract with the same code as ``target``. * ``target``: Address of the contract to proxy to * ``value``: The wei value to send to the new contract address (Optional, default 0) - * ``revert_on_failure``: If ``False``, instead of reverting in the cases below, return the null address. + * ``revert_on_failure``: If ``False``, instead of reverting when the create operation fails, return the null address. * ``salt``: A ``bytes32`` value utilized by the deterministic ``CREATE2`` opcode (Optional, if not supplied, ``CREATE`` is used) Returns the address of the newly created proxy contract. If the create operation fails (for instance, in the case of a ``CREATE2`` collision), execution will revert. @@ -164,13 +164,13 @@ Vyper has three built-ins for contract creation; all three contract creation bui Before version 0.3.4, this function was named ``create_forwarder_to``. -.. py:function:: create_copy_of(target: address, value: uint256 = 0, revert_on_failure: bool = True, [, salt: bytes32]) -> address +.. py:function:: create_copy_of(target: address, value: uint256 = 0, revert_on_failure: bool = True[, salt: bytes32]) -> address Create a physical copy of the runtime code at ``target``. The code at ``target`` is byte-for-byte copied into a newly deployed contract. * ``target``: Address of the contract to copy * ``value``: The wei value to send to the new contract address (Optional, default 0) - * ``revert_on_failure``: If ``False``, instead of reverting in the cases below, return the null address. + * ``revert_on_failure``: If ``False``, instead of reverting when the create operation fails, return the null address. * ``salt``: A ``bytes32`` value utilized by the deterministic ``CREATE2`` opcode (Optional, if not supplied, ``CREATE`` is used) Returns the address of the created contract. If the create operation fails (for instance, in the case of a ``CREATE2`` collision), execution will revert. If there is no code at ``target``, execution will revert. @@ -186,7 +186,7 @@ Vyper has three built-ins for contract creation; all three contract creation bui The implementation of ``create_copy_of`` assumes that the code at ``target`` is smaller than 16MB. While this is much larger than the EIP-170 constraint of 24KB, it is a conservative size limit intended to future-proof deployer contracts in case the EIP-170 constraint is lifted. If the code at ``target`` is larger than 16MB, the behavior of ``create_copy_of`` is undefined. -.. py:function:: create_from_blueprint(target: address, *args, value: uint256 = 0, raw_args: bool = False, code_offset: int = 3, revert_on_failure: bool = True, [, salt: bytes32]) -> address +.. py:function:: create_from_blueprint(target: address, *args, value: uint256 = 0, raw_args: bool = False, code_offset: int = 3, revert_on_failure: bool = True[, salt: bytes32]) -> address Copy the code of ``target`` into memory and execute it as initcode. In other words, this operation interprets the code at ``target`` not as regular runtime code, but directly as initcode. The ``*args`` are interpreted as constructor arguments, and are ABI-encoded and included when executing the initcode. @@ -195,7 +195,7 @@ Vyper has three built-ins for contract creation; all three contract creation bui * ``value``: The wei value to send to the new contract address (Optional, default 0) * ``raw_args``: If ``True``, ``*args`` must be a single ``Bytes[...]`` argument, which will be interpreted as a raw bytes buffer to forward to the create operation (which is useful for instance, if pre- ABI-encoded data is passed in from elsewhere). (Optional, default ``False``) * ``code_offset``: The offset to start the ``EXTCODECOPY`` from (Optional, default 3) - * ``revert_on_failure``: If ``False``, instead of reverting in the cases below, return the null address. + * ``revert_on_failure``: If ``False``, instead of reverting when the create operation fails, return the null address. * ``salt``: A ``bytes32`` value utilized by the deterministic ``CREATE2`` opcode (Optional, if not supplied, ``CREATE`` is used) Returns the address of the created contract. If the create operation fails (for instance, in the case of a ``CREATE2`` collision), execution will revert. If ``code_offset >= target.codesize`` (ex. if there is no code at ``target``), execution will revert.