Contract state: authorization, callbacks and accounting
Turn protocol promises into explicit invariants and test realistic local operation sequences with independent accounting.
- 01Resolve implementation
- 02Write unit-aware invariants
- 03Build actor baseline
- 04Exercise sequences
- 05Minimize state trace
At each step: retain the control → record the observation → continue only while the next boundary is understood.
Used in these playbooks#
Setup and security invariant#
Prepare a reviewed local checkout with dependencies and compiler already available. Use a private local EVM test harness; keep wallet secrets absent and review configuration for FFI, RPC endpoints and fork creation before execution. Record commit, compiler settings, proxy family, implementation, initialized roles and supported token behavior. A local reproduction against a simplified model explains an idea; an eligible report must establish that the same path exists in the relevant deployed implementation.
Write properties using explicit units: assets, shares, debt, fees and time. For example, redeeming a claim cannot pay more assets than its defined entitlement, and an ordinary actor cannot change the implementation or initialize an already initialized proxy. Do not equate token balance and share supply. Reuse the authorization matrix for actors and protected transitions. OpenZeppelin documents proxy-specific initialization and upgrade behavior, so inspect the actual family and version. OpenZeppelin proxies
Controlled procedure#
- Build realistic actors. Separate setup administrator, ordinary depositor A, depositor B and callback receiver. Setup may allocate synthetic tokens, but record each privilege and stop using administrator impersonation before the candidate sequence. Verify a normal deposit/redemption round trip and expected privileged-operation rejection.
- Trace callbacks. Mark every external call and the state visible at that instant. Give an owned callback contract one bounded reentry attempt into the original or a related function. Compare with a receiver that accepts the callback without reentry. Solidity warns that reentrancy concerns external contract calls and interacting contracts, not only Ether transfers. Solidity security considerations
- Track independent accounting. Record user assets, shares, protocol holdings, debts and fees before and after every action. Calculate the expected relationship independently of the implementation under test. For rounding, test zero supply, minimal amounts, mixed decimals, partial redemption and a direct donation, using only supported tokens. Quantify attacker cost as well as victim loss. ERC-4626 conversions and donations can expose rounding-sensitive states. OpenZeppelin ERC-4626
- Exercise ordering. Try short valid sequences: deposit → transfer shares → redeem; deposit → donate → second deposit; callback → related read/write; initialize → initialize again. Change one amount, role or order at a time. Count successful state-changing calls so an all-revert fuzz run cannot be mistaken for coverage.
- Minimize the failure. Reduce the sequence and amounts while preserving the violated property. Run the identical sequence with the benign callback or corrected authorization as a control. Preserve the earliest inconsistent state as well as the final balance difference.
Harness and command cheat sheet#
Create target-specific test functions; the names below are a suggested harness contract, not built-in Foundry scans.
| Function | Required assertion |
|---|---|
test_BaselineRoundTrip |
Expected fees and rounding explain final balances |
test_UnauthorizedUpgradeDenied |
Ordinary actor cannot alter implementation |
test_CallbackInvariant |
Bounded callback preserves the chosen property |
test_MinimizedSequence |
Replays exact candidate and checks measured effect |
git rev-parse HEAD
forge --version
rg -n 'ffi|rpc_endpoints|createFork|createSelectFork' foundry.toml test
forge test --match-test '^test_BaselineRoundTrip$' -vvvv
forge test --match-test '^test_CallbackInvariant$' -vvvv
forge test --match-test '^test_MinimizedSequence$' -vvvv
Run only after the named local tests exist and their setup has been reviewed. --match-test selects your tests; verbosity exposes local traces. These commands do not broadcast transactions, but test code can explicitly access external resources, which is why harness review matters. Foundry test reference
Evidence, false positives and completion#
Provide the implementation identity, exact harness, trace, initial capital, privileges, unit-aware balance sheet and bounded impact. Exclude unsupported malicious-token assumptions, fabricated oracle authority, admin-only behavior working as designed, and a callback that cannot occur in the real path.
- Baseline exercises the same implementation and setup.
- Actor powers match the claimed attack model.
- Successful transitions and reverts are counted.
- Loss is demonstrated independently of total value locked.
- Reproduction stays entirely in the local EVM.
Vulnerability reverse references#
- Contract callback reentrancy violates a state invariant — Nested calls observe or alter state under assumptions that do not hold while an earlier operation is incomplete.
- Contract initializer permits unauthorized ownership or configuration — A reachable initialization path lets an ordinary actor establish or replace privileged state outside the intended deployment lifecycle.
- Contract upgrade authority can be bypassed — An actor outside the upgrade policy can change a proxy’s execution target or equivalent privileged configuration.
- Vault share conversion or rounding breaks entitlement — Conversions, fees or rounding create an unintended claim on assets or an eligible loss under supported token behavior.
Nearby shared procedures#
Object, function, and property authorization · Authentication, recovery, federation, and session lifecycle · Trace input into queries, commands, and templates · Server-side fetching and network trust boundaries · Browser execution, cross-origin reads, and forged actions · Uploads, downloads, paths, archives, and XML · Workflow invariants, replay, idempotency, and races · REST, GraphQL, gRPC, WebSocket, and SSE permission parity