Skip to content

[SC-2] Comprehensive Foundry Test Suite for FishnetWallet #32

Description

@iamyxsh

[SC-2] Comprehensive Foundry Test Suite for FishnetWallet

Labels: smart-contract, testing, priority:high, week-3-4
Assignee: Yash


Context

Per the Source of Truth (Section 10, Week 3-4 deliverable), the FishnetWallet needs a comprehensive Foundry test suite covering all permit validation paths, owner functions, and edge cases. The contract is security-critical — it gates real fund execution.


Test File

File: contracts/test/FishnetWallet.t.sol

1. Test Setup

  • Deploy FishnetWallet with test owner and test signer addresses
  • Fund wallet with test ETH (e.g., vm.deal)
  • Deploy a mock target contract (simple receive() + a state-changing function)
  • Helper function: _signPermit(FishnetPermit memory permit, uint256 signerPrivateKey) — generates valid EIP712 signature off-chain using vm.sign
  • Helper function: _buildPermit(...) — constructs permit struct with defaults

2. Happy Path Tests

  • test_executeValidPermit — sign valid permit, call execute(), verify:
    • Target contract receives the call with correct calldata
    • Target receives correct ETH value
    • Nonce is marked as used
    • ActionExecuted event emitted with correct args
  • test_executeMultiplePermitsSequentially — execute 3 permits with incrementing nonces, all succeed
  • test_executeWithZeroValue — permit with value = 0 (pure function call, no ETH transfer)
  • test_executeComplexCalldata — permit with realistic swap calldata (Uniswap-like selector + params)

3. Permit Validation Failure Tests

Each test verifies the specific revert reason:

  • test_revert_expiredPermit — set permit.expiry to block.timestamp - 1, expect revert "permit expired"
  • test_revert_usedNonce — execute once with nonce=1, try again with nonce=1, expect revert "nonce already used"
  • test_revert_targetMismatchpermit.target = addressA, call execute(addressB, ...), expect revert "target mismatch"
  • test_revert_calldataMismatchpermit.calldataHash = keccak256(dataA), call with dataB, expect revert "calldata mismatch"
  • test_revert_walletMismatchpermit.wallet = otherAddress (not this contract), expect revert "wallet mismatch"
  • test_revert_invalidSignature — sign with a random key (not fishnetSigner), expect revert "invalid signature"
  • test_revert_zeroAddressSigner — signature that recovers to address(0), expect revert

4. Pause Tests

  • test_revert_executeWhenPaused — owner pauses wallet, valid permit still reverts with "wallet paused"
  • test_executeAfterUnpause — pause, unpause, then execute succeeds
  • test_revert_pauseByNonOwner — non-owner calls pause(), expect revert "not owner"
  • test_pauseEmitsEventpause() emits Paused event
  • test_unpauseEmitsEventunpause() emits Unpaused event

5. Owner Function Tests

  • test_setSigner — owner calls setSigner(newSigner):
    • Old signer's permits no longer work
    • New signer's permits work
    • SignerUpdated event emitted
  • test_revert_setSignerByNonOwner — non-owner calls setSigner, expect revert
  • test_withdraw — owner withdraws ETH, verify balance transferred
  • test_revert_withdrawByNonOwner — non-owner calls withdraw, expect revert
  • test_receiveETH — send ETH directly to wallet, verify balance increases

6. Edge Case Tests

  • test_revert_executionFailure — target contract reverts (e.g., revert("nope")), outer call reverts with "execution failed"
  • test_executeExactExpirypermit.expiry == block.timestamp (boundary condition), should succeed
  • test_nonceOrdering — nonces don't need to be sequential (nonce=5, then nonce=2, then nonce=99 all work if unused)
  • test_reentryProtection — target contract tries to re-enter execute() during callback, verify behavior (should fail due to nonce already used)
  • test_largeNoncenonce = type(uint256).max, verify it works
  • test_chainIdMismatch — permit signed with wrong chainId, signature recovery produces wrong address, expect revert

7. Gas Benchmarks

  • test_gasExecute — measure gas for a standard execute call, log it
  • test_gasDeniedPermit — measure gas for a denied (expired) permit, log it
  • Ensure execute cost is reasonable (target: < 80k gas excluding target call)

Acceptance Criteria

  • All tests pass with forge test -vvv
  • Every revert path has a dedicated test verifying the exact error message
  • No test relies on hardcoded block numbers or timestamps (use vm.warp, vm.roll)
  • Happy path tests verify both state changes and events
  • Edge cases cover boundary conditions (exact expiry, max nonce, zero value)
  • Gas benchmarks documented for core operations

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions