--- name: "ref-lifecycle" description: "Type Thought-template (instantiate before use) - Trigger Pattern Always (Aptos Move) -- ConstructorRef/TransferRef/MintRef/BurnRef lifecycle" --- # Skill: Reference Lifecycle Analysis > **Type**: Thought-template (instantiate before use) > **Trigger Pattern**: Always (Aptos Move) -- ConstructorRef/TransferRef/MintRef/BurnRef lifecycle > **Inject Into**: Breadth agents, depth-state-trace, depth-token-flow > **Research basis**: Aptos Object model capability-based access control, permanent reference semantics ## Background In Aptos Move, object capabilities (Refs) are unforgeable tokens that grant specific permissions over objects. Unlike role-based access control in EVM, Refs are permanent once created -- they CANNOT be revoked. A leaked or improperly stored Ref grants permanent capability to its holder. Key Ref types: - **ConstructorRef**: Created once during `object::create_*`. Parent of all other Refs. Grants ability to generate TransferRef, MintRef, BurnRef, DeleteRef, and ExtendRef. - **TransferRef**: Grants ability to transfer an object even when its `TransferRef` is frozen. Bypasses `ungated_transfer` restrictions. - **MintRef**: Grants ability to mint FungibleAsset. Unlimited minting if held. - **BurnRef**: Grants ability to burn FungibleAsset from any FungibleStore. - **DeleteRef**: Grants ability to delete an object. - **ExtendRef**: Grants ability to generate a signer for the object, enabling further resource manipulation. ## Trigger Patterns ``` ConstructorRef|TransferRef|MintRef|BurnRef|DeleteRef|ExtendRef| object::create_named_object|object::create_sticky_object|object::create_object| fungible_asset::generate_mint_ref|fungible_asset::generate_burn_ref| fungible_asset::generate_transfer_ref|object::generate_delete_ref| object::generate_extend_ref|object::generate_transfer_ref ``` ## Reasoning Template ### Step 1: Reference Inventory Enumerate ALL Ref types found in the codebase. For each: | Ref Type | Created In (module::function) | Stored Location | Access Control | Capability Granted | |----------|-------------------------------|-----------------|----------------|--------------------| | ConstructorRef | {module}::{init_fn} | {consumed / stored in resource} | {who can access} | Generate all other Refs | | MintRef | {module}::{init_fn} | {global resource at @addr} | {who can access} | Unlimited minting of {asset} | | BurnRef | {module}::{init_fn} | {global resource at @addr} | {who can access} | Burn {asset} from any store | | TransferRef | {module}::{init_fn} | {global resource at @addr} | {who can access} | Transfer {asset} bypassing freeze | | DeleteRef | {module}::{init_fn} | {global resource at @addr} | {who can access} | Delete {object} | | ExtendRef | {module}::{init_fn} | {global resource at @addr} | {who can access} | Generate signer for {object} | **Completeness check**: Search for ALL `generate_*_ref` calls and `object::create_*` calls. Every Ref created MUST appear in the table. ### Step 2: ConstructorRef Analysis The ConstructorRef is the root capability. It exists only during the `init_module` or object creation call. **Check 2a: Is ConstructorRef stored?** - Search for any struct field of type `ConstructorRef` -- this type has `drop` but NOT `store`, so it CANNOT be stored in global storage directly. - If code attempts to extract a signer from ConstructorRef via `object::generate_signer(&constructor_ref)` and stores the signer reference indirectly, trace what that signer can do. - **Expected pattern**: ConstructorRef is consumed during init to generate other Refs, then dropped. It should NOT persist beyond the creation transaction. **Check 2b: What Refs are generated from it?** - List every `generate_*_ref` call that uses this ConstructorRef - For each generated Ref: is it stored with appropriate access control? - **FINDING trigger**: If ConstructorRef generates MintRef AND that MintRef is stored with `public` visibility or weak access control -> unlimited minting capability leak. **Check 2c: ExtendRef derived signer** - If `object::generate_extend_ref` is called, the resulting ExtendRef can later produce a signer via `object::generate_signer_for_extending` - Trace ALL uses of this derived signer -- it can move resources, modify object state, and call `move_to`/`move_from` - **FINDING trigger**: If ExtendRef is stored with weaker access control than the operations its signer can perform. ### Step 3: MintRef / BurnRef Analysis **Check 3a: Storage access control** - Where is MintRef stored? (must be in a resource at a controlled address) - Who can call functions that borrow the MintRef? (check `acquires` and signer requirements) - Is there any `public fun` that exposes MintRef via return value or mutable reference? - **FINDING trigger**: `public fun` returning `&MintRef` or `&mut MintRef` = capability leak to any module. **Check 3b: Mint amount validation** - Does the minting function validate the amount? (cap, rate limit, per-epoch limit) - Is there a supply cap enforced? (`fungible_asset::supply` check before mint) - **FINDING trigger**: Unlimited minting with no cap = inflation vulnerability. **Check 3c: BurnRef scope** - `fungible_asset::burn_from` with a BurnRef can burn tokens from ANY FungibleStore - Check: does the burn function require the store owner's authorization, or only the BurnRef? - **FINDING trigger**: If BurnRef holder can burn from arbitrary user stores without authorization. **Check 3d: Mint/Burn symmetry** - If protocol has both MintRef and BurnRef: are they held by the same entity? - Can one be used without the other? (mint without ability to burn = permanent inflation; burn without mint = permanent deflation) - Are there economic invariants that depend on mint/burn balance? ### Step 4: TransferRef Analysis **Check 4a: Freeze bypass** - `fungible_asset::transfer_with_ref` bypasses frozen store checks - If the protocol uses `fungible_asset::set_frozen_flag` for compliance/security: does a stored TransferRef undermine the freeze? - **FINDING trigger**: TransferRef stored alongside freeze functionality = freeze can always be bypassed by TransferRef holder. **Check 4b: Transfer direction** - Can TransferRef be used to transfer FROM any store (withdrawal) or only TO (deposit)? - `fungible_asset::transfer_with_ref` takes `from: Object` and `to: Object` -- the holder controls BOTH ends - **FINDING trigger**: TransferRef holder can drain any FungibleStore of the associated asset. **Check 4c: Who holds TransferRef?** - If protocol stores TransferRef: who can invoke the transfer function? - Is there a path where an external caller (not admin) can trigger a TransferRef-backed transfer? - Trace all call paths from `public entry fun` to the `transfer_with_ref` invocation. ### Step 5: DeleteRef Analysis **Check 5a: Resource cleanup before deletion** - If `object::delete(delete_ref)` is called, what happens to resources stored at the object address? - Move does NOT automatically clean up resources when an object is deleted -- resources become orphaned - **FINDING trigger**: Object deletion without prior `move_from` of all resources = stranded assets (Rule 9: minimum MEDIUM). **Check 5b: Deletion authorization** - Who holds the DeleteRef? Can they delete an object that other users depend on? - Is there a dependency check before deletion? (e.g., are there outstanding balances, active positions?) - **FINDING trigger**: DeleteRef holder can delete a shared object (LP pool, vault) = griefing or fund loss. ### Step 6: Ref Leakage Path Analysis **Check 6a: Public function returns** - Search for ANY `public fun` or `public(friend) fun` that returns a Ref type - Even `&MintRef` (immutable reference) leak is dangerous because it can be passed to `fungible_asset::mint` within the same transaction - **Leakage severity**: `public fun` returning Ref = Critical leak (any module can use). `public(friend) fun` returning Ref = Medium leak (friend modules can use -- check friend list). **Check 6b: Friend module exposure** - List all `friend` declarations in modules that store Refs - For each friend module: does it re-export the Ref or expose a `public fun` that uses the Ref without additional access control? - **Transitive leak**: Module A stores MintRef, Module B is friend of A and gets MintRef access, Module B has `public fun` that calls Module A's mint function = any module can mint via Module B. **Check 6c: Store ability check** - Refs with `store` ability can be placed in arbitrary global storage locations - Check: do any Ref types in the protocol have `store`? (standard Aptos Refs: ConstructorRef has `drop` only; MintRef/BurnRef/TransferRef/DeleteRef/ExtendRef have `drop` and `store`) - **FINDING trigger**: Ref with `store` ability placed in a resource with weak access control = Ref can migrate to uncontrolled storage. ### Step 7: Ref Revocation Assessment **Critical fact**: Aptos Refs CANNOT be revoked once created. There is no `revoke_mint_ref` function in the framework. **Check 7a: Maximum blast radius** - For each stored Ref: what is the maximum damage if the Ref holder is compromised? - Document: if MintRef is compromised -> unlimited inflation. If TransferRef is compromised -> drain all stores. If ExtendRef is compromised -> arbitrary object state modification. **Check 7b: Compensating controls** - Since Refs cannot be revoked, does the protocol have compensating controls? - Pause mechanism that blocks functions using the Ref? - Multi-signer requirement before Ref-backed operations? - Rate limiting on Ref-backed operations? - **FINDING trigger**: No compensating controls on a stored Ref with high blast radius = single point of failure. **Check 7c: Module upgrade path** - If the module is upgradeable (`compatible` or `immutable` policy?): can an upgrade change who accesses the Ref? - If the module is immutable: the Ref access pattern is permanent -- any vulnerability is permanent. ### Step 8: Cross-Ref Interaction **Check 8a: Ref combination attacks** - Can MintRef + TransferRef be combined? (Mint tokens, then force-transfer them to a target store) - Can BurnRef + TransferRef be combined? (Transfer tokens from victim store, then burn the evidence) - Can ExtendRef + any other Ref be combined? (Generate signer to bypass access control, then use Ref) **Check 8b: Ref holder alignment** - Are all Refs held by the same entity? If different entities hold different Refs, model adversarial interaction. - Example: Admin holds MintRef, Operator holds TransferRef. If Operator is compromised, they can drain stores. Admin cannot revoke TransferRef. --- ## Finding Template ```markdown ## Finding [{PREFIX}-N]: {Title} **Verdict**: CONFIRMED / PARTIAL / REFUTED / CONTESTED **Step Execution**: {see checklist below} **Ref Type**: {ConstructorRef / MintRef / BurnRef / TransferRef / DeleteRef / ExtendRef} **Severity**: {Critical/High/Medium/Low/Info} **Location**: {SourceFile:LineN} **Description**: {What capability is exposed and how} **Impact**: {What an attacker/compromised entity can do with the Ref} **Evidence**: {Code showing Ref creation, storage, and access path} ### Blast Radius - **If compromised**: {Maximum damage description} - **Revocable**: NO (Aptos Refs are permanent) - **Compensating controls**: {pause/multisig/rate-limit or NONE} ``` --- ## Step Execution Checklist (MANDATORY) | Step | Required | Completed? | Notes | |------|----------|------------|-------| | 1. Reference Inventory | YES | Y/N/? | Must enumerate ALL Refs | | 2. ConstructorRef Analysis | YES | Y/N/? | | | 3. MintRef/BurnRef Analysis | IF present | Y/N(none)/? | | | 4. TransferRef Analysis | IF present | Y/N(none)/? | | | 5. DeleteRef Analysis | IF present | Y/N(none)/? | | | 6. Ref Leakage Path Analysis | YES | Y/N/? | Check public returns + friends | | 7. Ref Revocation Assessment | YES | Y/N/? | Always: Refs are permanent | | 8. Cross-Ref Interaction | IF 2+ Ref types | Y/N(single)/? | | ### Output Format for Step Execution ```markdown **Step Execution**: check1,2,3,4,6,7,8 | x5(no DeleteRef) ``` OR if incomplete: ```markdown **Step Execution**: check1,2,3 | ?4,6,7(TransferRef not fully traced) **FLAG**: Incomplete analysis -- requires depth review (leakage paths not exhausted) ``` ## Instantiation Parameters ``` {CONTRACTS} -- Move modules to analyze {ASSET_NAME} -- Primary fungible asset name {REF_STORAGE} -- Where Refs are stored (resource name and address) {ACCESS_CONTROL} -- Who can access stored Refs (signer requirements) {FRIEND_MODULES} -- Modules declared as friends {FREEZE_USED} -- Whether protocol uses freeze functionality (YES/NO) {UPGRADE_POLICY} -- Module upgrade policy (compatible/immutable) ``` ## Output Schema | Field | Required | Description | |-------|----------|-------------| | ref_inventory | yes | Complete table of all Refs in codebase | | constructor_ref_analysis | yes | ConstructorRef lifecycle and consumption | | mint_burn_analysis | if present | MintRef/BurnRef storage and access control | | transfer_ref_analysis | if present | TransferRef and freeze bypass potential | | delete_ref_analysis | if present | DeleteRef and resource cleanup | | leakage_paths | yes | Public/friend exposure of Refs | | revocation_assessment | yes | Blast radius and compensating controls | | cross_ref_interactions | if 2+ types | Combined Ref attack scenarios | | finding | yes | CONFIRMED / REFUTED / CONTESTED / NEEDS_DEPTH | | evidence | yes | Code locations with line numbers | | step_execution | yes | Status for each step |