惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Apple Machine Learning Research
Apple Machine Learning Research
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tenable Blog
P
Proofpoint News Feed
爱范儿
爱范儿
The Hacker News
The Hacker News
W
WeLiveSecurity
WordPress大学
WordPress大学
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
The Register - Security
The Register - Security
I
Intezer
S
SegmentFault 最新的问题
P
Privacy & Cybersecurity Law Blog
人人都是产品经理
人人都是产品经理
Security Archives - TechRepublic
Security Archives - TechRepublic
G
Google Developers Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
G
GRAHAM CLULEY
MongoDB | Blog
MongoDB | Blog
Blog — PlanetScale
Blog — PlanetScale
Hugging Face - Blog
Hugging Face - Blog
P
Palo Alto Networks Blog
A
About on SuperTechFans
Attack and Defense Labs
Attack and Defense Labs
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园_首页
SecWiki News
SecWiki News
博客园 - 聂微东
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hacker News - Newest:
Hacker News - Newest: "LLM"
Last Week in AI
Last Week in AI
Forbes - Security
Forbes - Security
C
Cyber Attacks, Cyber Crime and Cyber Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
F
Full Disclosure
Help Net Security
Help Net Security
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
P
Proofpoint News Feed
S
Schneier on Security
K
Kaspersky official blog
S
Secure Thoughts
C
Cisco Blogs
T
Tor Project blog
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
LangChain Blog
Hacker News: Ask HN
Hacker News: Ask HN
N
News and Events Feed by Topic
MyScale Blog
MyScale Blog

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant Common SOC 2 Failures (Real World) Stop Vibe-Checking Your AI App: A Practical Guide to Evals How to Use SonarQube and SonarScanner Locally to Level Up Your Code Quality Your Next To-Do App Is Dead — I Replaced Mine with an OpenClaw AI Sign a Nostr event in 60 lines of Python using coincurve — no nostr-sdk, no nbxplorer, no rust toolchain ITGC Audit Explained Like You’re in Big 4 Patch Tuesday abril 2026: Microsoft parcha 163 vulnerabilidades y un zero-day en SharePoint Stop scraping everything: a better way to track competitor price changes Listing on MCPize + the Official MCP Registry while routing payments OUTSIDE the marketplace — how I kept 100% of my x402 revenue Building an AI-Powered Risk Intelligence System Using Serverless Architecture Why We Ripped Function Overloading Out of Our AI Toolchain Testing AI-Generated Code: How to Actually Know If It Works SaaS Churn Is Killing Your Business. Here Is What to Do About It (Without a Support Team) The Speed of AI Is No Longer Linear - And Self-Improving Models Are Why How to Implement RBAC for MCP Tools: A Practical Guide for Engineering Teams From Standard Quote to Persuasive Proposal: AI Automation for Arborists I built a CLI that scaffolds complete multi-tenant SaaS apps Axios CVE-2025–62718: The Silent SSRF Bug That Could Be Hiding in Your Node.js App Right Now The dashboard that ended our friendship Data Pipelines Explained Simply (and How to Build Them with Python) The Hidden Cost of AI Systems Nobody Talks About. undefined vs undeclared, and how typeof behaves Switching from file-based jobs to NATS/Kafka in Rust without changing code io_uring Adventures: Rust Servers That Love Syscalls Why Agentic AI is Killing the Traditional Database The POUR principles of web accessibility for developers and designers Quantum Neural Network 3D — A Deep Dive into Interactive WebGL Visualization How To Install Caveman In Codex On macOS And Windows Automation Pipeline Reliability: Why Your Workflow Breaks When Nobody Is Watching I Built an 'Open World' AI Coding Agent — It Works From ANY Folder From Freelancing to Product: A Tech Service Company's SaaS Transformation China's AI Giants: Adding Tencent Hunyuan & ByteDance Doubao to AI University (74 Providers) On the Vibe Coders and Their Lies clerk: Auto-Summarize Your Claude Code Sessions AI Weekly — 2026/04/10–04/17 | The Model Lockdown Is Here, but the Toolchain Is the Real Battleground AI 週報 — 2026/04/10–2026/04/17 模型封鎖潮來了,但工具鏈才是真戰場 Maybe this is how Open-Source apps are born... 🚀 Fine-Tune LLMs with LoRA and QLoRA: 2026 Guide tRPC v11 + Next.js App Router: End-to-End Type Safety Without the Boilerplate ShadCN UI in 2026: Why I Stopped Installing Component Libraries and Started Owning My Components SaaS Billing in React Server Components: Stripe + Supabase Without a Single `useEffect` Join our DEV Weekend Challenge — $1,000 in Prizes Across TEN winners! Submissions Due April 20 at 6:59 AM UTC. Implementing FSRS Spaced Repetition in Flutter + Supabase — Adding Memory Science to an AI Learning App "I Texted My Localhost From the Train — Claude Code Fixed the Bug Before I Got Home" I Built a Sales Prep AI and It Went Deeper Than Expected Design to Code #2: One JSON, Eleven Outputs Solving the 100M-Row Problem: A Summary Table Pattern for High-Volume Push Notification Logs Flutter Web With Wasm: What Actually Changes For Developers I Built 50 Royalty-Free Soundtracks for My Side Project in a Weekend Using AI Music Generation The Vibe Coding Security Checklist: 7 Things to Check Before You Ship Stop Letting Googlebot Guess Fix Your React App's SEO Right Desconstruindo o Streaming do LinkedIn: Como Criar um Engine de Extração de Vídeo de Alta Performance com HLS e FFmpeg (EDA Part-1) EDA (Exploratory Data Analysis) Explained With Real Life — Why Looking at Your Data Is the Most Important Step in Machine Learning Brand Relationship Management at Scale: Our 4-Touch Outreach System for 200+ Brands Why String.fromEnvironment() Might Return an Empty String in Dart JGuardrails 1.0.0 — Hardening Java LLM Apps Against Jailbreaks, Toxicity, and Prompt Injection Plan and Schedule a Full Week of Threads Content From One Claude Conversation Coding Cat Oran Ep3, Five Tables Changed Everything Updated: BFF Pattern I'm done watching freelancers get buried by 200 proposals. So I'm building the alternative. This is my first post BFS Algorithm in Java Step by Step Tutorial with Examples Tracking LLM Pricing Monthly: An Open Dataset for 22 AI Models How We Measure Content ROI on a Comparison Site: Revenue Attribution Without Perfect Data Introducing Nova AI Ops: The AI-Native Operating System for SRE Teams I built a free desktop video downloader for Windows — Grabbit How Talkie OCR Helps Vision-Impaired & Dyslexic Users Read the World Around Them VRCFaceTracking安装和iPhone面捕配置教程,有bug Even CrowdStrike Can't See Your Agents The Automation Gold Rush: What n8n Workflows and Claude Are Opening Up for Developers Right Now
Contract-State Accounting vs UTXO Tokens: Two Models for On-Chain Value
Ameer Abdula · 2026-05-03 · via DEV Community

