cadence

The complete reference for writing, auditing, and shipping Cadence smart contracts on Flow. Covers language syntax, resource-oriented programming, capabilities, access control, tokens, and production best practices — with code examples throughout.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "cadence" with this command: npx skills add outblock/cadence-lang.org

Cadence Smart Contract Development

Use this skill whenever you are writing, reviewing, debugging, or auditing Cadence code on the Flow blockchain.

Documentation

Tip: Append .mdx to any cadence-lang.org doc URL to get raw markdown.


AI Tools

Cadence is designed for AI-native development with three integrations:

ToolDescription
SkillsInstall with npx skills add outblock/cadence-lang.org
MCP Serverhttps://cadence-mcp.up.railway.app/mcp — docs search, code checking, type inspection
LLM Endpoints/llms.txt (index) and /llms-full.txt (complete docs)

MCP Quick Start

One-click install:

Claude Code:

claude mcp add cadence-mcp -- npx -y mcp-remote https://cadence-mcp.up.railway.app/mcp

Claude Desktop / Cursor / Antigravity / OpenCode:

{
  "mcpServers": {
    "cadence": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://cadence-mcp.up.railway.app/mcp"]
    }
  }
}

OpenCode (alternative):

{
  "mcp": {
    "cadence": {
      "type": "remote",
      "url": "https://cadence-mcp.up.railway.app/mcp"
    }
  }
}

Local stdio server (requires Flow CLI):

npx @outblock/cadence-mcp

MCP Tools: search_docs, get_doc, browse_docs, cadence_check, cadence_hover, cadence_definition, cadence_symbols. All LSP tools support mainnet/testnet imports.

Agent-Specific Setup

AgentSetup
Claude Codenpx skills add outblock/cadence-lang.org + MCP above
Claude DesktopMCP config in claude_desktop_config.json
CursorSkills + .cursor/rules file + MCP
AntigravitySkills (auto-reads SKILL.md) + MCP config
OpenCodeSkills + MCP config
CodexSkills + AGENTS.md instructions
Gemini CLISkills + GEMINI.md instructions

1. Language Fundamentals

Types at a Glance

TypeExampleNotes
Int42Arbitrary precision
UInt6410064-bit unsigned
UFix641.5Fixed-point, 8 decimals. Use for balances
Booltrue
String"hello"UTF-8
Address0x1234Account address
[T][1, 2, 3]Array
{K: V}{"a": 1}Dictionary
T?nilOptional — nil-coalesce with ??
@TResource — unique, non-copyable
&TReference — borrow without moving

Variables, Optionals, Functions

let x: Int = 42                     // immutable
var name: String = "Cadence"        // mutable

// Optionals
let val: Int? = nil
let result = val ?? 0               // nil-coalescing
if let v = val { }                  // optional binding
let forced = val!                   // force-unwrap — panics, avoid

// Functions
access(all) fun greet(name: String): String {
    return "Hello, \(name)!"
}

// view = read-only (safe in scripts and pre/post)
access(all) view fun getBalance(): UFix64 { return self.balance }

// Pre/post conditions
access(all) fun withdraw(amount: UFix64): @Vault {
    pre  { amount > 0.0:              "Amount must be positive" }
    post { result.balance == amount:  "Withdrawal amount mismatch" }
}

Struct vs Resource

// Struct — copyable value type
struct Point {
    let x: Int; let y: Int
    init(x: Int, y: Int) { self.x = x; self.y = y }
}
let a = Point(x: 1, y: 2)
let b = a   // COPY — both exist independently

// Resource — unique, linear type (cannot copy)
resource Token {
    let id: UInt64
    init() { self.id = self.uuid }
}
let t1 <- create Token()
let t2 <- t1   // MOVE — t1 is no longer valid
destroy t2     // must explicitly destroy if not stored

Resource rules:

  1. Resources exist in exactly one place at a time.
  2. Every resource must be saved, moved, or destroyed before the current scope ends.
  3. Use @ in type annotations: @NFT, @{UInt64: NFT}.
  4. Use <- to move, <-! for force-assign into optional (panics on conflict).

2. Access Control & Entitlements

Access Modifiers

