返回 Skill 列表
extension
分类: 开发与工程无需 API Key

crane-architecture

当用户询问关于“facet”、“target”、“repo”、“diamond模式”、“存储槽”、“守护函数”、“DFPkg”、“AwareRepo”、“服务模式”、“修饰符”、“ERC2535”,或需要指导Crane的核心架构模式来构建模块化、可升级的智能合约时,应使用此技能。

person作者: jakexiaohubgithub

Crane Architecture Patterns

Crane is a Diamond-first (ERC2535) Solidity development framework for building modular, upgradeable smart contracts. This skill provides guidance on the core architectural patterns.

Core Pattern: Facet-Target-Repo

Every feature in Crane follows a three-tier architecture:

| Layer | File Pattern | Purpose | |-------|--------------|---------| | Repo | *Repo.sol | Storage library with assembly-based slot binding. Defines Storage struct and dual _layout() functions. No state variables. | | Target | *Target.sol | Implementation contract with business logic. Uses Repo for storage access. Inherits interfaces. | | Facet | *Facet.sol | Diamond facet. Extends Target and implements IFacet for metadata (name, interfaces, selectors). |

When to Create Each Layer

  • Repo: Always create first. Contains all storage and internal helper functions.
  • Target: Create when business logic needs to be shared or tested independently.
  • Facet: Create when exposing functionality through the Diamond proxy.

Storage Slot Pattern

All Repos use the Diamond storage pattern with dual function overloads:

library ExampleRepo {
    bytes32 internal constant STORAGE_SLOT = keccak256(abi.encode("crane.feature.name"));

    struct Storage {
        mapping(address => bool) isOperator;
    }

    // Parameterized version - allows custom slot
    function _layout(bytes32 slot) internal pure returns (Storage storage layout) {
        assembly { layout.slot := slot }
    }

    // Default version - uses STORAGE_SLOT
    function _layout() internal pure returns (Storage storage) {
        return _layout(STORAGE_SLOT);
    }
}

Dual Function Overload Pattern

Every Repo function has TWO overloads:

// 1. Parameterized: takes Storage as first param
function _isOperator(Storage storage layout, address query) internal view returns (bool) {
    return layout.isOperator[query];
}

// 2. Default: calls parameterized with _layout()
function _isOperator(address query) internal view returns (bool) {
    return _isOperator(_layout(), query);
}

This enables:

  • Default usage with standard storage slot
  • Custom slot usage for multi-instance patterns
  • Composability between Repos

Guard Functions Pattern

Repos contain _onlyXxx() guard functions with access control logic. Modifiers are thin wrappers:

// In Repo - contains the actual check logic
function _onlyOperator(Storage storage layout) internal view {
    if (!_isOperator(layout, msg.sender) && !_isFunctionOperator(layout, msg.sig, msg.sender)) {
        revert IOperable.NotOperator(msg.sender);
    }
}

function _onlyOperator() internal view {
    _onlyOperator(_layout());
}

// In Modifiers contract - thin delegation wrapper
modifier onlyOperator() {
    OperableRepo._onlyOperator();
    _;
}

Additional Patterns

Modifiers Contract (*Modifiers.sol)

Abstract contracts with reusable modifiers delegating to Repo guards:

abstract contract OperableModifiers {
    modifier onlyOperator() {
        OperableRepo._onlyOperator();
        _;
    }
}

Service Library (*Service.sol)

Stateless libraries for complex business logic. Use structs to avoid stack-too-deep:

library CamelotV2Service {
    struct SwapParams {
        ICamelotV2Router router;
        uint256 amountIn;
        IERC20 tokenIn;
    }

    function _swap(SwapParams memory params) internal { ... }
}

AwareRepo (*AwareRepo.sol)

Dependency injection for external contract references:

library BalancerV3VaultAwareRepo {
    bytes32 internal constant STORAGE_SLOT = keccak256("protocols.dexes.balancer.v3.vault.aware");

    struct Storage {
        IVault balancerV3Vault;
    }

    function _initialize(IVault vault) internal { _layout().balancerV3Vault = vault; }
    function _balancerV3Vault() internal view returns (IVault) { return _layout().balancerV3Vault; }
}

Diamond Factory Package (*DFPkg.sol)

Bundles facets into deployable packages. See references/dfpkg-pattern.md for complete details.

IFacet Interface

All facets implement IFacet:

function facetName() external view returns (string memory name);
function facetInterfaces() external view returns (bytes4[] memory interfaces);
function facetFuncs() external view returns (bytes4[] memory funcs);
function facetMetadata() external view returns (string memory name, bytes4[] memory interfaces, bytes4[] memory functions);

Storage Slot Naming Convention

Use hierarchical dot-notation:

| Pattern | Example | |---------|---------| | Crane core | "crane.access.operable" | | Protocol integrations | "protocols.dexes.balancer.v3.vault.aware" | | EIP implementations | "eip.erc.8023" |

Key Reference Files

For complete implementations, examine these files in the Crane codebase:

  • contracts/access/operable/ - Complete Facet-Target-Repo example
  • contracts/introspection/ERC2535/ERC2535Repo.sol - Diamond storage management
  • contracts/access/ERC8023/ - Two-step ownership (EIP-8023)
  • contracts/protocols/dexes/camelot/v2/services/CamelotV2Service.sol - Service pattern

Additional Resources

Reference Files

For detailed patterns and complete examples:

  • references/dfpkg-pattern.md - Diamond Factory Package pattern in depth
  • references/factory-service.md - FactoryService pattern for deployment

Quick Decision Guide

| Need | Pattern | |------|---------| | Storage for a feature | Create *Repo.sol | | Business logic | Create *Target.sol or *Service.sol | | Diamond-exposed functions | Create *Facet.sol | | External contract reference | Create *AwareRepo.sol | | Reusable access control | Create *Modifiers.sol | | Deployable package | Create *DFPkg.sol |