Contract-State Accounting vs UTXO Tokens## INTRODUCTION

Every blockchain application that handles value needs to answer the same question: how do you track who owns what? There are two dominant approaches, and choosing between them shapes your entire contract architecture.

Contract-state accounting behaves like a bank ledger. A single smart contract holds a balance map, and transactions update entries in place. The UTXO model behaves like physical cash. Each unit of value exists as an independent object that gets consumed and recreated with every transfer.

Midnight supports both patterns, and each has different tradeoffs for privacy, complexity, and scalability. This tutorial explains both models, shows you how to implement them in Compact, and helps you decide which one fits your use case.

Prerequisites

Before you continue, make sure you have:

  • The Compact toolchain installed by following the installation guide
  • A working understanding of Compact syntax from the Hello World tutorial
  • Familiarity with the disclose() function and selective disclosure concepts

What Is Contract-State Accounting?

Contract-state accounting uses a central registry. The smart contract maintains a single data structure, typically a Map, that tracks every account balance. When Alice sends 50 tokens to Bob, the contract decreases Alice's balance by 50 and increases Bob's balance by 50. The total supply is implicit in the sum of all balances.

This model mirrors how a bank operates. The bank keeps a ledger, and every transaction is a double entry update to that ledger.

Here is a basic implementation in Compact:

