Solidity: Expose chainid in the language

Created on 6 May 2020  路  6Comments  路  Source: ethereum/solidity

The chainid opcode was introduced in Istanbul and added as an opcode to inline assembly a while ago (see #7310).

It would be worth considering adding language support to it. I can imagine three options:
1) chainid() like gasleft()
2) tx.chainid
3) block.chainid

While I think 2) is nicer because less pollution of the global namespace, the EIP specifically says this property is part of the "chain configuration" of the client and not the transaction. It was discussed to make per-transaction, but that was decided against.

Option 3) may be the best considering the above, while the chainid property not being part of the block, it can only ever change on a block basis (in case of forking a chain).

Motivation: chanid is used via ERC-712 and ERC-1612.

More motivation: reduce the need for using assembly for such a feature, this also helps in improved the likelyhood of SMTChecker succeeding in more contracts. (One example is Uniswap)

breaking change language design

Most helpful comment

Let's implement it as uint block.chainid.

All 6 comments

Let's implement it as uint block.chainid.

I just noted that the assembly version introduced in https://github.com/ethereum/solidity/pull/7327 is considered pure, the following code compiles fine:

function _chainID() private pure returns (uint256) {
    uint256 chainID;
    assembly {
        chainID := chainid()
    }
    return chainID;
}

Isn't this a bug? Such a function cannot be really computed client-side: it very much depends on the node's 'state' (configuration?).

This is a good point and I think it is partly due how hastily chainid was added to the system :wink:

(Note: The stateMutability setting is not necessarily reflecting how EOAs interact with contracts (e.g. code run on client side or on chain), rather it is only reflecting whether a given function needs to read the state or needs to modify the state.)

I think we need to look at this from two angles:
1) Can it be calculated on client side.
2) Does it need any state access.

Let me answer 1) first as it is simpler: it can be calculated client side. See EIP-695 and EIP-1193 how a "modern" client should behave. Based on that I think nothing would stop a client from being capable to run it, without submitting it to the network.

And 2) is a bit more tricky, because chainid is not part of the state (or block), only part of client configuration. IIRC this was a debate at the time the EIP was added and made in such a way to avoid making forking simple. In this sense using block.chainid isn't the best, but I don't think there's any better option if we don't want another global function.

If we look at properly pure functions, then chainid should not be allowed, because that is an external variable (but as we know Solidity pure functions can depend on memory 馃槙).

@nventuro does the above give a good enough reasoning?

Yes, that seems to make sense. I'm not familiar however with clients that compure pure results without ever hitting a node (are there any?), it'd be good to check against those.

My concern about chainid specifically is around forks. A pure function that takes no arguments isn't much more than a complicated constant from the type system's point of view (barring oddities around memory being accessible). chainid however can change, and e.g. drastically affect an EIP712 signature.

The idea is that it becomes block.chainid, which would mean it is considered state accessing.

Now what to do with the opcode, I'm not sure, probably marking it view accordingly is the best option.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

chriseth picture chriseth  路  3Comments

chriseth picture chriseth  路  4Comments

axic picture axic  路  3Comments

VoR0220 picture VoR0220  路  4Comments

ddeclerck picture ddeclerck  路  3Comments