2

Context

The method that the solc compiler uses to set storage slots for a mapping is slightly different from how the Vyper compiler does so. In Vyper, it's keccak256(slotMapping, key), while in Solidity it's keccak256(key, slotMapping). Therefore, different logic is required for look-ups, depending on which compiler was used. I am trying to determine which compiler was used to generate a given compiled bytecode.


Question

Without having the contract's source code and using only the RPC method eth_getCode, is there a way to confidently determine whether the solc or Vyper compiler was used?

Constraints

I'm only interested in contracts compiled with solc or Vyper. I'm not concerned about other compilers at this moment.

1 Answer 1

2

You can use the bytecode metadata1 to determine the smart contract language and the compiler version used to compile the bytecode. Both compilers include bytecode metadata by default2.

  1. Get the runtime bytecode of a smart contract using eth_getCode.
  2. Extract the bytecode metadata by counting either n bytes (Solidity) OR n - 2 bytes (Vyper) backwards from the last two bytes of the bytecode, where the last two bytes of the bytecode are the length n.

For example, if the bytecode ends like this:

...04da03c68419056f81181800a16576797065728300030a0015

Then the metadata would be 03c68419056f81181800a16576797065728300030a if it was compiled using solc or 8419056f81181800a16576797065728300030a if it was compiled using vyper. To illustrate:

       ┌──────────────0x0015 bytes──────────────┐      Solidity
...04da03c68419056f81181800a16576797065728300030a0015
           └──────────0x0015 - 2 bytes──────────┘      Vyper

Or, in JavaScript:

const bytecode = '...04da03c68419056f81181800a16576797065728300030a0015';
const metadataLength = parseInt(bytecode.slice(-4), 16);
const solcCBOR = bytecode.substring(bytecode.length - 4 - metadataLength * 2, bytecode.length - 4);
const vyperCBOR = bytecode.substring(bytecode.length - metadataLength * 2, bytecode.length - 4);

Unfortunately, since you won't know which compiler was used until you decode the metadata, you'll likely have to try both and see which one decodes successfully.

  1. The bytecode metadata for both compilers is CBOR-encoded. You can use any CBOR decoder (online example) to decode it. Solidity encodes an object that looks like this:
{"ipfs": h'1220A454FC5E914817178F216E41C3F677683CB5B09C52970CE54C207F250B3AB7BC', "solc": h'000813'}

And Vyper encodes something like this:

[1391, [24], 0, {"vyper": [0, 3, 10]}]

In either case, the bytecode metadata includes information about the both the programming language and the compiler version used (e.g., solc v0.8.13 or vyper v0.3.10 in the examples above).

References:


1 The "bytecode metadata" (or "compiler metadata") is different from the JSON metadata that is produced from the --metadata flag. The bytecode metadata is directly encoded into the bytecode and is available on-chain (i.e., via eth_getCode) while the JSON metadata is a separate file that must be hosted somewhere off-chain.

2 A developer may configure the compiler to not include bytecode metadata using the --no-cbor-metadata flag for solc or the --no-bytecode-metadata flag for vyper.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.