1

I am creating a smart contract verification service as a learning experiment and I am now able to verify most of contracts that I come across etherScan (in part doing so there is not only an option).

I looked at the guys at Sourcify and wanted to do a mix between etherscan partial verification and Sourcify full verification.

Right now I'm with partial.

I'm having trouble trying to decode constructor arguments for this contract: https://etherscan.io/address/0xdE86327fBFD04C4eA11dC0F270DA6083534c2582#code

Problem

The end of the init data is

b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c65640000000000000000000000005ac4182a1dd41aeef465e40b82fd326bf66ab82c0000000000000000000000000f99738b2fc14d77308337f3e2596b63ae7bcc4a00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000

Everything but that is matching. I have been told that this is the abi encoded of the constructor params.

From the source code on remix I know the constructor is:


contract TransparentUpgradeableProxy is ERC1967Proxy {
    /**
     * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
     * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
     */
    constructor(
        address _logic,
        address admin_,
        bytes memory _data
    ) payable ERC1967Proxy(_logic, _data) {
        _changeAdmin(admin_);
    }

...

Putting it on chunks of 32 bytes:


b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103
416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c
206661696c6564 // no idea of what this is ???
0000000000000000000000005ac4182a1dd41aeef465e40b82fd326bf66ab82c // _logic address (head)
0000000000000000000000000f99738b2fc14d77308337f3e2596b63ae7bcc4a // admin_ address (head)
0000000000000000000000000000000000000000000000000000000000000060 // bytes head (starts at 96 byte, so 3rd line)
0000000000000000000000000000000000000000000000000000000000000000"// bytes tail (in this caseall 0's I guess)

I need help understanding how should I decode this.

ABI

If it helps I attach the ABI


[
  {
    inputs: [
      {
        internalType: "address",
        name: "_logic",
        type: "address",
      },
      {
        internalType: "address",
        name: "admin_",
        type: "address",
      },
      {
        internalType: "bytes",
        name: "_data",
        type: "bytes",
      },
    ],
    stateMutability: "payable",
    type: "constructor",
  },
  {
    anonymous: false,
    inputs: [
      {
        indexed: false,
        internalType: "address",
        name: "previousAdmin",
        type: "address",
      },
      {
        indexed: false,
        internalType: "address",
        name: "newAdmin",
        type: "address",
      },
    ],
    name: "AdminChanged",
    type: "event",
  },
  {
    anonymous: false,
    inputs: [
      {
        indexed: true,
        internalType: "address",
        name: "beacon",
        type: "address",
      },
    ],
    name: "BeaconUpgraded",
    type: "event",
  },
  {
    anonymous: false,
    inputs: [
      {
        indexed: true,
        internalType: "address",
        name: "implementation",
        type: "address",
      },
    ],
    name: "Upgraded",
    type: "event",
  },
  {
    stateMutability: "payable",
    type: "fallback",
  },
  {
    inputs: [
    ],
    name: "admin",
    outputs: [
      {
        internalType: "address",
        name: "admin_",
        type: "address",
      },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      {
        internalType: "address",
        name: "newAdmin",
        type: "address",
      },
    ],
    name: "changeAdmin",
    outputs: [
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
    ],
    name: "implementation",
    outputs: [
      {
        internalType: "address",
        name: "implementation_",
        type: "address",
      },
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      {
        internalType: "address",
        name: "newImplementation",
        type: "address",
      },
    ],
    name: "upgradeTo",
    outputs: [
    ],
    stateMutability: "nonpayable",
    type: "function",
  },
  {
    inputs: [
      {
        internalType: "address",
        name: "newImplementation",
        type: "address",
      },
      {
        internalType: "bytes",
        name: "data",
        type: "bytes",
      },
    ],
    name: "upgradeToAndCall",
    outputs: [
    ],
    stateMutability: "payable",
    type: "function",
  },
  {
    stateMutability: "payable",
    type: "receive",
  },
]

edit: Constructor params decoded fine.

2 Answers 2

0

In the creation bytecode, the part after the CBOR metadata (in this case a264...0033) is not necessarily the constructor arguments. The constructor arguments are appended to the compiled creation bytecode. In this case, the compiled creation bytecode should end with 206661696c6564.

There's no guarantee that the creation bytecode will end with the CBOR metadata part. The reason the creation bytecode contains the CBOR part in the first place is because at one point the creation code will copy the whole runtime bytecode to the contract's address' code. That's why you will see the runtime bytecode typically embedded inside the creation code. This however can be in any place in the code and not necessarily at the end, as seen in this example.

The part you couldn't make sense of could be anything but in this case you can see the contract has a constant called _ADMIN_SLOT which has the value 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103. This should be related to this variable. The other parts, I'm not sure where they come from.

1
  • Hi @kuzdogan thanks for your answer. I could also relate the _ADMIN_SLOT address there but I'm interested in the other bytecode. I also can't figure out what could be. Let's hope someone sees this post and brings some light to it! Commented Feb 6 at 9:56
0

In the dequanto library, I obtain the constructor parameters by determining the size of the deployment bytecode, looking for the CODESIZE opcode within constructor code.

[00]    PUSH1   80
[02]    PUSH1   40
[04]    MSTORE  
[05]    PUSH1   40
[07]    MLOAD   
[08]    PUSH3   001038
[0c]    CODESIZE

0x001038 represents the size of the deployment code excluding the constructor parameters, allowing them to be sliced out from the transaction input data.

2
  • Hey @tenbits thanks for the answer. Definitely interesting. One question does this CODESIZE opcode takes into account metadata and other auxdata? Like in my example, the CODESIZE opcode would reflect metadata plus that weird chunk of bytecode? Commented Feb 6 at 9:59
  • Yes, this is the complete size of the deployment code excluding just the parameters, so you can take the tx input data hex and slice out the deployment constructor arguments.
    – tenbits
    Commented Feb 6 at 12:20

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.