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-06·audit-finding

Setting a market as its own treasury permanently traps revenue

Fixednfterc-1155marketplace
TL;DR

Setting a market as its own treasury permanently traps revenue. Final disposition: Fixed.

Severity
LOW
Method
MManual review
CAT.

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

02Section · Affected files

Affected files

src/QuoteTreasuryManaged.sol#L62-L79 src/PlakxioSettlement.sol#L1130-L1160 src/PlakxioSpecialMarket.sol#L371-L385

03Section · Description

Description

The shared treasury setter rejects only the zero address, so either market can be configured as its own treasury through its constructor or admin setter.

Both payment paths first pull quote tokens into the market and then transfer the fee or commission to treasury. With treasury == address(this), a standard ERC-20 self-transfer succeeds without changing the balance. The trade completes, but protocol revenue remains in a non-upgradeable contract with no withdrawal or rescue path. Changing the treasury later affects only future payments.

A controlled test left both market balances at zero with an external treasury. After changing only the treasury destination, Settlement retained 10,000,000 quote-token units and SpecialMarket retained 4,347,826; redirecting the treasury did not recover either balance. Only an administrator can create this condition, and user principal is paid normally, so the issue is Low.

04Section · Recommendations

Recommendations

Reject address(this) in the shared _setTreasury() function so the guard covers both constructors and both external setters:

solidity
if (newTreasury == address(0) || newTreasury == address(this)) revert InvalidTreasury();

Do not add a privileged rescue path solely for this case, because that would unnecessarily expand the administrator's ability to withdraw assets from the trading contracts.

05Section · Remediation

Remediation

Fixed. The earlier "partially fixed" assessment is superseded for the reviewed implementation.

The guard is now symmetric. The badge's grant side inspects the enumerable live-operator set and refuses a candidate that any live operator already names as its treasury (SettlementIsATreasury), and refuses a candidate whose own treasury is itself or a live operator (TreasuryIsASettlement). The markets' own setter continues to refuse the badge and live operators (InvalidTreasury), so the property holds in both orderings. The pairing probe is a one-word staticcall under a bounded gas cap, and a grant that cannot fund the probe is refused by name (TreasuryProbeUnfunded) rather than skipping the check — we checked the arithmetic: with twice the probe budget in the frame the probe always receives its full allowance after EIP-150, so the fail-closed property holds.

Read-only simulations from the Safe against the deployed badge, agreeing across two providers, reproduced SettlementIsATreasury for treasury-then-whitelist and InvalidTreasury for the reverse ordering, with a permitted control confirming the gate discriminates rather than refusing every grant.

Retained limits, which the contract states in its own documentation: a future operator that cannot answer treasury() within the probe budget, or whose own treasury setter does not consult the badge, is not covered by a universal pairing guarantee; an operator can temporarily obstruct the grant of the one address it names as its treasury until it is revoked; and a retired market, a token implementation behind a proxy, or any other exit-less recipient still requires operator verification. No rescue path was added, as recommended.

06Section · Verification provenance

Verification provenance

Final disposition: Fixed. 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
L-06