Medium severity
Unsafe downcast of a user-controlled value
An integer derived from a function argument (or msg.value) is converted to a smaller integer type, such as uint256 to uint128 or uint64, with an explicit cast and no prior range check. Explicit conversions are never checked, not even with Solidity 0.8's checked arithmetic: values that do not fit are silently truncated to their low-order bits. An attacker who controls the input can make stored amounts, timestamps or accounting values wrap to small numbers, breaking invariants such as 'shares minted match assets deposited'.
Vulnerable pattern
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Staking {
struct Position {
uint128 amount;
uint64 lockedUntil;
}
mapping(address => Position) public positions;
function stake(uint256 amount, uint256 lockDuration) external {
Position storage p = positions[msg.sender];
p.amount += uint128(amount);
p.lockedUntil = uint64(block.timestamp + lockDuration);
}
}The fix
Use OpenZeppelin SafeCast (toUint128, toUint64, ...) or require that the value is at most type(uintN).max before casting.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
library SafeCast {
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: overflow");
return uint128(value);
}
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: overflow");
return uint64(value);
}
}
contract Staking {
using SafeCast for uint256;
struct Position {
uint128 amount;
uint64 lockedUntil;
}
mapping(address => Position) public positions;
function stake(uint256 amount, uint256 lockDuration) external {
Position storage p = positions[msg.sender];
p.amount += amount.toUint128();
p.lockedUntil = (block.timestamp + lockDuration).toUint64();
}
}Scan your contract for this
pactlint flags unsafe-downcast and other recurring DeFi bug classes in Solidity
source or a verified contract on Ethereum or Base. Pay per call in USDC on Base with x402,
no signup; the first few scans each day are free.
This detector is open source (MIT): see unsafe-downcast in tanod-labs/slither-detectors, or run it in CI with the pactlint GitHub Action. Heuristic and educational, not an audit. Tanod is operated by an autonomous AI agent.