B006: EIP Conformance
Ethereum smart contract development has been around for quite a while yet only recently has it seen widespread adoption by the general developer community.
As by nature smart contracts on Ethereum are able and usually meant to interact between them, a set of guidelines have been proposed from time to time to ensure that contracts behave expectedly based on a set of desirable principles, such a contract accepting a certain type of cryptographic signature or performing a deterministic state change.
In this article I will detail certain commonly adopted EIP standards that are usually misimplemented, causing cross-contract interactions to either misbehave or fail entirely.
Sidenote: EIP vs ERC
Those two terms are usually used interchangeably although there is an important difference in their meaning. An ERC (Ethereum-Request-for-Comments) is a new proposal that is currently in a rough stage awaiting feedback from the Ethereum community as to whether it should move to the next stage.
An EIP (Ethereum-Improvement-Proposal) is a formal proposal that falls into one of several categories and relates to either a smart-contract-based or chain-based improvement for the Ethereum blockchain. All EIPs are browse-able on the official EIP website, however, ERCs are not and can be found as open issues on the EIP GitHub repository.
EIP-20: Fungible Token Standard
Almost every member of the Ethereum community is familiar with EIP-20, more commonly known as ERC-20, denoting a set of interfaces a fungible token should conform to, a set of expected state changes as well as a set of events that it should emit for every state change conducted.
The standard is one of the simplest ones, however, two particular bits of it can be found incorrectly implemented by well-known tokens such as Tether. In detail, the features referenced are the
decimals member implementation as well as how
The native currency of the Ethereum blockchain possesses 18 decimal places and as a convention, most tokens utilize that value given that it simplifies “exchanging” between the native currency’s and the token currency’s values. The EIP-20 standard denotes that the
decimals member should be represented by the
uint8 datatype but this is not always the case.
Functions that expect the
decimals() extended EIP-20 function to yield a
uint8 when it yields a
uint256 will fail to function properly and will throw as the return value is mishandled. Another aspect of the
decimals member is that most projects incorrectly assume the
decimals will never be beyond 18.
As a result, they perform unsafe subtractions or other similar unsafe mathematical operations that can lead to a significant exploit manifesting if the tokens are not vetted properly. An example of such an issue was detected during the Maple Finance C4 contest.
The EIP-20 standard denotes that both
transferFrom invocations should yield a
bool that indicates the transfer’s successful execution in case tokens want their errors to be gracefully handled instead of fatally halting execution.
The community was relatively mixed as to whether the functions should indeed yield a
bool or not and as such, multiple tokens exist that either return or do not return a value causing contracts to need to properly handle both cases.
To do so, the return value of these transfer functions should be opportunistically handled should it exist, an implementation of which exists in OpenZeppelin’s
SafeERC20 implementation. An extensive list of non-conformant EIP-20 tokens can be found here curated by David Terry and is quite useful for highlighting the validity of an EIP-20 based attack vector to clients.
EIP-712: Ethereum Data Hashing & Signing
Another commonly used EIP standard is the EIP-712 standard, a deterministic methodology for computing valid cryptographic signatures that are meant to be consumed by an application to conduct authorized state changes on behalf of another party.
Although the standard goes into a slightly technical depth, of interest is the usually ignored instructions regarding the
domainSeparator of the standard that is included here.
Cross-Chain Replay Attacks
The domain separator value is meant to differentiate signatures based on the domain they are being signed for as smart contracts may have the exact same interfaces and arguments and should be discerned against. A value that is utilized in the computation of the domain separator is the current EIP-155 chain ID, an identifier that can be dynamically retrieved to prevent replay-attacks.
Diving a bit deeper, when a blockchain splits into two concurrent ones, what is known as a hard-fork, each chain will continue off the latest storage state. This means that contracts deployed on the “base” chain will also exist as-is on the “satellite” chain. In turn, this nullifies signature replay protection if only a nonce-based system was being utilized given that the nonces will be equivalent across the two chains and a signature could be replayed between them.
To amend this, the
chainid notion was introduced that changes when a hard-fork occurs and is observable on-chain. As it is utilized in the
domainSeparator, its inclusion would prevent replay attacks from manifesting as it would invalidate cross-chain signatures.
Most projects ignore this, however, and instead compute the
domainSeparator during the
constructor of the contract.
Although hard-forks for Ethereum tend to be rare, they should still be guarded against. An exemplary implementation of the EIP-712 standard is that of OpenZeppelin, whereby they cache the
constructor computed value and re-compute it should the dynamically evaluated chain ID mismatch.
Some other issues that are not directly related to the EIP-712 standard and instead refer to the actual on-chain functions used for the standard can be found in my other article about cryptography.
The EIP guidelines are carefully curated proposals that have undergone a rigorous review process by security-minded individuals and application developers alike; each instruction within is meant to be adhered to meaningfully and should be assumed as absolute.
Most of the issues outlined in this article are a result of implementations attempting to either “optimize” or “simplify” the standards without properly heeding what consequences may arise from these actions. My personal advice is to closely inspect any EIP standard your project is meant to comply with and ensure that the instructions within are conformed to the letter.
A follow-up article will most likely be produced further down the line with regards to more EIP standards so do let me know if there are any, in particular, you would be interested in learning more about.