
Loading...
Loading
Loading...
LoadingLoading audit report...

WalletGuard.ai, powered by Gestalt Labs
Findings selected for deep verification. Where possible we generated a Solidity proof-of-concept and executed it against a forked mainnet.
contracts/TempoStreamChannel.solFunction: initiateCloseLines: 294-306The analyzed contract implements a payment channel system allowing payers to open funded channels, make incremental off-chain payments via signed vouchers, and close channels through cooperative or forced mechanisms. The analysis identified 0 critical, 1 high, 6 medium, 3 low, and 2 informational findings across 13 total submissions. The most dangerous pattern is the complete absence of access control on the channel closure lifecycle: any address can initiate and finalize a close on any channel, combined with the cooperative close function's failure to enforce voucher nonce and expiry checks, creating compound exploit paths that can strip payees of earned funds. The contract is not safe for production deployment in its current state and requires significant remediation before handling user funds.
When a payment channel is closed cooperatively, the contract does not check whether the payment voucher has expired or whether it has already been invalidated by a newer payment. This means a payee could use an old, expired voucher to extract a payment from a channel that the payer believed was cancelled, or replay a previously-used payment record to collect funds twice.
Anyone on the internet, not just the payer or payee, can force any payment channel into its 1-hour closing window at any time. An attacker or a malicious payer can trigger this at a strategic moment, leaving the payee only one hour to submit all unpaid vouchers before permanently losing access to those funds.
If a payment channel is created with a zero address as the authorized signer (which the contract permits), then any malformed or completely invalid signature will be accepted as valid. An attacker could settle vouchers or close the channel without possessing any legitimate signing key.
An attacker (or a malicious payer) first calls initiateClose() on a target channel, which has no access control and succeeds unconditionally. This starts the 1-hour grace period. The payee must now race to call settle() for all outstanding vouchers within that window. If the payee is slow, congested, or the attacker times the initiation to coincide with high gas prices, the attacker then calls finalize() after the grace period expires, also with no access control. Finalization distributes funds based only on what was settled during the grace window, and the payee permanently loses any unsettled voucher value. Finding 2 (unrestricted finalize) directly amplifies Finding 1 (unrestricted initiateClose) into a complete forced-closure attack requiring zero cooperation from either channel party.
The close() function accepts a voucher that is verified against the VOUCHER_TYPEHASH (which includes nonce and expiry fields per EIP-712) but never enforces expiry or nonce ordering. A payee holding an expired high-value voucher that was never submitted through settle() can call close() with it at any time. Because close() also does not check that cumulativeAmount exceeds the already-settled amount, and because the payer's close signature uses a non-EIP-712 scheme without a nonce or chain ID, the entire cooperative close path is structurally unsound. The combination of Finding 4 and Finding 7 means a payee can use a stale voucher alongside a previously captured payer signature to close the channel and extract payment for service that was disputed or cancelled.
If openChannel() is called with authorizedSigner set to address(0) (permitted because there is no zero-address guard), then any call to settle() or close() that produces a malformed or entirely absent signature will cause _recoverSigner to return address(0). The recovered address is then compared to channel.authorizedSigner, which is also address(0), and the check passes. Combined with the unrestricted initiateClose() and finalize() access control gaps, an attacker can open a zero-signer channel, deposit funds (or wait for a victim to do so if channel IDs could be predicted), then settle arbitrary vouchers with invalid signatures and finalize the channel to extract funds.
| Agent | Status | Findings | Severity | Confidence | Duration | Coverage |
|---|---|---|---|---|---|---|
| reentrancy | success | 6 | 3M | 88% | 55.0s | Cross-function reentrancy — all external call sites analyzed for CEI pattern compliance, ERC-20/ERC-777 callback reentrancy — SafeERC20 usage verified, nonReentrant guards checked, Access control on all state-modifying functions (openChannel, addDeposit, settle, initiateClose, finalize, close), EIP-712 signature construction and replay protection in settle() and close(), Voucher nonce/expiry validation in settle() vs close(), Arithmetic overflow/underflow in deposit/settled calculations, Channel lifecycle state machine correctness, Deadline enforcement in openChannel(), Close cooperative flow and payer signature replay vectors |
| access control | success | 7 | 2M | 82% | 56.8s | Access control on all state-modifying functions (openChannel, addDeposit, settle, initiateClose, finalize, close), EIP-712 signature construction and verification in settle() and close(), ecrecover return value validation (address(0) check), Signature replay protection - nonces, chain ID, expiry, Signature malleability in _recoverSigner, Channel state machine transitions and invariants, Arithmetic overflow/underflow in settlement calculations, ReentrancyGuard usage across all fund-moving functions, Deadline parameter validation in openChannel, Cross-chain replay attack vectors in close() payer signature, Token transfer safety using SafeERC20, Channel ID collision resistance, Cooperative close vs forced close interaction, Zero-address handling for authorizedSigner |
| economic | success | 8 | 1H1L | 85% | 1.3m | Flash loan attack surface (balance-based pricing, collateral inflation), Oracle manipulation (no price feeds present, N/A), Chainlink/Pyth/Chronicle oracle integration (not present), Governance attack vectors (no governance present), Sandwich/MEV exposure on token swaps, Reward distribution manipulation, Fee-on-transfer token accounting, Reentrancy (nonReentrant guards on all state-modifying external functions except initiateClose), Access control on channel lifecycle functions, EIP-712 signature construction and replay protection, Nonce management and voucher validation, Arithmetic safety (Solidity 0.8 checked math), close() cooperative closure logic and validation gaps, Unused deadline parameter in openChannel, Grace period boundary race conditions, ecrecover return value handling (zero address check present via comparison) |
| logic validation | success | 8 | 1M2L | 87% | 1.5m | Input validation on all function parameters (payee, authorizedSigner, deposit, amount, deadline), EIP-712 domain separator caching and recomputation (OpenZeppelin EIP712.sol used correctly with lazy recompute), Signature verification in settle() and close() - ecrecover address(0) bypass, Arithmetic safety in settle() and close() - over/underflow with 0.8 protections, Access control on initiateClose() and finalize() - missing caller authorization, State machine integrity - channel lifecycle transitions (open, grace period, finalized), Reentrancy protection - nonReentrant guards on fund-transferring functions, abi.encodePacked hash collision risks - channelId generation uses address+timestamp+counter (no dynamic-type collision risk), close() payer signature non-EIP-712 encoding and replay risk, Fee-on-transfer token accounting (deposit stored as amount, not balance delta), Nonce validation in settle() - correctly requires nonce > settledNonces, Missing nonce and expiry checks in close(), Grace period state interactions with addDeposit, EIP-712 typehash correctness vs Voucher struct fields, Deadline parameter enforcement in openChannel |
| code quality | success | 9 | 85% | 1.2m | EIP-712 compliance and domain separator correctness, ECDSA signature recovery and replay protection, Access control on all state-mutating functions, Channel lifecycle state machine (open, settle, close, finalize), Integer arithmetic for deposit/settlement accounting, Reentrancy protection via nonReentrant guards, Voucher nonce ordering and expiry enforcement, Cooperative close signature scheme security, SafeERC20 usage for token transfers, Dead code / unused parameters (deadline), Storage layout and potential packing optimizations, Cross-function interaction between settle/close/finalize | |
| compiler bugs | success | 5 | 88% | 46.8s | Access control on all state-mutating functions (openChannel, addDeposit, settle, initiateClose, finalize, close), EIP-712 signature verification in settle() and close(), Reentrancy protection (nonReentrant guards present on token-transferring functions), Integer arithmetic for overflow/underflow in settle() and close(), Voucher replay protection via settledNonces mapping, Deadline enforcement in openChannel(), authorizedSigner zero-address validation, Channel finalization logic and fund distribution correctness, Cooperative close logic (close() function) for correctness of expiry, nonce, and delta checks, Compiler bug patterns: pragma ^0.8.20 is outside all affected compiler bug ranges (no findings) | |
| assembly safety | success | 6 | 1L | 86% | 1.1m | Full codepoint-by-codepoint scan for non-ASCII characters in identifiers, strings, and comments — none found, Inline assembly blocks: _recoverSigner uses assembly to extract r, s, v from signature — analyzed for correctness (correct offsets: 0x20, 0x40, 0x60), EIP-712 digest construction and signature recovery in settle, close functions, Access control on all state-mutating functions (openChannel, addDeposit, settle, initiateClose, finalize, close), Reentrancy protection via nonReentrant modifier coverage — settle, finalize, close, openChannel, addDeposit all protected; initiateClose is not (but makes no external calls, so safe), Integer arithmetic for channel accounting (deposit, settled, delta, payerRefund calculations), Voucher nonce replay protection in settle vs close, Voucher expiry validation in settle vs close, Cooperative close payer signature verification using eth_sign prefix, Channel ID uniqueness and collision resistance, Token transfer safety (SafeERC20 used correctly), Storage slot patterns — no hardcoded slot numbers, no suspicious sload/sstore, Shift operations — no shl/shr in assembly, Assembly return/leave usage — none present, Delegatecall patterns — none present, Homoglyph and RTLO character attacks — none detected, Zero-width character attacks in identifiers — none detected, DeadlineExpired error defined but unused, addDeposit allows any caller to deposit into any channel (intentional design for top-ups) |
| l2 specific | success | 6 | 88% | 46.6s | Access control on all state-modifying functions (openChannel, addDeposit, settle, initiateClose, finalize, close), EIP-712 signature verification and typehash construction, Nonce/replay protection in settle and close, Reentrancy protection (nonReentrant guards), Channel lifecycle state machine (opened -> grace -> finalized), Arithmetic safety (overflow/underflow in deposit/settled calculations), Token transfer safety (SafeERC20 usage), Cross-function interaction between settle() and close(), Voucher expiry enforcement in all settlement paths, Deadline parameter in openChannel, Payer signature construction in cooperative close, Available balance calculation correctness, Channel ID collision risks, L2/chain-specific patterns (no L2-specific imports detected; contract appears chain-agnostic) |
This automated audit has inherent limitations. The following areas are not covered.
This report is an automated point-in-time assessment and does not guarantee protection against all possible attacks. It does not cover off-chain components, economic modeling, or business logic correctness unless explicitly noted. Changes to the contract after the audit commit are not reviewed. This is not financial or legal advice. WalletGuard, powered by Gestalt Labs, provides this analysis as-is with no warranty of completeness.
[](https://walletguard.ai/audit/5c9771b4-8e60-46f1-a0e0-e65ed6cab090)
<a href="https://walletguard.ai/audit/5c9771b4-8e60-46f1-a0e0-e65ed6cab090"> <img src="https://walletguard.ai/api/badge/5c9771b4-8e60-46f1-a0e0-e65ed6cab090" alt="WalletGuard Audit Badge" /> </a>