ModifierWho can access
access(all)Everyone (public read, callable by anyone)
access(self)Only within the type itself
access(contract)Same contract
access(account)Same account
access(E)Callers holding entitlement E

Entitlements Pattern

access(all) entitlement Withdraw
access(all) entitlement Owner

access(all) resource Vault {
    access(self) var balance: UFix64

    // Anyone can read
    access(all) view fun getBalance(): UFix64 { return self.balance }

    // Anyone can deposit into you
    access(all) fun deposit(from: @Vault) {
        self.balance = self.balance + from.balance
        destroy from
    }

    // Only authorized callers can withdraw
    access(Withdraw) fun withdraw(amount: UFix64): @Vault {
        pre { self.balance >= amount: "Insufficient balance" }
        self.balance = self.balance - amount
        return <- create Vault(balance: amount)
    }
}

// Getting an entitled reference
let vaultRef = signer.storage
    .borrow<auth(Withdraw) &Vault>(from: /storage/vault)
    ?? panic("Could not borrow Vault")

References

let ref: &NFT = &myNFT                          // read-only reference
let authRef: auth(Withdraw) &Vault = &myVault   // entitled reference

// From storage
let r = signer.storage.borrow<auth(Withdraw) &Vault>(from: /storage/vault)
    ?? panic("No vault found")

Matching Access Modifiers Required for Interface Implementations

Implementation members must use exactly the same access modifier as the interface:

access(all) resource interface I {
    access(account) fun foo()
}

// BAD — access(all) is more permissive than access(account)
access(all) resource R: I {
    access(all) fun foo() {}
}

// GOOD
access(all) resource R: I {
    access(account) fun foo() {}
}

3. Capabilities

Capabilities are the mechanism for sharing access to stored objects. Nothing is public by default.

// 1. Save object to storage
signer.storage.save(<- create Vault(balance: 0.0), to: /storage/vault)

// 2. Issue a capability (creates a tracked controller)
let receiverCap = signer.capabilities.storage
    .issue<&{FungibleToken.Receiver}>(/storage/vault)

// 3. Publish so others can borrow it
signer.capabilities.publish(receiverCap, at: /public/receiver)

// 4. Borrow from another account
let receiver = getAccount(address).capabilities
    .borrow<&{FungibleToken.Receiver}>(/public/receiver)
    ?? panic("Account has no receiver capability")

// 5. Revoke when needed
let controller = signer.capabilities.storage
    .getController(byCapabilityID: capID) ?? panic("Controller not found")
controller.delete()

// Issue an entitled capability (grants Withdraw to holder)
let ownerCap = signer.capabilities.storage
    .issue<auth(Withdraw) &Vault>(/storage/vault)

Public Capability Acquisition Returns Non-Optional

capabilities.get<T> returns an invalid capability (not nil) when no capability exists or when T mismatches. Check validity with .check():

let capability = account.capabilities.get<&MyNFT.Collection>(/public/NFTCollection)
if !capability.check() {
    // Handle invalid capability (ID == 0, borrow returns nil)
}

4. Transactions & Scripts

Transaction Structure

transaction(amount: UFix64, recipient: Address) {

    // Declare fields shared across phases
    let vaultRef: auth(FungibleToken.Withdraw) &FungibleToken.Vault

    // Access accounts — the ONLY phase with account access
    prepare(signer: auth(BorrowValue) &Account) {
        self.vaultRef = signer.storage
            .borrow<auth(FungibleToken.Withdraw) &FungibleToken.Vault>(
                from: /storage/vault
            ) ?? panic("No vault found")
    }

    pre  { amount > 0.0: "Amount must be positive" }

    execute {
        let tokens <- self.vaultRef.withdraw(amount: amount)
        let receiver = getAccount(recipient).capabilities
            .borrow<&{FungibleToken.Receiver}>(/public/receiver)
            ?? panic("Recipient has no receiver")
        receiver.deposit(from: <- tokens)
    }

    post { /* verify post-conditions */ }
}

Phases: preparepreexecutepost

Common Signer Authorizations