pragma language_version 0.22;
import CompactStandardLibrary;

export ledger balances: Map<Bytes<32>, Uint<64>>;
export ledger totalSupply: Uint<64>;

witness localSk(): Bytes<32>;

constructor(initialSupply: Uint<64>) {
    const _sk = localSk();
    let creator = getDappPublicKey(_sk);
    balances.insert(disclose(creator), disclose(initialSupply));
    totalSupply = disclose(initialSupply);
}

export circuit transfer(
    recipient: Bytes<32>,
    amount: Uint<64>
): [] {
    const _sk = localSk();
    let sender = getDappPublicKey(_sk);

    let senderBalance = balances.lookup(sender);
    assert(senderBalance >= amount, "Insufficient balance");

    let newSenderBalance = senderBalance - amount;
    balances.insert(sender, newSenderBalance);

    let recipientBalance = balances.lookup(recipient);
    let newRecipientBalance = recipientBalance + amount;
    balances.insert(recipient, newRecipientBalance);
}

export circuit getDappPublicKey(_sk: Bytes<32>): Bytes<32> {
    return persistentHash<Vector<2, Bytes<32>>>([
        pad(32, "accounting:pk:"),
        _sk
    ]);
}

Enter fullscreen mode Exit fullscreen mode

This implementation is straightforward. Every transfer reads two balances, performs arithmetic, and writes two updated balances. The state changes are destructive. Once Alice's balance is updated, the previous balance is gone forever.

Advantages of Contract-State Accounting

Simplicity: The mental model is intuitive. Developers coming from traditional backend systems immediately understand a balance map.

Gas predictability: Each transfer performs a constant number of operations regardless of transaction history. Two map lookups, two map insertions, and some arithmetic.

Easy aggregation: If you need to know the total supply or the number of holders, the data is right there in the contract state. You can query it directly.

Familiar tooling: The pattern works well with standard wallet interfaces. Users see a balance and a send button.

Disadvantages of Contract-State Accounting

Privacy challenges: In a fully public implementation, every balance is visible to every observer. Midnight mitigates this with ZK proofs, but the pattern still requires careful selective disclosure design to protect individual balances.

State bloat: The balance map grows with every new holder and never shrinks. Even accounts with zero balance may need to remain to preserve historical data.

Concurrency issues: If two transactions try to update the same balance simultaneously, one will fail. This creates friction in high throughput applications.

What Is the UTXO Model?

UTXO stands for Unspent Transaction Output. The mental model is physical cash. When you hold a ten dollar bill, that bill is a discrete object with its own serial number and value. When you pay for something that costs seven dollars using that ten dollar bill, two things happen: the ten dollar bill is destroyed, a seven dollar bill goes to the merchant, and a three dollar bill comes back to you as change.

In a UTXO system, every unit of value is a distinct object stored on chain. Each object has an owner and an amount. A transaction consumes input UTXOs and creates output UTXOs. The sum of input amounts must equal the sum of output amounts, preserving the total supply.

Here is a Compact implementation of a basic UTXO token:

pragma language_version 0.22;
import CompactStandardLibrary;

export ledger utxos: Map<Bytes<32>, Uint<64>>;
export ledger utxoOwners: Map<Bytes<32>, Bytes<32>>;

witness localSk(): Bytes<32>;

constructor(initialSupply: Uint<64>) {
    const _sk = localSk();
    let creator = getDappPublicKey(_sk);

    let utxoId = persistentHash<Vector<2, Bytes<32>>>([
        pad(32, "utxo:genesis:"),
        _sk
    ]);

    utxos.insert(disclose(utxoId), disclose(initialSupply));
    utxoOwners.insert(disclose(utxoId), disclose(creator));
}

