Early rate$2,400 of senior audit time for $500. Early members keep the rate as it climbs.$2,400 of senior audit time for $500See how →
F-2026-0016·centralization-risk

Admin renunciation is a single irreversible call, and the walled garden turns admin loss into permanent illiquidity for every holder

Fixednfterc-1155marketplace
TL;DR

Admin renunciation is a single irreversible call, and because badges can only move through whitelisted markets, losing the admin would leave every holder permanently illiquid.

Severity
LOW
Impact
LOW
Likelihood
MEDIUM
Method
MManual review
CAT.
Complexity
MEDIUM
Exploitability
LOW
02Section · Description

Description

All three contracts inherit OpenZeppelin AccessControl unmodified — PlakxioBadge directly, and PlakxioSettlement and PlakxioSpecialMarket through their shared QuoteTreasuryManaged base. DEFAULT_ADMIN_ROLE is its own admin, grantRole accepts any address with no acceptance step, and renounceRole executes in one call with only a self-confirmation:

solidity
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}

OpenZeppelin's own AccessControlDefaultAdminRules — two-step and delayed, and recommended in a comment in the very file being inherited — is available and unused.

The generic form of this is well known. What makes it worth raising here is what the walled garden does to the consequence. A handover that grants to a mistyped address and then renounces leaves PlakxioBadge with no reachable admin, so setSettlementContract can never be called again and the whitelist plus every class mask freeze permanently. Because the badge admits no wallet-to-wallet transfer and exposes no burn, every badge in every wallet becomes permanently immobile except through whichever settlement contract happened to be whitelisted at that moment — and the settlement contract has already been redeployed several times over this epic, so a replacement could never be whitelisted and a superseded one never revoked.

The same call on the other two contracts freezes feeBps, flatMinFee, the escalation parameters, initialFloor and treasury. Freezing treasury is the sharper one: it is the recovery lever for a treasury that can no longer receive the quote token, so losing it turns a bounded outage into a permanent one.

03Section · Impact

Impact

A handover that grants to a mistyped address and then renounces leaves the badge with no reachable admin. Because the badge admits no wallet-to-wallet transfer and exposes no burn, every badge in every wallet becomes permanently immobile except through whichever settlement contract happened to be whitelisted at that moment. The same call on the other two contracts freezes the fee parameters, the escalation split, the floor and the treasury address — the last of which is the recovery lever for a treasury that can no longer receive the quote token.

04Section · Recommendation

Recommendation

Replace AccessControl with AccessControlDefaultAdminRules on all three contracts:

diff
-import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
+import {AccessControlDefaultAdminRules} from "@openzeppelin/contracts/access/extensions/AccessControlDefaultAdminRules.sol";
diff
-contract PlakxioBadge is ERC1155, AccessControl {
+contract PlakxioBadge is ERC1155, AccessControlDefaultAdminRules {

with the constructor taking an initialDelay and the initial admin, replacing the current _grantRole(DEFAULT_ADMIN_ROLE, admin). This makes the default-admin transfer two-step and delayed and disables bare renounceRole for that role. supportsInterface already overrides both parents and needs its override list updated to match.

05Section · Resolution

Resolution

All three contracts now use OpenZeppelin's two-step, delayed default-admin rules, so the handover cannot complete in a single call and a mistyped grant is recoverable within the window. Verified at audit-v2.

06Section · Affected files

Affected files

  • src/PlakxioBadge.sol#L17 and src/QuoteTreasuryManaged.sol#L27 at commit 5c38893
Status
Fixed
F-2026-0016