Pre-auditThree days of senior audit time for $1,500, credited in full toward your Zealynx audit.3 audit days, credited toward your auditSee how →
L-01·audit-finding

Gasless makers cannot invalidate unenumerated orders

Fixed · residualnfterc-1155marketplace
TL;DR

Gasless makers cannot invalidate unenumerated orders. Final disposition: Fixed · residual.

Severity
LOW
Method
MManual review
CAT.

Tracker: issue #19 · Status: Fixed · residual · Verified at: generation-d

02Section · Affected files

Affected files

src/PlakxioSettlement.sol#L575

03Section · Description

Description

incrementNonce() is the only cancellation path that invalidates every order in a maker's nonce epoch, including orders the maker cannot enumerate. However, it reads and updates nonces[msg.sender], while settlement checks nonces[order.maker].

The documented production account model uses gasless Particle MPC EOAs that do not submit transactions. cancelOrder() and cancelOrders() support this model through maker signatures that anyone can relay, but incrementNonce() has no equivalent signature-authorized path. A relayer calling it advances only the relayer's nonce and leaves the maker's orders valid.

A maker can therefore cancel every known order, but cannot invalidate unknown orders after losing client state or recovering from a signing compromise. Those orders may still settle until they expire, for up to 365 days.

04Section · Recommendations

Recommendations

Add a permissionless, signature-authorized cancel-all function. Bind the signed message to the maker, the maker's current nonce, and a deadline; support both EOA and ERC-1271 makers; and advance the maker's nonce using the existing unpredictable-jump logic.

Retain incrementNonce() for makers that can submit transactions. This adds one selector and typed-data flow without changing existing order or cancellation signatures. Test invalid signatures, expired authorizations, replay across nonce epochs, and ERC-1271 makers.

05Section · Remediation

Remediation

Fixed; a bounded residual is retained and documented.

The gasless cancel-all path stands as verified: incrementNonceFor binds the maker's current nonce, checks it before signature recovery, caps the authorization's life by MAX_AUTHORIZATION_LIFETIME, and supports an ERC-1271 maker. The jump is hashed over maker rather than msg.sender, so a deliverer cannot choose it by choosing its own address.

One property is narrower than the original fix note claimed, and the generation-d change for it is commentary rather than a new entropy source. On Base's two-second cadence every input to the jump is known once block N−1 is sealed, so the post-jump nonce can be computed roughly one block before delivery rather than only inside it. The contract comment now states that bound exactly.

We retain this as a Low residual rather than treating it as closed. It is maker self-exposure: a fresh order at the post-jump nonce still requires the maker's own signature, and a deliverer who front-runs the cancel-all gains nothing, because its effect does not depend on who delivers it. Cancel-all therefore remains a reliable statement that every order signed before it is dead, and is not a statement that the maker will not sign again immediately afterwards — a risk system that needs the latter must key on the maker's next signature. The same construction, and the same residual, applies to the holder opt-out nonce. The existing Fixed label refers to the implemented original fix; the predictability has not been eliminated.

06Section · Verification provenance

Verification provenance

Final disposition: Fixed · residual. Verified at generation-d, commit bcf18bb2926589bb8538fa8c322eabc871ff2801. This finding was assessed in the generation-d mitigation review. The report-wide scope and evidence limitations remain applicable; see the final report overview.

Status
Fixed · residual
L-01