👓Examples
Understanding the Examples: In the "Examples" section, we delve into practical instances of front-running vulnerabilities unearthed from actual bug bounty reports, Code Arena analyses, and more. By studying these real-life cases, you'll gain a deeper, more tangible understanding of unsafe casting and its potential impact, enhancing your auditing skills in a practical and impactful way.
Example 1: Immutable DOMAIN_SEPARATOR With Stored ChainId
Real-life Bug Find With Payout
Consider a system where the domain separator in a contract following EIP-712 is calculated at the contract initialization and then stored in an immutable variable. This variable is used in further operations like message signing. The issue arises if a hard fork happens after the contract deployment. In the event of a fork, the block.chainid
changes, but the stored domain separator remains the same on both chains, making it invalid on the forked chain.
In the constructor, domainSeparator
is calculated using block.chainid
at the time of deployment and stored in an immutable variable.
Recommended Mitigation Steps:
Dynamically calculate the domain separator to take into account the possible changes of block.chainid
. This can be done by adding a function that returns the domain separator, calculated with the current block.chainid
.
function getDomainSeparator() public view returns (bytes32) {
return keccak256( abi.encode( EIP712DOMAIN_TYPEHASH, name, version, block.chainid, address(this) ) );
}
Example 2: Static Domain Separator in EIP-2612 Implementation
Real-life Bug Find With Payout
EIP-2612 permit
Consider an ERC-20 token that implements the EIP-2612 permit mechanism. The contract calculates the chainid during its execution in the constructor and permanently stores it in an immutable variable. If Ethereum forks in the future, the block.chainid
will change but the stored chainid used for permits will not, thus breaking the token functionality on the forked chain.
The issue here is that chainid
is being calculated and stored during contract initialization, and it is not updated in the event of a chain split.
Recommended Mitigation Steps:
The chainid should be calculated dynamically on each permit invocation. As a gas optimization, the pre-calculated hash for the permits can be stored to an immutable variable, and a validation can occur on the permit function that ensures the current chainid is equal to the one of the cached hash. If not, it should be recalculated on the spot.
Last updated