Skip to content

Commit 95b1a05

Browse files
committed
fix: add zero-address checks for Slither missing-zero-check
- Validate pool manager, tokens, receiver, creator, implementation, execute target - Add DiscreteStakingRewards ZeroAddress error; TimeLock ZeroAddress for execute - Reduce Slither low missing-zero-check from 11 to 0 on filtered scan Covers: UniswapV4Swap/LimitOrder/FlashLoan, PaymentChannel, DiscreteStakingRewards, Proxy in Fallback, CrowdFund, MinimalProxy, TimeLock Made-with: Cursor
1 parent 1380cb4 commit 95b1a05

19 files changed

Lines changed: 85 additions & 1 deletion

PROGRESS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ A hands-on Solidity training ground based on solidity-by-example.org.
1111
- [x] **Codecov**: Upgraded `codecov/codecov-action` to v5, added root `codecov.yml` (informational checks, PR comment layout), documented installing the [Codecov GitHub App](https://github.com/marketplace/codecov) in `CI.md` for reliable uploads and PR comments.
1212
- [x] **Code Scanning / Slither workflow**: Fixed SARIF upload (use `steps.slither-clean.outputs.sarif`, `upload-sarif@v4`, `fetch-depth: 0`, remove `continue-on-error` on clean scan so failed runs do not upload empty SARIF; `crytic/slither-action@v0.4.1`). Corrected `CI.md` (hacks job = artifact only; accurate fail-on / troubleshooting).
1313
- [x] **Slither medium = 0 (non-hacks, 2026-04)**: Merged CEI-style refactors for AMMs (`ConstantProductAMM`, `ConstantSumAMM`, `StableSwapAMM` with OpenZeppelin `Math.mulDiv`), `Vault.deposit`, staking (`StakingRewards`, `DiscreteStakingRewards`); `Call` / `SendingEther` forward ETH on `receive`; `TargetContract` / `EtherReceiver` gained owner `withdrawEther` so Slither `locked-ether` clears on demos. With `--filter-paths` excluding `src/hacks/`, `lib/`, `test/`, `script/`, Slither reports **zero medium** (low / informational / optimization still exist).
14+
- [x] **Slither missing-zero-check (2026-04)**: Added zero-address validation on constructors and external entry points flagged by Slither (`UniswapV4*` pool manager + flash-loan token, `PaymentChannel` receiver, `DiscreteStakingRewards` tokens, `Fallback.Proxy` implementation, `CrowdFund` creator, `MinimalProxy` implementation, `TimeLock.execute` target), with matching `vm.expectRevert` tests.
1415

1516
### ✅ Phase 1: Project Setup
1617
- [x] Copy training documentation to repo

src/applications/MinimalProxy.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ contract MinimalProxy {
1818

1919
/// @notice Constructor sets the implementation address
2020
constructor(address _implementation) {
21+
require(_implementation != address(0), "Zero address");
2122
implementation = _implementation;
2223
}
2324

src/applications/PaymentChannel.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,14 @@ contract PaymentChannel {
3232
error ChannelAlreadyClosed();
3333
error InvalidSignature();
3434
error TransferFailed();
35+
error ZeroAddress();
3536

3637
// ─── Constructor ────────────────────────────────────────────────────
3738

3839
/// @notice Create a payment channel
3940
/// @dev The sender funds the channel by sending ETH with this constructor call.
4041
constructor(address _receiver, uint256 _duration) payable {
42+
if (_receiver == address(0)) revert ZeroAddress();
4143
sender = msg.sender;
4244
receiver = _receiver;
4345
expiration = block.timestamp + _duration;

src/applications/TimeLock.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ contract TimeLock {
3131
error TimestampNotPassed(uint256 executeTime, uint256 currentTime);
3232
error TimestampExpired(uint256 executeTime, uint256 expiryTime);
3333
error ExecutionFailed();
34+
error ZeroAddress();
3435

3536
constructor() {
3637
owner = msg.sender;
@@ -75,6 +76,8 @@ contract TimeLock {
7576
payable
7677
onlyOwner
7778
{
79+
if (target == address(0)) revert ZeroAddress();
80+
7881
bytes32 txId = getTxId(target, value, data, executeTime);
7982

8083
if (!queued[txId]) revert NotQueued(txId);

src/basic/Fallback.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ contract Proxy {
5050
address public immutable owner;
5151

5252
constructor(address _implementation) {
53+
require(_implementation != address(0), "Zero address");
5354
implementation = _implementation;
5455
owner = msg.sender;
5556
}

src/defi/CrowdFund.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ contract CrowdFund {
5757
uint256 _duration
5858
) {
5959
require(_token != address(0), "Invalid token");
60+
require(_creator != address(0), "Invalid creator");
6061
require(_goal > 0, "Invalid goal");
6162
require(_duration > 0, "Invalid duration");
6263

src/defi/DiscreteStakingRewards.sol

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,10 @@ contract DiscreteStakingRewards {
5151
error NotOwner();
5252
error NoRewards();
5353
error TransferFailed();
54+
error ZeroAddress();
5455

5556
constructor(address _stakingToken, address _rewardToken) {
57+
if (_stakingToken == address(0) || _rewardToken == address(0)) revert ZeroAddress();
5658
owner = msg.sender;
5759
stakingToken = _stakingToken;
5860
rewardToken = _rewardToken;

src/defi/UniswapV4FlashLoan.sol

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ contract UniswapV4FlashLoan {
3434

3535
/// @notice Initialize with PoolManager address
3636
constructor(address _poolManager) {
37+
require(_poolManager != address(0), "Zero address");
3738
poolManager = _poolManager;
3839
}
3940

@@ -48,6 +49,7 @@ contract UniswapV4FlashLoan {
4849
uint256 amount,
4950
bytes calldata data
5051
) external {
52+
require(token != address(0), "Zero address");
5153
// Calculate the fee
5254
uint256 fee = (amount * FLASH_LOAN_FEE_BPS) / 10000;
5355
uint256 amountToRepay = amount + fee;
@@ -105,6 +107,7 @@ contract UniswapV4FlashLoan {
105107
address callbackTarget,
106108
bytes calldata callbackData
107109
) external {
110+
require(token != address(0), "Zero address");
108111
uint256 fee = (amount * FLASH_LOAN_FEE_BPS) / 10000;
109112
uint256 amountToRepay = amount + fee;
110113

src/defi/UniswapV4LimitOrder.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ contract UniswapV4LimitOrder is ReentrancyGuard {
6262

6363
/// @notice Initialize with PoolManager address
6464
constructor(address _poolManager) {
65+
require(_poolManager != address(0), "Zero address");
6566
poolManager = _poolManager;
6667
}
6768

src/defi/UniswapV4Swap.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ contract UniswapV4Swap {
3939

4040
/// @notice Initialize the contract with a PoolManager address
4141
constructor(address _poolManager) {
42+
require(_poolManager != address(0), "Zero address");
4243
poolManager = _poolManager;
4344
}
4445

0 commit comments

Comments
 (0)