# Centralization Risk

> Trigger Pattern Protocol has privileged capabilities (AdminCap, OwnerCap, UpgradeCap, TreasuryCap, custom caps) - Inject Into Breadth agents (optional), depth-state-trace

- Skill: `plamentsv/centralization-risk-2` (Agent Skill)
- Install (CLI): `npx skillmds@latest add plamentsv/centralization-risk-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/plamentsv/centralization-risk-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: plamentsv (https://skillmd.com/u/plamentsv)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/plamentsv/centralization-risk-2

---


# Skill: CENTRALIZATION_RISK (Sui)

> **Trigger Pattern**: Protocol has privileged capabilities (AdminCap, OwnerCap, UpgradeCap, TreasuryCap, custom caps)
> **Inject Into**: Breadth agents (optional), depth-state-trace
> **Finding prefix**: `[CR-N]`
> **Rules referenced**: R2, R6, R9, R10, R13
> **Required**: NO (recommended when protocol has 3+ distinct privileged capability types)

Covers: single points of failure, privilege escalation, capability object management, external governance dependencies, emergency powers. On Sui, centralization risk has unique dimensions: capability objects (AdminCap) are first-class owned objects that can be transferred, UpgradeCap controls full package replacement, TreasuryCap controls token supply, and shared objects can be the target of admin-gated mutations. The ownership and lifecycle of capability objects IS the access control model.

---

## Trigger Patterns

```
AdminCap|OwnerCap|UpgradeCap|TreasuryCap|GovernanceCap|OperatorCap|PauserCap|MinterCap|
Cap\b|_cap|admin|authority|privilege
```

---

## Step 1: Capability Inventory

Enumerate ALL capability objects and ALL functions requiring capabilities:

| # | Capability Type | Module | Abilities | Created Where | Holder | What It Controls | Impact If Lost/Stolen |
|---|----------------|--------|-----------|---------------|--------|------------------|----------------------|
| 1 | {CapType} | {module} | {key, store, ...} | {init or func} | {address/shared} | {list functions} | {worst case} |

**Key question for each capability**: Is it OWNED (by a single address) or SHARED (accessible via reference in any transaction)?
- **Owned AdminCap**: Only the owner can pass it as a tx argument. Strongest access control, but single point of failure.
- **Shared config with admin field**: Admin address stored in shared object. More flexible but requires `assert!(sender == admin)` checks -- verify these are present on ALL admin functions.
- **Shared capability object**: DANGEROUS -- anyone can pass a shared `AdminCap` as a transaction argument without ownership.

**Categorize each by impact**:
- **FUND_CONTROL**: Can move, lock, or destroy user funds (e.g., emergency withdraw, treasury drain)
- **PARAMETER_CONTROL**: Can change fees, rates, thresholds, delays (e.g., set_fee, set_max_leverage)
- **OPERATIONAL_CONTROL**: Can pause, unpause, add/remove pools, whitelist/blacklist
- **UPGRADE_CONTROL**: UpgradeCap -- controls package upgrades, policy changes
- **MINT_CONTROL**: TreasuryCap -- controls token supply (mint/burn)

**Sui-specific ability checks**:
- Does the capability have `store` ability? If YES -> anyone holding it can `public_transfer` it, meaning the privilege is freely transferable. Is this intentional?
- Does the capability have `drop` ability? If YES -> it can be silently discarded. For AdminCap, dropping it means admin functions become permanently uncallable. For UpgradeCap, dropping makes the package permanently immutable.
- Is the capability created ONLY in `init()`? If created elsewhere, can unauthorized parties mint new capabilities?

---

## Step 2: Capability Hierarchy and Separation

Map the capability hierarchy:

| Capability | Created By | Can Create Other Caps? | Transferable (has `store`)? | Destructible (has `drop`)? | Timelock/Delay? |
|-----------|-----------|----------------------|---------------------------|---------------------------|-----------------|
| {cap} | {init / admin_func} | YES/NO | YES/NO | YES/NO | YES/NO |

**Check**:
- [ ] Are FUND_CONTROL and UPGRADE_CONTROL separated into different capability types?
- [ ] Does any single capability type grant both PARAMETER_CONTROL and FUND_CONTROL?
- [ ] Are capability transfers behind timelocks or governance mechanisms?
- [ ] Can capabilities be destroyed, and what happens when they are?
- [ ] Is there a master capability that can create all other capabilities?

**Sui-specific separation patterns**:
- Best practice: UpgradeCap held by governance multisig, AdminCap held by operations team, TreasuryCap held by treasury
- Anti-pattern: single `init` function creates ALL caps and transfers them to `tx_context::sender()` -- single point of failure at deployment
- Two-step transfer: propose new admin -> new admin accepts. Prevents accidental transfer to wrong address.

### UpgradeCap Analysis (CRITICAL)

| Package | UpgradeCap Holder | Upgrade Policy | Destroyed? | Risk Level |
|---------|------------------|---------------|-----------|------------|
| {package_id} | {address or description} | {compatible/additive/dep_only} | YES (immutable) / NO | {assessment} |

**Risk levels**:
- **UpgradeCap destroyed (`make_immutable`)**: No upgrade risk.
- **UpgradeCap held by governance multisig with timelock**: Low risk.
- **UpgradeCap held by multisig (no timelock)**: Low-Medium risk.
- **UpgradeCap held by single address**: **CRITICAL** risk -- one compromised key replaces entire package. All shared objects now interact with attacker code.
- **UpgradeCap stored in shared object**: Check access control carefully. If extraction is possible -> same as single address risk.

---

## Step 3: Single Points of Failure

For each capability type:

| Capability | Key Compromise Impact | Current Protection | Residual Risk |
|-----------|----------------------|-------------------|---------------|
| {cap} | {what attacker can do with it} | {multisig holder? timelock wrapper?} | {what remains} |

### Sui-Specific SPOF Analysis

| Risk | Description | Severity |
|------|-------------|----------|
| **UpgradeCap compromise** | Attacker publishes malicious upgrade. All shared objects now interact with attacker code. ALL user funds at risk. | CRITICAL if single address, HIGH if multisig without timelock |
| **AdminCap compromise** | Attacker calls admin functions: drain pools, change parameters, pause protocol. | HIGH if AdminCap controls fund extraction |
| **TreasuryCap compromise** | Attacker mints unlimited tokens, diluting all holders. | HIGH if supply-sensitive protocol |
| **AdminCap with `store`** | Holder (or compromised key) can transfer AdminCap to anyone via `public_transfer`. New holder has full admin access. | Adds transfer risk to any compromise scenario |
| **AdminCap with `drop`** | Admin can accidentally destroy the capability. Admin functions become permanently uncallable. | MEDIUM -- permanent loss of admin access (Rule 9 if admin functions needed for user fund recovery) |
| **Phantom ownership** | Capability transferred to an address nobody controls (e.g., `@0x0`). Object is permanently inaccessible -- equivalent to destroying it. | Same as destruction if holding value or needed for operations |

**Severity assessment**:
- Single address with FUND_CONTROL or UPGRADE_CONTROL -> **HIGH** (minimum)
- Multisig holds capability + timelock -> **LOW** (but document)
- UpgradeCap destroyed + no admin fund extraction -> **INFO**

---

## Step 4: External Governance Dependencies

Identify parameters or behaviors controlled by EXTERNAL governance:

| Dependency | External Entity | What They Control | Protocol Impact If Changed | Notification? |
|------------|----------------|-------------------|---------------------------|---------------|
| {dep} | {entity} | {parameter/behavior} | {impact} | YES/NO |

**Sui-specific external governance**:
- **Sui framework upgrades**: Validators upgrade `sui::*` packages via governance. Can framework changes break this protocol?
- **Oracle provider changes**: If protocol reads from oracle shared object, oracle admin can change prices, feeds, or parameters
- **DeFi protocol governance**: External pools, vaults, or DEXes may change parameters
- **Bridge governance**: Wormhole guardian set rotation, Sui Bridge committee changes
- **Dependency package upgrades**: If a dependency has active UpgradeCap, its owner can publish new versions. Our package pins to specific version at compile time, but compatible upgrades preserve types that we import.

**Check**:
- Can external governance changes break protocol invariants?
- Does the protocol have circuit breakers for external changes?
- **Does the protocol verify external package addresses at call sites?** Types from compatible-upgraded packages remain the same, but behavior may change.

---

## Step 5: Emergency Powers

Document emergency/pause capabilities:

| Emergency Function | Required Capability | What It Affects | Recovery Path | Time to Recover |
|-------------------|-------------------|-----------------|---------------|-----------------|
| {func} | {cap_type} | {scope: all operations / specific pool} | {how to resume} | {estimate} |

### Sui Emergency Patterns

| Pattern | Description | Risk |
|---------|-------------|------|
| **Global pause field** | Shared config object has `paused: bool`. All user functions check it. | Standard -- check: can users withdraw when paused? |
| **Capability destruction** | Admin destroys their own capability to "renounce" control. | Irreversible -- if needed later for recovery, funds stranded |
| **Object freeze** | Admin calls `transfer::public_freeze_object` on a config. Permanent immutability. | If done to wrong object, permanent loss of admin access |
| **Package policy tightening** | UpgradeCap holder restricts policy (compatible -> additive -> immutable). | Good for security, but irreversible. Cannot loosen policy. |

**Check**:
- [ ] Can pausing strand user funds permanently? (Rule 9 -- stranded asset severity floor: minimum MEDIUM)
- [ ] Is there a maximum pause duration or automatic unpause?
- [ ] Can users emergency-withdraw during pause?
- [ ] What happens if the PauserCap/AdminCap is lost or destroyed?
- [ ] Can the protocol be permanently bricked by destroying a critical capability?
- [ ] If no exit during pause -> apply Rule 9 (minimum MEDIUM)

---

## Output Schema

```markdown
## Finding [CR-N]: Title

