.claude/skills/crane-access/SKILL.md
This skill should be used when the user asks about "access control", "onlyOwner", "onlyOperator", "ownership transfer", "MultiStepOwnable", "ERC8023", "operable pattern", "function-level permissions", "reentrancy protection", "reentrancy lock", or needs to restrict function access in Crane Diamond contracts.
npx skillsauth add cyotee/crane crane-accessInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Crane provides three access control patterns for Diamond proxies: MultiStepOwnable (ERC-8023), Operable, and ReentrancyLock.
| Need | Component | Purpose | |------|-----------|---------| | Ownership with time-lock | MultiStepOwnable | Two-step ownership transfer with buffer period | | Delegated permissions | Operable | Global and function-level operator authorization | | Reentrancy protection | ReentrancyLock | Transient storage lock for cross-function reentrancy |
// Import modifiers
import {MultiStepOwnableModifiers} from "@crane/contracts/access/ERC8023/MultiStepOwnableModifiers.sol";
import {OperableModifiers} from "@crane/contracts/access/operable/OperableModifiers.sol";
import {ReentrancyLockModifiers} from "@crane/contracts/access/reentrancy/ReentrancyLockModifiers.sol";
// Use in your Target contract
contract MyTarget is MultiStepOwnableModifiers, OperableModifiers, ReentrancyLockModifiers {
function adminOnly() external onlyOwner {
// Only owner can call
}
function operatorOnly() external onlyOperator {
// Global operators or function-specific operators
}
function ownerOrOperator() external onlyOwnerOrOperator {
// Either owner or operator can call
}
function noReentrant() external nonReentrant {
// Protected from reentrancy
}
}
Time-locked two-step ownership transfer with buffer period.
struct Storage {
address owner;
address pendingOwner;
bool pendingOwnerConfirmed;
uint256 ownershipBufferPeriod; // e.g., 1 days
uint256 bufferPeriodEnd;
}
initiateOwnershipTransfer(newOwner)confirmOwnershipTransfer(newOwner)acceptOwnershipTransfer()// Step 1: Current owner initiates
MultiStepOwnableRepo._initiateOwnershipTransfer(newOwner);
// Step 2: Wait for buffer period (e.g., 1 day)
// Step 3: Current owner confirms
MultiStepOwnableRepo._confirmOwnershipTransfer(newOwner);
// Step 4: New owner accepts
MultiStepOwnableRepo._acceptOwnershipTransfer();
// Owner can cancel anytime before acceptance
MultiStepOwnableRepo._cancelPendingOwnershipTransfer();
// Revert if msg.sender is not owner
MultiStepOwnableRepo._onlyOwner();
// Revert if msg.sender is not pending owner
MultiStepOwnableRepo._onlyPendingOwner();
modifier onlyOwner() {
MultiStepOwnableRepo._onlyOwner();
_;
}
// In DFPkg.initAccount()
MultiStepOwnableRepo._initialize(
initialOwner, // First owner
1 days // Buffer period for transfers
);
Delegated authorization for global and function-level permissions.
struct Storage {
mapping(address => bool) isOperator; // Global operators
mapping(bytes4 func => mapping(address => bool)) isOperatorFor; // Function-level
}
onlyOperator// Grant/revoke global operator status
OperableRepo._setOperatorStatus(operatorAddress, true); // Grant
OperableRepo._setOperatorStatus(operatorAddress, false); // Revoke
// Grant/revoke function-level operator
OperableRepo._setFunctionOperatorStatus(
IMyContract.someFunction.selector, // Function selector
operatorAddress, // Operator
true // Approval
);
// Check global operator
bool isGlobal = OperableRepo._isOperator(account);
// Check function operator
bool isForFunc = OperableRepo._isFunctionOperator(
IMyContract.someFunction.selector,
account
);
// Revert if not global OR function operator for msg.sig
OperableRepo._onlyOperator();
// Revert if not owner AND not operator
OperableRepo._onlyOwnerOrOperator();
// Only global or function-level operators
modifier onlyOperator() {
OperableRepo._onlyOperator();
_;
}
// Owner OR any operator
modifier onlyOwnerOrOperator() {
OperableRepo._onlyOwnerOrOperator();
_;
}
Transient storage-based reentrancy protection (EIP-1153).
modifier nonReentrant() {
ReentrancyLockRepo._onlyUnlocked();
ReentrancyLockRepo._lock();
_;
ReentrancyLockRepo._unlock();
}
// Lock the reentrancy guard
ReentrancyLockRepo._lock();
// Unlock the reentrancy guard
ReentrancyLockRepo._unlock();
// Check if locked
bool locked = ReentrancyLockRepo._isLocked();
// Revert if locked (guard function)
ReentrancyLockRepo._onlyUnlocked();
| Component | Files |
|-----------|-------|
| MultiStepOwnable | MultiStepOwnableRepo.sol, MultiStepOwnableModifiers.sol, MultiStepOwnableTarget.sol, MultiStepOwnableFacet.sol |
| Operable | OperableRepo.sol, OperableModifiers.sol, OperableTarget.sol, OperableFacet.sol |
| ReentrancyLock | ReentrancyLockRepo.sol, ReentrancyLockModifiers.sol, ReentrancyLockTarget.sol, ReentrancyLockFacet.sol |
Deploy access control facets:
import {AccessFacetFactoryService} from "@crane/contracts/access/AccessFacetFactoryService.sol";
// Deploy MultiStepOwnable facet
IFacet ownerFacet = AccessFacetFactoryService.deployMultiStepOwnableFacet(create3Factory);
// Deploy Operable facet
IFacet operableFacet = AccessFacetFactoryService.deployOperableFacet(create3Factory);
// Deploy ReentrancyLock facet
IFacet reentrancyFacet = AccessFacetFactoryService.deployReentrancyLockFacet(create3Factory);
For detailed patterns and examples:
references/access-patterns.md - Complete access control integration patternsdevelopment
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
documentation
Write to contracts and send transactions. Use when executing state-changing contract functions.
development
HTTP and WebSocket transports for blockchain connectivity. Use when configuring network connections.
data-ai
Read contract data with type-safe ABI. Use when querying smart contract view/pure functions.