Vyper Bytecode Analysed

Bytecode structure

Smart contracts have two bytecodes: creation_bytecode and runtime_bytecode:

  • the creation_bytecode is the one included in the transaction that deploys the contract.
  • the runtime_bytecode is the one stored at the address of the contract, fetchable by calling the eth_getCode RCP method.

This section is referring to Vyper contracts with version >=0.4.1, below you can find a separate section containing differences for older Vyper versions.

Creation bytecode structure

  • Init section: this section includes instructions to initialize the contract, including its constructor.
  • Runtime section: this section includes the runtime bytecode, see below for more details about it.
  • Auxdata section: it includes a cbor encoded array with several information: [integrity, runtime_size, data_sizes, immutable_size, compiler]. You can read more information in the section below.
  • Constructor arguments section: appended at the end you can find all the constructor arguments

Runtime bytecode structure

  • Execution section: the opcodes related to the code of the contract
  • Data section: code locations and other metadata used for the different functions selectors the compiler can use. You can read more about this in sections 2.2.2.2 to 2.2.2.5 of this report.
  • Immutable section: constants used by the contract's code. Read more about it here

Auxdata structure

Starting Vyper version 0.4.1 and beyond, auxdata is structured as follows:

  • CBOR array: The auxdata is a CBOR-encoded array with the following components:
    1. Integrity: a fingerprint of the Vyper compilation to ensure consistency of the compiled bytecode. You can read more about it here
    2. Runtime size: The size of the compiled runtime bytecode (e.g., 168).
    3. Data sizes: An array representing the lengths of data sections (e.g., [10, 22]). See Runtime bytecode structure > Data section above.
    4. Immutable Size: The size of the immutable variables section, which is appended to the runtime bytecode on-chain (e.g., 96). See Runtime bytecode structure > Immutable section above.
    5. Compiler: A CBOR-encoded object containing the compiler version (e.g., {"vyper": [0, 4, 1]}).
  • Auxdata length: two bytes that represent the total length of the auxdata (including these two bytes).

Availability: Auxdata is included only in the creation runtime bytecode (not in the recompiled runtime bytecode).

Example

A typical Vyper auxdata in version 0.4.1 could look like this:

85582005b754c58b2e540a14aba6f16717ab2c30edc74936c8985d77b152cd97887e07188f8000a1657679706572830004010034

This decodes to:

[0x05B754C58B2E540A14ABA6F16717AB2C30EDC74936C8985D77B152CD97887E07, 143, [], 0, {"vyper": [0, 4, 1]}] + auxdata_length (0x0034 or 52 bytes)

Auxdata differences between Vyper versions

  • until 0.3.4: auxdata is appended at the end of both runtime and creation bytecode and it contains only the cbor encoded object with the vyper version. E.g. a165767970657283000304 -> {"vyper": [0, 3, 4]}.
  • >=0.3.5: like until 0.3.4 but it includes two additional bytes to keep track of the length of the auxdata cbor section. E.g. a165767970657283000308000b -> a165767970657283000308|000b -> {"vyper": [0, 3, 8]}|11.
  • >=0.3.10: auxdata is now available only in the creation runtime bytecode. There are still two bytes representing the length of the auxdata cbor, but they include also the lenght of the two bytes themselves. The cbor section is now an array composed by [runtime_size, data_sizes, immutable_size, compiler].
    E.g. 84188f8000a16576797065728300030a0012 -> 84188f8000a16576797065728300030a|0012 -> [143, [], 0, {"vyper": [0, 3, 10]}]|18
  • >=0.4.1: the cbor array now includes also, as first element, the integrity check: [integrity, runtime_size, data_sizes, immutable_size, compiler]. The integrity check is a fingerprint of a Vyper compilation, you can read more about it here.
Select a repo