Skip to main content

clarity-patterns

Clarity smart contract pattern library — reusable code patterns, contract templates, and design references for building on Stacks.

Ir a la instalación

Datos de origen

Repositorio
aibtcdev/skills
Última actividad en el origen
8 de abril de 2026 a las 01:02
Idioma detectado de SKILL.md
inglés
Estrellas
10
Forks
44

Opciones de instalación

De forma predeterminada está seleccionado el prompt que primero revisa el origen. Puedes cambiar a un comando directo o descargar una copia local.

Revisa los archivos de origen

Lee SKILL.md y los archivos complementarios que muestra SkillsMP antes de decidir si quieres instalarlo.

Explorador de archivos
5 archivos

Mostrando SKILL.md

SKILL.md
Instrucciones de origen · Vista previa de solo lectura
name
clarity-patterns
description
Clarity smart contract pattern library — reusable code patterns, contract templates, and design references for building on Stacks.
metadata
{"author":"whoabuddy","author-agent":"Arc","user-invocable":"false","arguments":"list | get | template","entry":"clarity-patterns/SKILL.md","requires":"","tags":"read-only, l2, infrastructure"}
# Clarity Patterns Skill Canonical pattern library for Clarity smart contract development on Stacks. All patterns and templates are bundled in this skill — no external dependencies. This is a doc-only skill. Agents read this file and the colocated reference files directly. The CLI interface documents the planned implementation. ``` bun run clarity-patterns/clarity-patterns.ts <subcommand> [options] ``` ## Subcommands - `list [--category <category>]` — List available patterns and templates (categories: `code`, `registry`, `templates`, `testing`) - `get --name <pattern-name>` — Return a specific pattern with code and notes - `template --name <template-name>` — Return a complete contract template with source, tests, and checklist --- ## Code Patterns ### Public Function Template Standard structure for public functions with guards and error handling. ```clarity (define-public (transfer (amount uint) (to principal)) (begin (asserts! (is-eq tx-sender owner) ERR_UNAUTHORIZED) (try! (ft-transfer? TOKEN amount tx-sender to)) (ok true))) ``` - Use `try!` for subcalls to propagate errors - Use `asserts!` for guards before state changes - Add post-conditions on tx for asset safety ### Standardized Events Emit structured events for off-chain indexing. ```clarity (print { notification: "contract-event", payload: { amount: amount, sender: tx-sender, recipient: to } }) ``` - `notification`: string identifier for the event type - `payload`: tuple with camelCase keys - Examples: [usabtc-token](https://github.com/USA-BTC/smart-contracts/blob/main/contracts/usabtc-token.clar), [ccd002-treasury-v3](https://github.com/citycoins/protocol/blob/main/contracts/extensions/ccd002-treasury-v3.clar) ### Error Handling with Match Handle external call failures gracefully. ```clarity (match (contract-call? .other fn args) success (ok success) error (err ERR_EXTERNAL_CALL_FAILED)) ``` ### Bit Flags for Status/Permissions Pack multiple booleans into a single uint. ```clarity (define-constant STATUS_ACTIVE (pow u2 u0)) ;; 1 (define-constant STATUS_PAID (pow u2 u1)) ;; 2 (define-constant STATUS_VERIFIED (pow u2 u2)) ;; 4 ;; Pack multiple flags: (+ STATUS_ACTIVE STATUS_PAID) → u3 ;; Check flag: (> (bit-and status STATUS_ACTIVE) u0) ;; Set flag: (var-set status (bit-or (var-get status) NEW_FLAG)) ;; Clear flag: (var-set status (bit-and (var-get status) (bit-not FLAG))) ``` Examples: [aibtc-action-proposal-voting](https://github.com/aibtcdev/aibtcdev-daos/blob/main/contracts/dao/extensions/aibtc-action-proposal-voting.clar) ### Multi-Send Pattern Send to multiple recipients in one transaction using fold. ```clarity (define-private (send-maybe (recipient {to: principal, ustx: uint}) (prior (response bool uint))) (match prior ok-result (let ( (to (get to recipient)) (ustx (get ustx recipient))) (try! (stx-transfer? ustx tx-sender to)) (ok true)) err-result (err err-result))) (define-public (send-many (recipients (list 200 {to: principal, ustx: uint}))) (fold send-maybe recipients (ok true))) ``` ### Parent-Child Maps (Hierarchical Data) Store hierarchical data with pagination support. ```clarity (define-map Parents uint {name: (string-ascii 32), lastChildId: uint}) (define-map Children {parentId: uint, id: uint} uint) (define-read-only (get-child (parentId uint) (childId uint)) (map-get? Children {parentId: parentId, id: childId})) (define-private (is-some? (x (optional uint))) (is-some x)) (define-read-only (get-children (parentId uint) (shift uint)) (filter is-some? (list (get-child parentId (+ shift u1)) (get-child parentId (+ shift u2)) (get-child parentId (+ shift u3)) ;; ... up to page size ))) ``` ### Whitelisting (Assets/Contracts) Control which contracts/assets can interact. ```clarity (define-map Allowed {contract: principal, type: uint} bool) ;; Check in function (asserts! (default-to false (map-get? Allowed {contract: contract, type: type})) ERR_NOT_ALLOWED) ;; Batch update (define-public (set-allowed-list (items (list 100 {token: principal, enabled: bool}))) (ok (map set-iter items (ok true)))) ``` Examples: [ccd002-treasury-v3](https://github.com/citycoins/protocol/blob/main/contracts/extensions/ccd002-treasury-v3.clar), [aibtc-agent-account](https://github.com/aibtcdev/aibtcdev-daos/blob/main/contracts/agent/aibtc-agent-account.clar) ### Trait Whitelisting Only allow calls from trusted trait implementations. ```clarity (define-map TrustedTraits principal bool) ;; In functions accepting traits (asserts! (default-to false (map-get? TrustedTraits (contract-of t))) ERR_UNTRUSTED) ``` ### Delayed Activation Activate functionality after a Bitcoin block delay. ```clarity (define-constant DELAY u21000) ;; ~146 days in BTC blocks (define-data-var activation-block uint u0) ;; Set on deploy or init (var-set activation-block (+ burn-block-height DELAY)) (define-read-only (is-active?) (>= burn-block-height (var-get activation-block))) ``` Example: [usabtc-token](https://github.com/USA-BTC/smart-contracts/blob/main/contracts/usabtc-token.clar) ### Rate Limiting Prevent rapid repeated actions. ```clarity (define-data-var last-action-block uint u0) (define-public (rate-limited-action) (begin (asserts! (> burn-block-height (var-get last-action-block)) ERR_RATE_LIMIT) (var-set last-action-block burn-block-height) ;; ... action (ok true))) ``` ### DAO Proposals with Snapshot Voting (Stacks 3.4+) > **Note:** Contracts using `at-block` will fail after Stacks 3.4 activation (~2026-04-02, BTC block 943,333). The `at-block` built-in was removed in Stacks 3.4 (SIP-042). The aibtcdev-daos DAO contracts that used `at-block` will need migration before activation. Store token balance snapshots at proposal creation time using a composite-key map. This avoids `at-block`, eliminates `filter`/list scan at read time, and gives O(1) lookup. ```clarity ;; Stacks 3.4+: at-block removed. Store snapshot at proposal creation time. ;; Using a composite-key map for O(1) lookup — no filter/list scan needed. (define-map ProposalSnapshots {proposalId: uint, voter: principal} uint) (define-map Proposals uint { votesFor: uint, votesAgainst: uint, status: uint, liquidTokens: uint, snapshotBlock: uint }) ;; At proposal creation: capture token balances for eligible voters ;; (caller supplies voter list; balances read from current block) (define-public (create-proposal (voters (list 1000 principal))) (let ( (proposal-id (+ (var-get last-proposal-id) u1)) (snapshot-block stacks-block-height)) ;; fold is used (not map) because Clarity has no partial application — ;; map requires a bare function identifier, not a call expression. ;; proposal-id is threaded as the accumulator so store-snapshot can use it. ;; fold threads proposal-id as accumulator; store-snapshot fires map-set as side effect (fold store-snapshot voters proposal-id) (map-set Proposals proposal-id { votesFor: u0, votesAgainst: u0, status: u0, liquidTokens: u0, snapshotBlock: snapshot-block}) (var-set last-proposal-id proposal-id) (ok proposal-id))) (define-private (store-snapshot (voter principal) (acc uint)) (begin (map-set ProposalSnapshots {proposalId: acc, voter: voter} (unwrap! (contract-call? .token get-balance voter) u0)) acc)) ;; O(1) lookup — no list scan, no filter (define-read-only (get-vote-power (proposal-id uint) (voter principal)) (default-to u0 (map-get? ProposalSnapshots {proposalId: proposal-id, voter: voter}))) ;; Quorum check: (>= (/ (* total-votes u100) liquid-supply) QUORUM_PERCENT) ``` Key points: - **No `(filter ...)` with closures** — Clarity has no partial application. Composite-key map replaces the filter pattern entirely. - **`default-to u0` works correctly** — `map-get?` returns `(optional uint)`, so `default-to u0` handles missing voters without panic (unlike `unwrap-panic` on a filtered list). - **O(1) lookup** instead of O(n) list scan at read time. Example: [aibtcdev-daos](https://github.com/aibtcdev/aibtcdev-daos) — DAO contracts using `at-block` require migration before Stacks 3.4 activation. ### Fixed-Point Arithmetic Handle decimal values with scale factor. ```clarity (define-constant SCALE (pow u10 u8)) ;; 8 decimal places ;; Multiply then divide to preserve precision (define-read-only (calculate-share (amount uint) (percentage uint)) (/ (* amount percentage) SCALE)) ;; Convert to/from scaled values (define-read-only (to-scaled (amount uint)) (* amount SCALE)) (define-read-only (from-scaled (amount uint)) (/ amount SCALE)) ``` Example: [ccd012-redemption-nyc](https://github.com/citycoins/protocol/blob/main/contracts/extensions/ccd012-redemption-nyc.clar) ### Treasury Pattern with as-contract Use `as-contract` for contract-controlled funds. ```clarity (define-public (withdraw (amount uint) (recipient principal)) (begin (asserts! (is-authorized tx-sender) ERR_UNAUTHORIZED) (as-contract (stx-transfer? amount (as-contract tx-sender) recipient)))) ``` Warning: `as-contract` changes both `tx-sender` and `contract-caller` to the contract principal. ### tx-sender vs contract-caller Decision Framework | Call Path | contract-caller | tx-sender | |-----------|-----------------|-----------| | user -> target | user | user | | user -> proxy -> target | proxy | user | | user -> proxy (as-contract) -> target | proxy | proxy | - **tx-sender**: Use for auth checks, identity attribution, self-action guards. Preserves human identity through normal proxies. Preferred for composability. - **contract-caller**: Use when you need the IMMEDIATE caller identity specifically. - **Security note**: Using `contract-caller` for self-action guards (e.g., "owner can't give themselves feedback") is bypassable — owner routes through any proxy and `contract-caller` shows the proxy, not the owner. `tx-sender` catches this because it preserves the human origin. Examples: [ccd002-treasury-v3](https://github.com/citycoins/protocol/blob/main/contracts/extensions/ccd002-treasury-v3.clar), [aibtc-agent-account](https://github.com/aibtcdev/aibtcdev-daos/blob/main/contracts/agent/aibtc-agent-account.clar) ### Clarity 4: Asset Restrictions Restrict what assets a contract call can move. ```clarity (as-contract (with-stx u1000000) ;; Allow 1 STX (with-ft .token TOKEN u500) ;; Allow 500 fungible tokens (with-nft .nft-contract NFT (list u1 u2 u3)) ;; Allow specific NFT IDs ;; ... body ) ;; DANGER: Avoid unless necessary (with-all-assets-unsafe) ``` ### Multi-Party Coordination Coordinate actions requiring multiple signatures. ```clarity ;; Proposal state (define-map Intents uint { participants: (list 20 principal), accepts: uint, ;; Bitmask of who accepted status: uint, ;; 0=pending, 1=ready, 2=executed, 3=cancelled expiry: uint, payload: (buff 256) }) ;; Accept via signature verification (define-public (accept (intent-id uint) (signature (buff 65))) (let ( (intent (unwrap! (map-get? Intents intent-id) ERR_NOT_FOUND)) (msg-hash (sha256 (concat (int-to-ascii intent-id) (get payload intent)))) (signer (try! (secp256k1-recover? msg-hash signature)))) ;; Verify signer is participant, update accepts bitmask (ok true))) ``` Reference: ERC-8001 pattern for decidable multi-party coordination. --- ## Registry Patterns ### Block Snapshot Pattern Capture comprehensive chain state at transaction time. This is the "receipt" that makes a transaction worth the fee. ```clarity ;; Full snapshot — comprehensive (use for high-value records) (define-private (capture-snapshot) { stacksBlock: stacks-block-height, burnBlock: burn-block-height, tenure: tenure-height, blockTime: stacks-block-time, chainId: chain-id, txSender: tx-sender, contractCaller: contract-caller, txSponsor: tx-sponsor?, stacksBlockHash: (get-stacks-block-info? id-header-hash (- stacks-block-height u1)),
Ver en GitHub
Este SKILL.md es muy grande, por eso SkillsMP muestra aqui solo la primera seccion. Ver en GitHub