export circuit transfer(
    inputUtxoId: Opaque<Bytes<32>>,
    outputAmount1: Uint<64>,
    outputAmount2: Uint<64>,
    recipient1: Bytes<32>,
    recipient2: Bytes<32>
): [] {
    // Verify ownership of the input UTXO
    const _sk = localSk();
    let sender = getDappPublicKey(_sk);

    let utxoId = disclose(inputUtxoId);
    assert(utxos.member(utxoId), "UTXO does not exist");
    assert(utxoOwners.lookup(utxoId) == sender, "You do not own this UTXO");

    let inputAmount = utxos.lookup(utxoId);

    // Verify amount conservation
    assert(
        inputAmount == outputAmount1 + outputAmount2,
        "Input and output amounts must match"
    );

    // Destroy the input UTXO
    utxos.remove(utxoId);
    utxoOwners.remove(utxoId);

    // Create output UTXOs
    if (outputAmount1 > 0) {
        let newUtxoId1 = persistentHash<Vector<3, Bytes<32>>>([
            pad(32, "utxo:output:"),
            utxoId,
            pad(32, "1")
        ]);
        utxos.insert(disclose(newUtxoId1), outputAmount1);
        utxoOwners.insert(disclose(newUtxoId1), recipient1);
    }

    if (outputAmount2 > 0) {
        let newUtxoId2 = persistentHash<Vector<3, Bytes<32>>>([
            pad(32, "utxo:output:"),
            utxoId,
            pad(32, "2")
        ]);
        utxos.insert(disclose(newUtxoId2), outputAmount2);
        utxoOwners.insert(disclose(newUtxoId2), recipient2);
    }
}

export circuit getDappPublicKey(_sk: Bytes<32>): Bytes<32> {
    return persistentHash<Vector<2, Bytes<32>>>([
        pad(32, "utxo:pk:"),
        _sk
    ]);
}

Enter fullscreen mode Exit fullscreen mode

This implementation captures the essence of the UTXO model. Each UTXO is a distinct entry in the utxos map with a corresponding owner. A transfer destroys one UTXO and creates up to two new ones, typically one for the recipient and one for change back to the sender.

Advantages of the UTXO Model

Natural privacy; Because UTXOs are independent objects, you can use different keys for each one. An observer cannot trivially link multiple UTXOs to the same owner without additional information.

Parallelism; Transactions that consume different UTXOs do not conflict with each other. Alice sending to Bob and Charlie sending to Dave can process simultaneously because they touch different state entries.

Auditability; The total supply is preserved by construction. Every transaction must balance, so you can verify the entire history without trusting a central authority.

Resistance to replay; Each UTXO has a unique identifier. Once consumed, a UTXO cannot be used again because it no longer exists in the state.

Disadvantages of the UTXO Model

Complexity; The model is less intuitive for developers used to account-based systems. Generating change outputs, selecting which UTXOs to consume, and managing fragmented balances adds surface area for bugs.

Fragmentation; Over time, a user may accumulate many small UTXOs. Spending a large amount requires combining multiple inputs, which increases transaction size and complexity.

State growth; Every transaction creates new UTXOs. Even as old ones are consumed, the total number of UTXOs in existence tends to grow, especially if users do not consolidate their holdings.

Comparing the Two Models

Let us examine the same scenario implemented in both models to highlight the practical differences.

Scenario: Alice sends 30 tokens to Bob

Contract-state accounting:

The contract reads Alice's balance. If she has at least 30 tokens, it subtracts 30 from her balance and adds 30 to Bob's balance. Two state entries are updated. The transaction is a single unit of work.

UTXO model:

Alice's wallet selects one or more UTXOs that sum to at least 30 tokens. Suppose she has a single UTXO worth 50 tokens. The transaction consumes that UTXO and creates two new ones: 30 tokens owned by Bob and 20 tokens owned by Alice as change. One state entry is destroyed, two are created.

The UTXO approach creates more on-chain objects, but each object reveals less about the overall distribution of funds. An observer sees a 50 token UTXO disappear and a 30 and 20 appear, but they cannot trivially tell which is the payment and which is the change without additional analysis.

