Skip to content

yeap-finance/protocol-object

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Protocol Object Framework

Description

This Move library provides a comprehensive framework for managing Aptos objects and fungible assets within a protocol context. It consists of two main modules:

  1. protocol_object: Defines a generic framework for protocol-managed objects that wraps underlying Aptos Framework objects
  2. protocol_fungible_asset: Provides specialized functionality for managing fungible assets within the protocol framework

The library enables protocols to maintain secure, controlled access to object capabilities and fungible asset operations through a standardized capability delegation system.

Modules Overview

protocol_object Module

A protocol-managed object is an object whose lifecycle and core functionalities (like extending its data, deriving new objects from it, transferring it, or deleting it) are explicitly controlled by a designated "protocol" module, rather than being freely accessible to any module or the object's owner.

This pattern solves several key problems compared to managing raw objects:

  • Standardized Capability Handling: It provides a consistent mechanism for protocols to manage an object's fundamental capabilities (ExtendRef, DeriveRef, DeleteRef, TransferRef). Instead of each protocol reinventing how to store or gate access to these, protocol_object offers a ready-to-use framework.
  • Protocol-Specific Logic Encapsulation: By controlling how and when capabilities are used, protocols can enforce their own rules and logic around object interactions. For example, a protocol might only allow an object to be extended if certain conditions are met, or it might manage transfers through a specific approval process.
  • Enhanced Security and Control: It prevents unauthorized modification or misuse of objects by ensuring that critical operations can only be performed through the protocol's explicit authorization. This is achieved by storing capabilities within the object itself, but making them accessible only to the designated protocol.

protocol_fungible_asset Module

The protocol_fungible_asset module extends the protocol framework to support Aptos fungible assets. It provides:

  • Delegated Fungible Asset Capabilities: Mint, burn, transfer, and metadata mutation capabilities can be delegated to protocol control
  • Protocol-Authorized Operations: All fungible asset operations (mint, burn, withdraw) require protocol authorization
  • Standardized Asset Management: Consistent patterns for managing fungible asset lifecycles within protocol contexts

Core Concepts

Both modules rely on two primary structs to manage capabilities in a controlled manner:

Capability<phantom P, R>

  • Role: This struct acts as a persistent store for capabilities. When protocol-managed objects or fungible assets are created, instances of Capability are stored directly on the object for each relevant capability (e.g., ExtendRef, DeriveRef, DeleteRef, TransferRef, MintRef, BurnRef).
  • Protocol Control: The P is a phantom type parameter representing the specific Protocol that manages this capability. This ensures that only the designated protocol can extract and use the capability.
  • Resource Type: The R parameter represents the actual type of the capability being stored (e.g., object::ExtendRef, fungible_asset::MintRef).

Ref<phantom P, R>

  • Role: This struct represents a temporary, "borrowed" reference to a capability R, under the control of Protocol P. When a protocol needs to use a capability, it calls extract_ref to move the Capability<P, R> from storage and returns a Ref<P, R>.
  • Safe Borrowing Mechanism: The protocol can use the inner field of the Ref to perform operations. Once done, the protocol must call return_ref to move the capability back to storage.
  • Ensuring Controlled Access: This extract/return mechanism ensures capabilities are not duplicated or lost, and are always returned to object control under protocol supervision.
  • Phantom Types for Safety: The phantom type P ensures that a Ref extracted by one protocol cannot be accidentally used by another. The R ensures type safety for the specific capability.

Installation

To use this module in your Aptos Move project:

  1. Add protocol-object as a dependency in your Move.toml:
    [dependencies]
    protocol_object = { git = "https://github.com/yeap-finance/protocol-object.git", rev = "<specific-commit-hash-or-tag>" }
    # e.g., rev = "main" or a specific commit SHA
    Replace <specific-commit-hash-or-tag> with the desired version (e.g., a commit SHA, tag, or branch like main).
  2. Import the module in your Move code:
    use protocol_object::protocol_object;
    // If your module also needs to define its own named address for protocol_object,
    // ensure it matches the one used by the protocol_object module itself (typically defined in its own Move.toml).
  3. Compile your project: When you compile your project, the Aptos CLI will fetch and build the dependency. You'll typically specify the named address for your own module(s).
    aptos move compile --named-addresses your_module_name=<your-account-address>

For Local Development (Cloning this Repository)

If you've cloned this protocol-object repository directly and want to build or test it as a standalone project:

  1. Navigate to the cloned directory.
  2. Compile the module:
    aptos move compile --named-addresses protocol_object=<your-account-address>
  3. Run tests (if available):
    aptos move test --named-addresses protocol_object=<your-account-address>
    Replace <your-account-address> with an Aptos account address for local testing and deployment (e.g., a profile from aptos init).

Usage

This module is intended to be used by other Move modules that act as "protocols" to manage objects.

