What we'd have flagged this week, and why bridges keep paying for the same bug from 2022.
A weekly view from inside the audit queue: one finding worth your attention, one exploit autopsied at the line-of-code level, a tooling note, what we published on the blog this week, and an Academy update.
Your wallet UX is now an audit deliverable.
When the Ethereum Foundation shipped Clear Signing on May 12, the framing was deliberate. From the Foundation's own post: across major exploits in crypto, the final step is often not a bug in code, it's a user approving a transaction they could not read. The post calls out the Bybit incident as the canonical example. The public record on Bybit (Lazarus, February 2025, ~$1.5B in ETH) is exactly the failure mode Clear Signing is built to remove: a multisig confirming a transaction whose true effect was masked by raw hex.
The standard, built on ERC-7730 (Ledger's 2024 descriptor proposal) plus ERC-8176 (the attestation framework), turns each user-facing function into a JSON descriptor the wallet renders as plain language: which assets are moving, who is receiving them, what permissions are being granted. Trezor's CTO Tomáš Sušánka has put a date on it: transaction decoding early in Q2 2026, full human-readable signing by end of Q2 (June 30). Ledger, MetaMask, Fireblocks, and the One Trillion Dollar Security Initiative are in the working group.
Here is what changes in the engagement, starting now. The findings we have started raising on every Solidity review from this week onward include the following.
H-severity: missing ERC-7730 descriptor for user-facing function. Any public or external function callable by an EOA without a registered descriptor is a function the user cannot read before they sign. Where the function moves value, approves spend, or grants permissions, the absence of a descriptor is no longer a UX gap. It is a documented contributing factor to the most expensive exploits in the industry. Remediation: ship a descriptor file as part of the deploy artifact, registered in the Ethereum Foundation's public registry, attested per ERC-8176.
M-severity: descriptor present, but ambiguous. Common pattern we are catching: descriptors that say "approve token" without naming the spender contract, or "stake" without disclosing the unbonding period. The wallet renders the descriptor verbatim. If it reads like marketing, the signer learns nothing the hex didn't tell them. Remediation: write the descriptor as though a user with no knowledge of your protocol will read it under time pressure, because they will.
This is the kind of finding that did not exist as a category two weeks ago. It exists now. Scope your next engagement accordingly.
Verus-Ethereum bridge, $11.58M, ten lines short of safe.
What the bridge verified correctly
Per Blockaid's published post-incident analysis, the Verus-Ethereum bridge ran a notarization protocol. The Ethereum-side contract correctly checked that incoming state roots from Verus were signed by at least 8 of 15 notary keys. All signatures in the attack transaction were cryptographically valid. The notary set was not compromised. The signature scheme was not broken. From a smart-contract security view in isolation, the verification logic worked.
What the bridge did not verify
According to Blockaid, the contract did not check that the source-chain transaction the state root committed to actually locked the assets it was authorizing the Ethereum side to release. Blockaid's analysis names the specific function: checkCCEValues, which did not validate source-side amounts against destination payouts. The attacker built a Verus transaction spending ~$10 in fees that committed to a payout blob with empty source-side totals. Verus included the transaction in its ledger. The notaries signed the resulting state root. The Ethereum contract verified the signatures, accepted the payout instruction, and released the reserves.
In pseudocode (illustrative, not the actual Verus contract source), the bug reduces to the missing line:
function releasePayout(bytes32 stateRoot, Payout calldata p, bytes[] calldata sigs) external { // ✓ correctly verified require(verifyNotarySignatures(stateRoot, sigs), "bad sigs"); require(stateRootIncludes(stateRoot, p), "not in root"); // ✗ missing: the line that ties source value to destination payout require(p.sourceLockedValue == p.destinationReleaseValue, "value mismatch"); token.transfer(p.recipient, p.destinationReleaseValue); }
That single equality check, plus the upstream commitment ensuring sourceLockedValue reflects assets actually escrowed on Verus, is the entire fix. Blockaid's public estimate: roughly ten lines of code.
The timing is worth noting. Verus had pushed an "urgent and mandatory" emergency update two days before this exploit, patching a different vulnerability. The team was actively touching this codebase. The cross-domain binding check was not the bug they were looking for, which is exactly the kind of bug you find when you fuzz the boundary rather than the function.
The test we would have written
In an engagement, the property under test for any bridge is not "do signatures verify." It is the cross-domain invariant: for every wei released on the destination, an equal amount of value is escrowed on the source at the moment of release. That is a Foundry invariant test, not a unit test. The handler fuzzes payout blobs across both sides:
function invariant_destinationNeverExceedsSource() public { assertLe( totalReleasedOnDestination, totalEscrowedOnSource ); }
An invariant test that fails on Payout({ sourceLockedValue: 0, destinationReleaseValue: 1000 ether }) on the first round of fuzzing is the entire engagement for a bridge. Anything else is decoration.
Why this is the same class as Wormhole and Nomad
Wormhole (Feb 2022, $326M) accepted forged "guardian" signatures because the verification function defaulted-true on an empty sysvar account. Nomad (Aug 2022, $190M) accepted any message whose merkle root was a zero-hash because the proven message root mapping was initialized to zero. Both were source-destination binding failures, dressed up as different bugs. Verus is the third instance of the same category in four years. The lesson is that bridges are not single-system audits. They are two-system audits with a contract in the middle, and the property the contract has to enforce is the equivalence between the two sides.
Postscript (May 22)
Four days after the drain, the attacker returned 4,052 ETH (~$8.5M, 75% of the haul) to the Verus team in a negotiated settlement, keeping 1,350 ETH (~$2.8M) as a "bounty" in exchange for the team standing down on charges and investigation. PeckShield confirmed the transfer. It does not change the engineering lesson. The bug class is still the bug class, the missing line of code is still the missing line of code, and the next bridge to ship without the cross-domain invariant test will not be able to negotiate its way out of the post-mortem.
Cross-system invariants with Krait + Foundry.
We have been shipping Krait as a public set of Claude Code skills for Solidity security review. The relevant skill this week is the invariant-property scaffolder. Point it at a contract that exposes cross-domain state (bridges, vaults with off-chain price feeds, restaking primitives, anything with a "trust me, this matches" boundary) and it generates a Foundry invariant test harness with the binding property pre-declared, the handler skeleton, and the failing-trace shrinker pre-wired.
For Verus-class bugs, the workflow is three commands:
$ krait scaffold-invariants --contract Bridge.sol --boundary cross-domain $ forge test --match-contract BridgeInvariants -vvv $ krait explain-failure ./out/failure.trace
The boundary tag tells Krait to scaffold the "for every output, an equivalent input" invariant rather than the per-function pre/post conditions you would write for a closed system. Krait holds 90% precision across 40 blind Code4rena contests on these patterns, at zero API cost because it runs as a Claude Code skill on the engineer's machine. We use it on every engagement that touches a bridge, a restaking adapter, or an oracle.
If you ship bridge code and you are not running invariant tests with explicit cross-domain binding properties, the Verus post-mortem is the regression test you do not want to find out you needed.
What we published this week.
- AuditWhat smart contract audits actually cost in 2026. The real numbers: auditor-day pricing, what moves a quote up or down by 5x, and the pricing games most firms play with founders who don't know what to ask. Companion piece to the YouTube video.
- AuditThe hidden ROI of smart contract audits: gas, market cap, and the exchange-listing gate. Why "audit ROI" in 2026 manifests as three gatekeeper approvals (Tier-1 listings, institutional capital, insurance capacity), and the 70/30 budget split that survived our last twelve engagements.
- MethodologyDivide-and-conquer auditing: breaking Uniswap V2 into bug-finding units. The methodology elite auditors use to decompose monolithic protocols into isolated modules, verify each component's invariants independently, then stress-test the seams. Live case study on V2.
- SolanaSolana audit guide 2026: Firedancer, Token-2022, durable nonces. The four-part checklist that addresses the post-Firedancer multi-client environment, transfer-hook acyclicity, skip-vote resilience, and the pre-signed durable-nonce attack vector that drained Drift for $285M in April.
Build the protocols you'll one day audit.
Zealynx Academy is the free, gamified platform where builders write production DeFi protocols line by line, then audit real forks in the Shadow Arena. Two modules are live, both modernized to Solidity 0.8.x:
- LiveUniswap V2 — 16 sections. The full AMM from scratch: Factory, Pair, Router, Library. Swap math, fee accounting, LP token logic, the price-oracle pattern, fee-on-transfer support. Starter scaffolding provided, you write the function bodies, automated tests grade your work.
- LiveCompound V2 — 18 sections. Lending protocol from scratch: cToken accounting, interest-rate models, collateral factors, liquidation paths. The module where most builders first encounter the gap between "the math is right" and "the implementation is safe."
Fully free. No paywall, no premium tier, no credit card. You earn Lynx points and ranks as you progress.
Alongside Clear Signing, the Ethereum Foundation rolled out a $1M audit subsidy program for open-source projects the same day. If you'd rather have Zealynx cover the audit directly, our Round 02 grants are open below.
Notes from inside an audit firm, every Tuesday.
One finding from a real engagement, one exploit autopsied like we would in a kickoff call, a tooling note, and what Zealynx shipped this week. Built for the engineers who will own the fix.