When to Choose Contract-State Accounting

Choose this model when:

  • Your application needs frequent balance queries from the contract itself
  • You are building something familiar like a token with standard wallet integrations
  • The user experience of seeing a single balance matters more than transaction level privacy
  • Your total number of holders is reasonably bounded

When to Choose the UTXO Model

Choose this model when:

  • Privacy is a primary design goal and you want to avoid balance correlation
  • Your application benefits from parallel transaction processing
  • You need strong guarantees about supply preservation that are enforced by the transaction structure itself
  • You are building payment systems or exchange mechanisms

Hybrid Approaches

You are not forced to pick one model exclusively. Many applications combine elements of both patterns.

For example, a DApp might store user balances as a map for quick lookups while also issuing UTXO style receipts for individual transactions. The balance map provides convenience, and the receipts provide auditability.

Here is a sketch of how that might look:

pragma language_version 0.22;
import CompactStandardLibrary;

export ledger balanceMap: Map<Bytes<32>, Uint<64>>;
export ledger transactionReceipts: Map<Bytes<32>, Bytes<32>>;

witness localSk(): Bytes<32>;

export circuit deposit(amount: Opaque<Uint<64>>): [] {
    const _sk = localSk();
    let user = getDappPublicKey(_sk);

    let currentBalance = balanceMap.lookup(user);
    let newBalance = currentBalance + disclose(amount);
    balanceMap.insert(user, newBalance);

    let receiptId = persistentHash<Vector<2, Bytes<32>>>([
        pad(32, "receipt:"),
        _sk
    ]);
    let receiptData = pad(64, "deposit");
    transactionReceipts.insert(disclose(receiptId), receiptData);
}

export circuit getDappPublicKey(_sk: Bytes<32>): Bytes<32> {
    return persistentHash<Vector<2, Bytes<32>>>([
        pad(32, "hybrid:pk:"),
        _sk
    ]);
}

Enter fullscreen mode Exit fullscreen mode

The balance map handles the operational needs of the application. The receipt map provides an immutable audit trail. Each solves a different problem, and together they offer more than either alone.

Practical Recommendations

Based on these models, here is a decision framework for your own contracts:

  1. Start with contract-state accounting if you are building a straightforward token or a DApp where user experience and developer simplicity are priorities. The pattern is easier to get right and easier to debug.

  2. Move to the UTXO model if you discover privacy or concurrency requirements that the account model cannot satisfy. The additional complexity is worth it when these properties matter.

  3. Consider a hybrid approach if you need both operational efficiency and an audit trail. Just be clear about which state serves which purpose.

  4. Regardless of which model you choose, apply domain separated hashing for all user identifiers. This prevents cross DApp tracking and preserves the privacy guarantees you are building into your contract logic.

  5. Test your assumptions on a local Devnet before deploying to a public testnet. Both models behave differently under load, and early testing catches architecture problems before they become expensive.

A Privacy Note on Both Models

Neither model automatically guarantees privacy. In contract-state accounting, if you disclose user public keys directly in the balance map, every balance becomes linkable to every transaction that user makes. In the UTXO model, if you reuse the same owner key across multiple UTXOs, you create the same linkability problem.

Always derive DApp specific public keys using domain separation. Consider whether you can use commitments instead of raw values. And apply the selective disclosure audit checklist covered in the companion tutorial on disclosure patterns.

Next Steps

Understanding these two models gives you a foundation for designing value transfer systems on Midnight. To deepen your knowledge:

  1. Study the Token Transfers example in the Midnight documentation to see a production oriented implementation.

  2. Experiment with both patterns using the create-mn-app scaffolding tool. Build the same simple token with each model and compare the developer experience.

  3. Join the Midnight Discord and share your implementation. The community is actively discussing these design patterns.

  4. Apply the selective disclosure audit checklist from the companion tutorial to whichever model you choose. Privacy is not automatic, it requires deliberate design.

Contract-state accounting and the UTXO model are both valid paths. The right choice depends on your application requirements and your users needs. Now you have the tools to make that choice confidently.