API Reference

protocol_object Module Functions

Core Functions

  • extract_ref<Protocol, T: store>(_protocol: &Protocol, object_address: address): Ref<Protocol, T> Extracts a capability T from the object at object_address and wraps it in a Ref<Protocol, T>. This implements the "borrow" mechanism: it removes the Capability<Protocol, T> from storage and gives the protocol temporary exclusive access.

  • return_ref<Protocol, T: store>(ref: Ref<Protocol, T>) Returns a previously extracted Ref<Protocol, T> back to the object's storage. This is the "return" part of the mechanism that ensures capabilities are safely returned to object control.

  • ref<P, R>(self: &Ref<P, R>): &R Returns an immutable reference to the inner capability from a Ref.

Object Management Functions

  • delegate_all<Protocol>(constructor_ref: &ConstructorRef) Delegates all available capabilities of an object to a specific protocol during initialization. Automatically delegates ExtendRef, DeriveRef, and conditionally delegates DeleteRef and TransferRef.

  • delegate_extend_ref<Protocol>(object_ref: &ConstructorRef) Delegates an ExtendRef capability to the protocol.

  • delegate_derive_ref<Protocol>(object_ref: &ConstructorRef) Delegates a DeriveRef capability to the protocol.

  • delegate_delete_ref<Protocol>(object_ref: &ConstructorRef) Delegates a DeleteRef capability to the protocol (if the object can be deleted).

  • delegate_transfer_ref<Protocol>(object_ref: &ConstructorRef) Delegates a TransferRef capability to the protocol (if the object is transferable).

  • object_signer<Protocol>(_protocol: &Protocol, object_address: address): signer Gets the signer for the object using its ExtendRef capability.

Capability Management Functions

  • is_delegated<Protocol, R: store>(object: address): bool [view function] Checks if a specific capability is delegated to the protocol on the given object.

  • revoke<Protocol, R: store>(_p: &Protocol, object_address: address): R Revokes a specific capability from a protocol-managed object and returns it.

  • revoke_all<Protocol>(_protocol: &Protocol, object_address: address) Revokes all capabilities from a protocol-managed object. Useful for cleanup.

Transfer Control Functions

  • disable_ungated_transfer<Protocol>(_protocol: &Protocol, object_address: address) Disables direct transfers by the object owner; transfers must be mediated by the protocol.

  • enable_ungated_transfer<Protocol>(_protocol: &Protocol, object_address: address) Enables direct transfers by the object owner.

  • generate_linear_transfer_ref<Protocol>(_protocol: &Protocol, object_address: address): LinearTransferRef Generates a LinearTransferRef for one-time transfer scenarios.

  • grant_permission<Protocol, T: key>(_protocol: &Protocol, permissioned_signer: &signer, object: Object<T>) Grants transfer permission to another signer.

protocol_fungible_asset Module Functions

Core Functions

  • extract_ref<Protocol, T: store>(_protocol: &Protocol, object_address: address): Ref<Protocol, T> Extracts a fungible asset capability from the asset object.

  • return_ref<Protocol, T: store>(ref: Ref<Protocol, T>) Returns a previously extracted fungible asset capability.

  • ref<P, R>(self: &Ref<P, R>): &R Returns an immutable reference to the inner capability.

Capability Delegation Functions

  • delegate_all(asset_object_ref: &ConstructorRef) Delegates all standard fungible asset capabilities in a single call, using capability types as protocol parameters.

  • delegate_mint_ref<P>(asset_object_ref: &ConstructorRef) Delegates a MintRef capability to the protocol for the fungible asset.

  • delegate_burn_ref<P>(asset_object_ref: &ConstructorRef) Delegates a BurnRef capability to the protocol.

  • delegate_transfer_ref<P>(asset_object_ref: &ConstructorRef) Delegates a TransferRef capability to the protocol.

  • delegate_mutate_metadata_ref<P>(asset_object_ref: &ConstructorRef) Delegates a MutateMetadataRef capability to the protocol.

  • delegate_raw_supply_ref<P>(asset_object_ref: &ConstructorRef) Delegates a RawSupplyRef capability to the protocol for direct supply management.

  • delegate_raw_balance_ref<P>(asset_object_ref: &ConstructorRef) Delegates a RawBalanceRef capability to the protocol for direct balance management.

Capability Management Functions

  • revoke<Protocol, R: store>(_p: &Protocol, object_address: address): R Revokes a specific capability from a protocol-managed fungible asset and returns it.

  • revoke_all<Protocol>(_p: &Protocol, object_address: address) Revokes all standard fungible asset capabilities from a protocol-managed asset.

  • is_delegated<Protocol, R: store>(object: address): bool [view function] Checks if a specific capability is delegated to the protocol on the given fungible asset.