**Verdict**: CONFIRMED / PARTIAL / REFUTED
**Step Execution**: check1,2,3,4,5 | skip(reason) | uncertain
**Rules Applied**: [R2:___, R6:___, R9:___, R10:___, R13:___]
**Severity**: Critical/High/Medium/Low/Info
**Location**: sources/{module}.move:LineN

**Centralization Type**: FUND_CONTROL / PARAMETER_CONTROL / OPERATIONAL_CONTROL / UPGRADE_CONTROL / MINT_CONTROL
**Affected Capability**: {cap_type}
**Mitigation Present**: {multisig / timelock / UpgradeCap destroyed / governance / NONE}

**Description**: What is wrong
**Impact**: What can happen if capability is compromised, lost, or holder acts maliciously
**Recommendation**: How to mitigate (destroy UpgradeCap, use multisig, add timelock, remove `store` ability, wrap in governance)
```

---

## Step Execution Checklist (MANDATORY)

| Step | Required | Completed? | Notes |
|------|----------|------------|-------|
| 1. Capability Inventory (all cap-gated functions) | YES | | Owned vs shared, abilities checked |
| 2. Capability Hierarchy and Separation | YES | | `store`/`drop` analysis, UpgradeCap assessment |
| 3. Single Points of Failure (per capability) | YES | | |
| 4. External Governance Dependencies | YES | | |
| 5. Emergency Powers and Recovery Paths | YES | | |

### Cross-Reference Markers

**After Step 1**: Cross-reference with ABILITY_ANALYSIS Section 4 (Capability Pattern Audit) -- capabilities with `store` enable unrestricted transfer.

**After Step 2**: If UpgradeCap held by single address -> immediate finding (minimum HIGH).

**After Step 3**: If AdminCap has `drop` and is needed for fund recovery -> Rule 9 stranded asset finding.

**After Step 5**: If no emergency withdraw exists AND pause is possible -> Rule 9 stranded asset finding.

**After Step 5**: If protocol claims trustlessness but retains UpgradeCap/AdminCap -> Rule 13 anti-normalization finding.

If any step skipped, document valid reason (N/A, no external governance, no emergency functions, single capability only).

