EIP-5478: CREATE2COPY Opcode


eip: 5478
title: CREATE2COPY Opcode
description: Reducing the gas cost of contract creation with existing code
author: Qi Zhou (@qizhou)
discussions-to: EIP-5478: CREATE2COPY Opcode
status: Draft
type: Standards Track
category: Core
created: 2022-08-17
requires: 1014, 2929

thatā€™s a great idea. but instead of introducing a new opcode, did you try to implement this by creating a constructor that accepts an address (and parameters) and uses extcodecopy (instead of calldata)? you will still pay for memory expansion but I think it might be cheaper still. also this works around introducing a new opcode.

Thanks for the comment. We could definitively use extcodecopy to exactly copy the code from another contract in init_code of CREATE/CREATE2, however, this means that for every byte in the code to be copied, the tx needs to pay 200 gas. For example, copying a contract with 10K bytes code will cost 200 * 10K = 2M gas!

The CREATE2COPY opcode addresses this issue by charging only 2600 (for cold account access). This would save the gas cost from 2M to 2600.

I hope this could explain. Feel free to let me know if you have further question.

(correction in the posts below)


Iā€™m not sure where you brought that 200 gas comes from.
I ran a test, please correct me if Iā€™m wrong:

PUSH1 0x40 ; len
PUSH1 0x00 ; dst offset
PUSH1 0x00 ; src offset
ADDRESS    ; addr to copy from (address(this))
EXTCODECOPY
PUSH1 0x00 ; just to verify it was copied properly
MLOAD

execute by the running the following:

evm --json --code 604060006000303c600051 run

{"pc":0,"op":96,"gas":"0x2540be400","gasCost":"0x3","memSize":0,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}

{"pc":2,"op":96,"gas":"0x2540be3fd","gasCost":"0x3","memSize":0,"stack":["0x60"],"depth":1,"refund":0,"opName":"PUSH1"}

{"pc":4,"op":96,"gas":"0x2540be3fa","gasCost":"0x3","memSize":0,"stack":["0x60","0x0"],"depth":1,"refund":0,"opName":"PUSH1"}

{"pc":6,"op":48,"gas":"0x2540be3f7","gasCost":"0x2","memSize":0,"stack":["0x60","0x0","0x0"],"depth":1,"refund":0,"opName":"ADDRESS"}

{"pc":7,"op":60,"gas":"0x2540be3f5",**"gasCost":"0x70"**,"memSize":0,"stack":["0x60","0x0","0x0","0x7265636569766572"],"depth":1,"refund":0,"opName":"EXTCODECOPY"}

{"pc":8,"op":96,"gas":"0x2540be37f","gasCost":"0x3","memSize":96,"stack":[],"depth":1,"refund":0,"opName":"PUSH1"}

{"pc":10,"op":81,"gas":"0x2540be37c","gasCost":"0x3","memSize":96,"stack":["0x0"],"depth":1,"refund":0,"opName":"MLOAD"}

{"pc":11,"op":0,"gas":"0x2540be379","gasCost":"0x0","memSize":96,"stack":["0x604060006000303c600051000000000000000000000000000000000000000000"],"depth":1,"refund":0,"opName":"STOP"}

{"output":"","gasUsed":"0x87","time":128667}

I have marked in bold the gas used by EXTCODECOPY: 0x70 for 2 words (0x40). notice that for 3 words (0x41~0x60) the gas cost is increase by 6 to 0x76.

The 200 gas per byte is from CREATE2/CREATE2, where the code here only copies the code in memory, but the code is not yet stored as a contract code. Hope this could explain.

Ah my mistake.

I understand now; I interpreted each byte to copy as the cost of EXTCODECOPY. you were talking about deposit gas price, hereā€™s a reference:

1 Like

Yes, that is exactly the EIP is optimizing for :slight_smile:

My attempt at something like this was Skinniest CREATELINK by wjmelements Ā· Pull Request #2185 Ā· ethereum/EIPs Ā· GitHub
I think there are benefits to recognizing that many contracts might have identical code.

1 Like

Agree! I think CREATE2COPY and CREATELINK can co-exist as they serve a bit different purposes:

  • CREATE2COPY can be used to create a contract with a constructor, which initializes contract storage based on ctor logic;
  • CREATELINK can be used to create a ā€œblankā€ contract. The contract may be have a initialize() method to initialize the storage such as owner, initial parameters, etc.

This EIP seems very limited due fact of popularity of ā€œimmutableā€ values, since they are part of the byte code.

1 Like

If there are ā€œimmutableā€ values, then the contract_code will be generated by init_code, which will be generally different even though most of the parts are the same. Yes, we cannot save any store cost of the contract anymore in this case.

However, it depends on which type of contract we want to copy, e.g., in the smart wallet case, if the operator wants to create millions of such smart wallet contracts with minimal storage cost, then the operator may optimize the contract so that all values are either const or in local storage.