Asset Operations

  • mint<P>(_p: &P, asset_metadata: &Object<Metadata>, amount: u64): FungibleAsset Mints a specified amount of a fungible asset, authorized by protocol P.

  • burn<P>(_p: &P, asset_metadata: &Object<Metadata>, asset: FungibleAsset) Burns a FungibleAsset, authorized by protocol P.

  • withdraw<P>(_p: &P, store: Object<FungibleStore>, amount: u64): FungibleAsset Withdraws a specified amount from a FungibleStore, authorized by protocol P.

Metadata and Balance Operations

  • mutate_metadata<P>(_p: &P, metadata: Object<Metadata>, name: Option<String>, symbol: Option<String>, decimals: Option<u8>, icon_uri: Option<String>, project_uri: Option<String>) Mutates the metadata of a fungible asset, authorized by protocol P.

  • raw_supply<P>(_p: &P, metadata: Object<Metadata>): Option<u128> Gets the raw supply of a fungible asset using protocol authorization.

  • raw_balance<P>(_p: &P, store: Object<FungibleStore>): u64 Gets the raw balance of a fungible store using protocol authorization.

Example Usage

Basic Protocol Object Usage

Here's how to create a protocol that manages objects:

module your_addr::my_protocol_module {
    use std::signer;
    use aptos_framework::object::{Self, Object, ExtendRef, ConstructorRef};
    use protocol_object::protocol_object;

    /// Define a struct representing our protocol
    struct MyProtocol has key {
        admin: address,
    }

    /// Initialize your protocol
    public entry fun initialize_protocol(sender: &signer) {
        move_to(sender, MyProtocol { admin: signer::address_of(sender) });
    }

    /// Create a new object managed by MyProtocol
    public entry fun create_my_object(sender: &signer, owner: address) {
        let protocol_instance = borrow_global<MyProtocol>(signer::address_of(sender));

        // Create a named object
        let constructor_ref = object::create_named_object(sender, b"my_object");

        // Delegate capabilities to the protocol
        protocol_object::delegate_all<MyProtocol>(&constructor_ref);

        // Transfer to desired owner
        let object = object::object_from_constructor_ref<object::ObjectCore>(&constructor_ref);
        object::transfer(sender, object, owner);
    }

    /// Example of using extracted capabilities
    public fun extend_my_object_data(
        protocol_signer: &signer,
        object_address: address,
    ) acquires MyProtocol {
        let protocol_instance = borrow_global<MyProtocol>(signer::address_of(protocol_signer));

        // Extract the ExtendRef capability
        let extend_capability_ref = protocol_object::extract_ref<MyProtocol, ExtendRef>(
            protocol_instance,
            object_address
        );

        // Get a reference to the actual ExtendRef
        let extend_ref = protocol_object::ref(&extend_capability_ref);

        // Use the ExtendRef to add resources to the object
        let object_signer = protocol_object::object_signer(protocol_instance, object_address);
        // move_to(&object_signer, YourDataStruct { /* ... */ });

        // Return the capability
        protocol_object::return_ref<MyProtocol, ExtendRef>(extend_capability_ref);
    }
}

Fungible Asset Protocol Usage

Here's how to create a protocol that manages fungible assets:

module your_addr::my_token_protocol {
    use std::signer;
    use std::string;
    use aptos_framework::object::{Self, Object};
    use aptos_framework::fungible_asset::{Self, Metadata, FungibleAsset, MintRef, BurnRef};
    use protocol_object::protocol_fungible_asset;

    struct MyTokenProtocol has key {
        admin: address,
    }

    /// Initialize the token protocol
    public entry fun initialize_protocol(sender: &signer) {
        move_to(sender, MyTokenProtocol { admin: signer::address_of(sender) });
    }

    /// Create a new fungible asset managed by the protocol
    public entry fun create_token(
        sender: &signer,
        name: string::String,
        symbol: string::String,
        decimals: u8,
        icon_uri: string::String,
        project_uri: string::String,
    ): Object<Metadata> {
        // Create the fungible asset
        let constructor_ref = &object::create_named_object(sender, *string::bytes(&name));

        fungible_asset::create_metadata(
            constructor_ref,
            100, // max_supply (optional)
            name,
            symbol,
            decimals,
            icon_uri,
            project_uri,
        );

        // Delegate capabilities to our protocol
        protocol_fungible_asset::delegate_mint_ref<MyTokenProtocol>(constructor_ref);
        protocol_fungible_asset::delegate_burn_ref<MyTokenProtocol>(constructor_ref);
        protocol_fungible_asset::delegate_transfer_ref<MyTokenProtocol>(constructor_ref);
        protocol_fungible_asset::delegate_mutate_metadata_ref<MyTokenProtocol>(constructor_ref);

        object::object_from_constructor_ref<Metadata>(constructor_ref)
    }

