Security review

ArcFlowLocks + ArcFlowVesting · internal review + forge test suite · Arc Mainnet

Full disclosure: this review was produced by the Arcflow team with AI-assisted line-by-line analysis and an adversarial re-read, backed by 64 passing Foundry tests including fuzzing, reentrancy attackers and Arc-specific native value revert scenarios. It is not a third-party audit. There are no open critical, high or medium findings. Treat the contracts as new software and size positions accordingly.

0 critical · 0 high · 0 medium · 1 low · 5 info

  • CCRinfoContract centralization risk

    Owner can set the flat fee (hard-capped at 1 USDC), change the fee recipient and withdraw collected fees. There is no pause, upgrade or sweep — never any power over user escrow. Ownership transfer is two-step.

    Status: Acknowledged

  • AOHinfoAppend-only ownership history

    locksOf(account) returns every id ever owned by an account, including locks transferred away. Callers must filter on getLock(id).owner. View-only; the UI does this and a paginated variant exists for large histories.

    Status: Acknowledged

  • FOTinfoFee-on-transfer tokens

    Deposits measure the escrow balance delta, so a lock records what actually arrived rather than the requested amount. Tokens that rebase or charge fees on outbound transfers may deliver less on withdrawal — a property of the token, not the escrow.

    Status: Acknowledged

  • NVRlowArc native value rules

    On Arc a native USDC send can revert at runtime (blocklisted or self-destructed recipient). Withdrawals bubble the failure, roll back state and keep the lock claimable; the owner can transfer the lock to a different address and withdraw from there.

    Status: Acknowledged

  • TSNinfoNon-strict block timestamps

    Arc block timestamps are non-decreasing but not strictly increasing. The contract only relies on >= comparisons against unlockTime, which is unaffected.

    Status: Acknowledged

  • MDRinfoMaximum duration

    Locks and vesting schedules are capped at 10 years from creation/extension to prevent accidental permanent escrow from mistyped timestamps.

    Status: Acknowledged

0 critical · 0 high · 0 medium · 0 low · 4 info

Shares fee, ownership, reentrancy and transfer plumbing with ArcFlowLocks via ArcFlowBase, so the CCR, FOT, NVR and TSN findings above apply identically.

  • CXLinfoCancel semantics

    Only the sender can cancel, and only if the schedule was created cancelable. Cancel freezes total to the vested amount at that block and refunds the remainder to the sender immediately. The recipient's vested-but-unclaimed balance stays claimable forever — cancel never pushes funds to the recipient, so a blocklisted recipient cannot block the sender's refund.

    Status: Acknowledged

  • BKDinfoBackdated start

    start may be in the past, in which case the already-elapsed portion vests at creation. This is intentional for migrating off-chain agreements. The UI shows the immediately claimable amount before signing.

    Status: Acknowledged

  • RNDinfoInteger rounding

    Linear vesting uses total * elapsed / duration with floor division; at most (duration - 1) wei of dust is delayed until end, never lost. Fuzz tests assert conservation: sender refund + recipient payouts == deposit.

    Status: Acknowledged

  • RCPinfoRecipient transfer

    Only the current recipient can hand a schedule to a new address; the sender can never redirect funds. schedulesAsRecipient is append-only history and is filtered client-side on the current recipient.

    Status: Acknowledged

Verified invariants

  • Withdrawals never exceed a lock's recorded deposit; each lock pays out at most once.
  • Only the current lock owner can withdraw, extend or transfer; withdrawal requires block.timestamp >= unlockTime.
  • unlockTime is monotonic: it can only ever increase.
  • Vested amount is monotonic in time and never exceeds total; recipient payouts + sender refund == deposit.
  • Nothing is claimable before the cliff; after cancel no further accrual happens.
  • withdrawFees moves only collectedFees (native fees accrued on creation) and never touches totalLocked escrow.
  • All fund-moving functions are reentrancy-guarded and follow checks-effects-interactions.
  • Stray native value sent to either contract is rejected; all deposits go through explicit create functions.

Arc-specific considerations

  • USDC as gas. Native USDC has 18 decimals; the ERC-20 interface at 0x3600…0000 has 6. The contracts store raw units per token and never mix the two.
  • 20 gwei minimum base fee. The frontend clamps maxFeePerGas so transactions are never silently dropped.
  • Deterministic finality. One confirmation is final; the UI treats the first receipt as settled.