prepare(signer: auth(SaveValue) &Account)                          // save to storage
prepare(signer: auth(BorrowValue) &Account)                        // borrow from storage
prepare(signer: auth(LoadValue) &Account)                          // load (remove) from storage
prepare(signer: auth(IssueStorageCapabilityController) &Account)   // issue capabilities
prepare(signer: auth(PublishCapability) &Account)                  // publish capabilities
prepare(signer: auth(Contracts) &Account)                          // deploy/update contracts
prepare(signer: auth(Keys) &Account)                               // manage account keys

Scripts (read-only, free, no transaction needed)

access(all) fun main(address: Address): UFix64 {
    return getAccount(address).capabilities
        .borrow<&{FungibleToken.Balance}>(/public/balance)
        ?.balance ?? 0.0
}

5. Account Storage

// Save — moves resource into storage
signer.storage.save(<- create NFT(), to: /storage/myNFT)

// Borrow — get a reference without removing
let ref = signer.storage.borrow<&NFT>(from: /storage/myNFT)

// Load — removes and returns (must handle the resource)
let nft <- signer.storage.load<@NFT>(from: /storage/myNFT)!

// Check existence and type
let t: Type? = signer.storage.type(at: /storage/myNFT)
if t != nil { /* already exists */ }

// Iterate all stored values
signer.storage.forEachStored(fun (path: StoragePath, type: Type): Bool {
    return true  // true = continue, false = stop
})

6. NFT Contract (Production Pattern)

access(all) contract BasicNFT {

    access(all) var totalSupply: UInt64
    access(all) event Minted(id: UInt64)
    access(all) event Withdraw(id: UInt64, from: Address?)
    access(all) event Deposit(id: UInt64, to: Address?)
    access(all) entitlement Withdraw

    access(all) let CollectionStoragePath: StoragePath
    access(all) let CollectionPublicPath: PublicPath

    access(all) resource NFT {
        access(all) let id: UInt64
        access(all) let metadata: {String: String}
        init(metadata: {String: String}) {
            self.id = self.uuid
            BasicNFT.totalSupply = BasicNFT.totalSupply + 1
            emit Minted(id: self.id)
        }
    }

    access(all) resource Collection {
        access(self) var ownedNFTs: @{UInt64: NFT}

        access(all) fun deposit(token: @NFT) {
            emit Deposit(id: token.id, to: self.owner?.address)
            self.ownedNFTs[token.id] <-! token
        }

        access(Withdraw) fun withdraw(id: UInt64): @NFT {
            let token <- self.ownedNFTs.remove(key: id)
                ?? panic("NFT \(id) not found in collection")
            emit Withdraw(id: token.id, from: self.owner?.address)
            return <- token
        }

        access(all) fun getIDs(): [UInt64] { return self.ownedNFTs.keys }
        access(all) view fun borrowNFT(_ id: UInt64): &NFT? {
            return &self.ownedNFTs[id]
        }

        init() { self.ownedNFTs <- {} }
    }

    access(all) fun createEmptyCollection(): @Collection {
        return <- create Collection()
    }

    init() {
        self.totalSupply = 0
        self.CollectionStoragePath = /storage/basicNFTCollection
        self.CollectionPublicPath = /public/basicNFTCollection
    }
}

NFT Setup Transaction

transaction {
    prepare(signer: auth(SaveValue, IssueStorageCapabilityController, PublishCapability) &Account) {
        // Idempotent — skip if already set up
        if signer.storage.type(at: BasicNFT.CollectionStoragePath) != nil { return }

        signer.storage.save(<- BasicNFT.createEmptyCollection(), to: BasicNFT.CollectionStoragePath)
        let cap = signer.capabilities.storage
            .issue<&BasicNFT.Collection>(BasicNFT.CollectionStoragePath)
        signer.capabilities.publish(cap, at: BasicNFT.CollectionPublicPath)
    }
}

Mint & Transfer

// Mint (admin transaction)
transaction(recipient: Address, name: String) {
    execute {
        let nft <- create BasicNFT.NFT(metadata: {"name": name})
        getAccount(recipient).capabilities
            .borrow<&BasicNFT.Collection>(BasicNFT.CollectionPublicPath)
            ?.deposit(token: <- nft)
            ?? panic("Recipient has no collection")
    }
}

// Transfer (user transaction)
transaction(recipient: Address, nftID: UInt64) {
    let withdrawRef: auth(BasicNFT.Withdraw) &BasicNFT.Collection
    prepare(signer: auth(BorrowValue) &Account) {
        self.withdrawRef = signer.storage
            .borrow<auth(BasicNFT.Withdraw) &BasicNFT.Collection>(
                from: BasicNFT.CollectionStoragePath
            ) ?? panic("No collection")
    }
    execute {
        let nft <- self.withdrawRef.withdraw(id: nftID)
        getAccount(recipient).capabilities
            .borrow<&BasicNFT.Collection>(BasicNFT.CollectionPublicPath)
            ?.deposit(token: <- nft)
            ?? panic("Recipient has no collection")
    }
}

7. Fungible Token (Production Pattern)

access(all) contract BasicToken {
    access(all) var totalSupply: UFix64
    access(all) entitlement Withdraw

    access(all) resource Vault {
        access(all) var balance: UFix64
        init(balance: UFix64) { self.balance = balance }

        access(all) fun deposit(from: @Vault) {
            self.balance = self.balance + from.balance
            destroy from
        }

        access(Withdraw) fun withdraw(amount: UFix64): @Vault {
            pre { self.balance >= amount: "Insufficient balance" }
            self.balance = self.balance - amount
            return <- create Vault(balance: amount)
        }
    }

    access(all) fun createEmptyVault(): @Vault {
        return <- create Vault(balance: 0.0)
    }

    init() { self.totalSupply = 0.0 }
}

Cadence 1.0 Token Standard Changes

FungibleToken.Vault and NonFungibleToken.NFT / NonFungibleToken.Collection are now interfaces, not concrete types. Update all references:

// Before (Cadence 0.x)
fun deposit(from: @FungibleToken.Vault)

// After (Cadence 1.0)
fun deposit(from: @{FungibleToken.Vault})

Why vaults beat ledgers:

Ledger (Solidity)Vault (Cadence)
Balances in a contract mappingEach user holds their own Vault
Reentrancy attacks possibleResources move atomically
Admin can modify any balanceOnly the holder can access their Vault

8. Security Rules (Non-Negotiable)

S1 — Least Privilege

Start access(self), widen only when justified. Never access(all) on state-modifying functions.

// BAD — anyone drains your vault
access(all) fun withdraw(amount: UFix64): @Vault { ... }

// GOOD — requires entitlement
access(Withdraw) fun withdraw(amount: UFix64): @Vault { ... }

S2 — Never Pass Fully Authorized Account Refs as Parameters

// BAD — gives full storage access to callee
access(all) fun setup(admin: auth(Storage) &Account) { ... }

// GOOD — use a narrowly-scoped capability instead
access(all) fun setup(adminCap: Capability<&Admin>) { ... }

S3 — Always Validate with Pre/Post Conditions

access(Withdraw) fun withdraw(amount: UFix64): @Vault {
    pre  { amount > 0.0:                             "Amount must be positive" }
    pre  { self.balance >= amount:                   "Insufficient balance" }
    post { result.balance == amount:                 "Withdrawal amount mismatch" }
    post { self.balance == before(self.balance) - amount: "Balance accounting error" }
}

S4 — Meaningful Panic Messages (Never Force-Unwrap Silently)

// BAD — silent crash
let ref = cap.borrow()!

// GOOD — actionable message
let ref = cap.borrow()
    ?? panic("Could not borrow Vault — capability may be revoked or incorrect type")

S5 — Private Data Is Not Secret

access(self) controls programmatic access, not blockchain visibility. All storage is publicly readable off-chain. Never store secrets, private keys, or PII on-chain.

S6 — Never Hard-Code Addresses

// BAD
import FungibleToken from 0xf233dcee88fe0abe

// GOOD (uses flow.json aliases, resolved at deployment)
import "FungibleToken"

S7 — Check Before Setup (Idempotent Transactions)

transaction {
    prepare(signer: auth(SaveValue) &Account) {
        if signer.storage.type(at: /storage/vault) != nil { return }
        signer.storage.save(<- MyToken.createEmptyVault(), to: /storage/vault)
    }
}

9. Testing

Tests are Cadence files using the Test standard library, executed via Flow CLI.

import Test

let blockchain = Test.newEmulatorBlockchain()
let admin = blockchain.createAccount()

access(all) fun setup() {
    let err = blockchain.deployContract(
        name: "MyNFT",
        code: Test.readFile("../contracts/MyNFT.cdc"),
        account: admin,
        arguments: []
    )
    Test.expect(err, Test.beNil())
}

access(all) fun testMint() {
    let tx = Test.Transaction(
        code: Test.readFile("../transactions/mint.cdc"),
        authorizers: [admin.address],
        signers: [admin],
        arguments: ["My NFT"]
    )
    Test.expect(blockchain.executeTransaction(tx), Test.beSucceeded())

    let events = Test.eventsOfType(Type<MyNFT.Minted>())
    Test.assertEqual(1, events.length)
}

access(all) fun testQuery() {
    let result = blockchain.executeScript(
        Test.readFile("../scripts/get_ids.cdc"),
        [admin.address]
    )
    let ids = result.returnValue! as! [UInt64]
    Test.assertEqual(1, ids.length)
}

Common assertions:

Test.assertEqual(expected, actual)
Test.assert(condition, message: "reason")
Test.expect(result, Test.beSucceeded())
Test.expect(result, Test.beFailed())
Test.expect(err, Test.beNil())

Run tests:

flow test tests/my_test.cdc
flow test --cover tests/my_test.cdc

10. Naming Conventions

ConstructConventionExample
Contract, Resource, Struct, Interface, EventPascalCaseFlowToken, TokenVault
Function, variable, parametercamelCasegetBalance(), totalSupply
Storage/public pathslet fields on the contractlet VaultStoragePath: StoragePath
EntitlementsPascalCaseentitlement Withdraw

Never hardcode path strings in transactions — always reference the contract's published path constants.


11. Anti-Patterns

A1 — Never Pass Fully Authorized Account Refs as Parameters

A function accepting auth(Storage) &Account can access all storage — drain vaults, steal NFTs.

// BAD — callee can withdraw FLOW or modify anything
access(all) fun transferNFT(id: UInt64, owner: auth(Storage) &Account) { ... }

// GOOD — authenticate via resources/capabilities
access(all) fun transferNFT(id: UInt64, collectionCap: Capability<auth(Withdraw) &Collection>) { ... }

A2 — Public Functions Should Be Read-Only or Explicitly Entitled

Only view functions and functions everyone should call should be access(all). State-modifying functions require entitlements.

A3 — Capability-Typed Public Fields Are Security Holes

Capabilities are value types — a public field can be copied by anyone:

// BAD — anyone copies the capability and calls its functions
access(all) var adminCap: Capability<&Admin>

// GOOD
access(self) var adminCap: Capability<&Admin>
access(Owner) fun getAdminCap(): Capability<&Admin> { return self.adminCap }

A4 — Never Make Admin Creation Functions Public

// BAD — anyone mints admin access
access(all) fun createAdmin(): @Admin { return <- create Admin() }

// GOOD — create once in init, existing admins create new ones
init() {
    self.account.storage.save(<- create Admin(), to: /storage/currencyAdmin)
}

A5 — Never Emit Events or Modify Contract State in Struct Initializers

Structs are public and can be created by anyone. Side effects in init() can be exploited:

// BAD — anyone spams events and overflows nextPlayID
access(all) struct Play {
    init() {
        TopShot.nextPlayID = TopShot.nextPlayID + 1  // BAD
        emit PlayCreated(id: self.playID)             // BAD
    }
}

// GOOD — state changes happen only inside admin resource
access(all) resource Admin {
    access(all) fun createPlay() {
        var newPlay = Play()
        TopShot.nextPlayID = TopShot.nextPlayID + UInt32(1)
        emit PlayCreated(id: newPlay.playID)
    }
}

A6 — Complex/Capability Fields Must Be access(self)

access(all) on arrays, dictionaries, structs, resources, or capabilities allows direct mutation:

// BAD
access(all) var adminCap: Capability<&Admin>

// GOOD
access(self) var adminCap: Capability<&Admin>

12. Capability Bootstrapping (Inbox API)

When account A needs to send a capability to account B, a single transaction cannot be signed by both accounts simultaneously. Use the Inbox API:

Step 1 — Provider publishes the capability to recipient's inbox:

import "BasicNFT"

transaction(receiver: Address, name: String) {
    prepare(signer: auth(IssueStorageCapabilityController, PublishInboxCapability) &Account) {
        let capability = signer.capabilities.storage
            .issue<&BasicNFT.Minter>(BasicNFT.minterPath)

        let controller = signer.capabilities.storage
            .getController(byCapabilityID: capability.id)
            ?? panic("Controller not found")
        controller.setTag(name)

        signer.inbox.publish(capability, name: name, recipient: receiver)
    }
}

Step 2 — Recipient claims the capability:

import "BasicNFT"

transaction(provider: Address, name: String) {
    prepare(signer: auth(ClaimInboxCapability, SaveValue) &Account) {
        let capability = signer.inbox.claim<&BasicNFT.Minter>(name, provider: provider)
            ?? panic("No capability named '\(name)' from \(provider)")
        signer.storage.save(capability, to: BasicNFT.minterPath)
    }
}

The provider can call signer.inbox.unpublish(name) to retract before the recipient claims.


13. Performance: Prefer borrow Over load/save

load() moves the resource out of storage (expensive), save() moves it back (expensive again). borrow() returns an in-place reference — always prefer it.

// BAD — unnecessary round-trip moves the entire resource
transaction {
    prepare(acct: auth(LoadValue, SaveValue) &Account) {
        let vault <- acct.storage.load<@ExampleToken.Vault>(from: /storage/exampleToken)!
        let burned <- vault.withdraw(amount: 10)
        destroy burned
        acct.storage.save(<- vault, to: /storage/exampleToken)
    }
}

// GOOD — borrow a reference, mutate in-place, no save needed
transaction {
    prepare(acct: auth(BorrowValue) &Account) {
        let vault = acct.storage.borrow<&ExampleToken.Vault>(from: /storage/exampleToken)!
        let burned <- vault.withdraw(amount: 10)
        destroy burned
    }
}

14. Measuring Time

Flow produces blocks approximately every 0.8 seconds. Both block timestamp and block height are available on-chain.

let block = getCurrentBlock()
block.timestamp   // Unix timestamp (UFix64, seconds)
block.height      // Block number (UInt64)

let pastBlock = getBlock(at: 70001)
pastBlock?.timestamp
pastBlock?.height
MethodUse when
getCurrentBlock().timestampAcceptable for events lasting hours/days. Accurate to ~10 seconds.
getCurrentBlock().heightMore manipulation-resistant. Requires off-chain rate estimation.

Rules:

  • Timestamps cannot go backwards and cannot be more than 10 seconds ahead of the previous block.
  • Never hardcode an assumed block rate — it changes over time.
  • Auctions and time-locks should have an extension mechanism.

15. Contract Upgrades

Compatible Changes (Preferred)

Cadence supports in-place upgrades for additive changes: adding new fields (with defaults), new functions, new events. Use flow contracts update.

Incompatible Changes

Option A — New address (safest):

  1. Deploy new contract to a new account.
  2. Increment path suffixes (/public/MyVault002).
  3. Write upgrade transactions to migrate users' resources.

Option B — Same address (risky, last resort):

  1. Delete all resources in the contract account (e.g., Admin resource).
  2. Delete the contract from the account.
  3. Deploy the new contract.

⚠️ If any user account holds structs or resources from the old contract version, those will **fail to

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Security

Aws Security

List of open source tools for AWS security: defensive, offensive, auditing, DFIR, etc. my arsenal of aws security tools, shell, auditing, aws-infrastructure,...

Registry SourceRecently Updated
Security

Nginx Config

Nginx配置生成。服务器配置、反向代理、SSL、缓存、安全加固、性能优化。Nginx config generator with reverse proxy, SSL, caching, security, optimization. Nginx、服务器、运维。

Registry SourceRecently Updated
1770Profile unavailable
Security

Watadot Aws Iam

IAM security patterns by Watadot Studio. Manage users, roles, and policy verification.

Registry SourceRecently Updated
80Profile unavailable
Security

Adversarial Robustness Toolbox

Adversarial Robustness Toolbox (ART) - Python Library for Machine Learning Security - Evasion, Poiso adversarial robustness toolbox, python, adversarial-atta...

Registry SourceRecently Updated
1150Profile unavailable