    /// Mint tokens using protocol authorization
    public fun mint_tokens(
        protocol_signer: &signer,
        asset_metadata: &Object<Metadata>,
        amount: u64,
    ): FungibleAsset acquires MyTokenProtocol {
        let protocol = borrow_global<MyTokenProtocol>(signer::address_of(protocol_signer));
        protocol_fungible_asset::mint<MyTokenProtocol>(protocol, asset_metadata, amount)
    }

    /// Burn tokens using protocol authorization
    public fun burn_tokens(
        protocol_signer: &signer,
        asset_metadata: &Object<Metadata>,
        asset: FungibleAsset,
    ) acquires MyTokenProtocol {
        let protocol = borrow_global<MyTokenProtocol>(signer::address_of(protocol_signer));
        protocol_fungible_asset::burn<MyTokenProtocol>(protocol, asset_metadata, asset);
    }

    /// Withdraw from a store using protocol authorization
    public fun protocol_withdraw(
        protocol_signer: &signer,
        store: Object<fungible_asset::FungibleStore>,
        amount: u64,
    ): FungibleAsset acquires MyTokenProtocol {
        let protocol = borrow_global<MyTokenProtocol>(signer::address_of(protocol_signer));
        protocol_fungible_asset::withdraw<MyTokenProtocol>(protocol, store, amount)
    }
}

Error Handling

Functions within both modules use assert! to enforce preconditions and maintain invariants. If an assertion fails, the transaction will abort.

Common Error Codes

  • E_NOT_OBJECT (Error Code 1): Occurs when an operation expects an object but it's not found or is not a valid object. This can happen if:
    • return_ref is called but the object has been deleted
    • Required Capability resources are missing from the object
    • Invalid object addresses are provided to functions

Best Practices

  • Always ensure object addresses are valid before calling functions
  • Verify that the protocol has been correctly initialized for objects/assets
  • Confirm necessary capabilities exist on objects before extraction
  • Always pair extract_ref calls with corresponding return_ref calls
  • Handle capability extraction/return in the same function when possible to prevent capability leaks

Advanced Patterns

Capability Batching

For operations requiring multiple capabilities, you can extract multiple capabilities:

public fun complex_operation(protocol: &MyProtocol, object_addr: address) {
    let extend_ref = protocol_object::extract_ref<MyProtocol, ExtendRef>(protocol, object_addr);
    let derive_ref = protocol_object::extract_ref<MyProtocol, DeriveRef>(protocol, object_addr);

    // Perform operations with both capabilities
    // ...

    // Return capabilities
    protocol_object::return_ref(derive_ref);
    protocol_object::return_ref(extend_ref);
}

Capability Querying and Management

You can check if capabilities are delegated and manage them:

public fun manage_capabilities(protocol: &MyProtocol, object_addr: address) {
    // Check if capabilities are delegated
    let has_extend = protocol_object::is_delegated<MyProtocol, ExtendRef>(object_addr);
    let has_delete = protocol_object::is_delegated<MyProtocol, DeleteRef>(object_addr);

    if (has_delete) {
        // Revoke a specific capability
        let delete_ref = protocol_object::revoke<MyProtocol, DeleteRef>(protocol, object_addr);
        // Use the delete_ref or store it elsewhere
        object::delete(delete_ref);
    };

    // Or revoke all capabilities at once for cleanup
    // protocol_object::revoke_all<MyProtocol>(protocol, object_addr);
}

Fungible Asset Management

public fun manage_asset_capabilities(protocol: &MyTokenProtocol, asset_addr: address) {
    // Check and revoke specific fungible asset capabilities
    let mint_ref = protocol_fungible_asset::revoke<MyTokenProtocol, fungible_asset::MintRef>(
        protocol,
        asset_addr
    );

    // Use the mint_ref directly or delegate to another protocol
    let assets = fungible_asset::mint(&mint_ref, 1000);

    // Query asset information using protocol authorization
    let metadata = object::address_to_object<Metadata>(asset_addr);
    let supply = protocol_fungible_asset::raw_supply<MyTokenProtocol>(protocol, metadata);

    // Clean up all capabilities when done
    // protocol_fungible_asset::revoke_all<MyTokenProtocol>(protocol, asset_addr);
}

Protocol Composition

Protocols can compose with each other by delegating sub-capabilities:

public fun delegate_to_subprotocol<MainProtocol, SubProtocol>(
    main_protocol: &MainProtocol,
    object_addr: address,
    constructor_ref: &ConstructorRef
) {
    // Main protocol can delegate specific capabilities to sub-protocols
    protocol_object::delegate_mint_ref<SubProtocol>(constructor_ref);
    // while retaining others
    protocol_object::delegate_burn_ref<MainProtocol>(constructor_ref);
}

License

This project is licensed under the LICENSE file.

About

No description, website, or topics provided.

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages