Skip to main content

3 posts tagged with "midnight"

View All Tags

Introduction to the FungibleToken contract on Midnight

· 11 min read
Claude Barde
Developer Relations

Introduction to the FungibleToken contract on Midnight

Midnight is a privacy-first blockchain designed to bring privacy to decentralized applications. It achieves this through zero-knowledge proofs, programmable data protection, and developer-friendly tools like Compact, a TypeScript-based DSL (Domain-Specific Language) for writing privacy-aware smart contracts.

OpenZeppelin is renowned in the Ethereum ecosystem for its battle-tested smart contract libraries, which have secured trillions in on-chain value. Recently, OpenZeppelin partnered with Midnight to bring comparable tooling to the Compact ecosystem, adapting familiar standards like ERC-20 into privacy-preserving variants.

In the Ethereum world, the ERC-20 standard defines a fungible token with public ledger functions like balanceOf, transfer, approve, etc. It exposes transaction data transparently and lacks built-in privacy. The FungibleToken contract on Midnight draws inspiration from this, but operates within Midnight’s zero-knowledge, selective-disclosure framework.

Fungible tokens are a cornerstone of the blockchain ecosystem, representing digital assets that are interchangeable – much like traditional currency. On various blockchains, these tokens power a wide array of applications, from facilitating seamless transactions and enabling decentralized finance (DeFi) protocols to representing ownership in digital communities and driving the mechanics of in-game economies.

Unlike unique non-fungible tokens (NFTs), the value of one fungible token is identical to another of the same type, making them ideal for use cases requiring divisibility and ease of exchange. Their widespread adoption underscores their importance in building liquid and interconnected digital economies.

In this article, you'll learn about the core features of the contract, including how it manages ledger state variables, its key entry points and circuits for operations like minting, burning, and transferring tokens, and the essential safety and utility functions provided by the Utils and Initializable modules.
By understanding how these components fit together, you’ll gain insight into how the FungibleToken contract balances fungibility, usability, and privacy, providing an essential building block for privacy-preserving DeFi, identity, and tokenized assets on Midnight.


Features of the FungibleToken Contract

The FungibleToken contract on Midnight utilizes ledger state variables to keep track of balances, allowances, total supply, name, symbol, and decimals. Its functionality is exposed through "circuits" (entry points) like Mint, Burn, Transfer, Approve, TransferFrom, and Initialize, all of which enforce specific zero-knowledge validated transitions and maintain the integrity of the token's state.

Ledger State Variables

In Compact, the contract defines a structured state storing token balances and allowances—similar to ERC-20. The _balances map keeps track of the users’ token balances and is updated when a transfer occurs. The _allowances map keeps track of the permission given to specific users to spend tokens on behalf of another user:

export ledger _balances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>;
export ledger _allowances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>>;

These values live in the contract's ledger and are updated through transactions sent to the contract.

There are other values in the ledger that are set when the contract is deployed:

export ledger _totalSupply: Uint<128>;
export sealed ledger _name: Opaque<"string">;
export sealed ledger _symbol: Opaque<"string">;
export sealed ledger _decimals: Uint<8>;

These values provide different information about the token managed by the contract, its total supply, its name, its symbol, and its decimal (for display).

Entry Points and Circuits

In Compact, entry points are defined as circuits (akin to Solidity functions), each modelling a zero-knowledge validated transition. The difference between a circuit entry point and a circuit is that the entry point is callable via a transaction, while the non-entry point circuit is internal. Core circuits include:

  • Mint / Burn (to mint new tokens or burn existing tokens).

  • Transfer: to move tokens between addresses.

  • Approve, TransferFrom: standard ERC-20-style delegation mechanisms.

  • Initialize: via the Initializable module for contract setup.

Each circuit enforces necessary constraints — for example, ensuring sufficient balance, managing allowance decrements, and preserving total supply.

In the next step of the contract lifecycle, the different metadata stored in the ledger of the contract are safely initialized.

Initialization and Metadata**

The following circuits define the essential setup and retrieval logic for the fungible token metadata and users’ balances, enforcing correct initialization.

  • initialize(name_, symbol_, decimals_)
    One-time setup. Calls Initializable_initialize(), then stores the (disclosed) name, symbol, and decimals. Every other public circuit asserts that the contract is initialized first.

  • name() / symbol() / decimals() / totalSupply()
    Simple getters that first assert initialized, then return the sealed (read only) ledger values.

  • balanceOf(account)
    Safe map lookup that returns 0 if the account isn’t present (to prevent contract failure if the key is absent).


The Transfer Family

The FungibleToken contract's transfer circuits manage token movement. Key circuits include: transfer for safe user-initiated transfers, _unsafeTransfer for internal token movement, _transfer for administrative transfers, _unsafeUncheckedTransfer for low-level token movement, and _update as the central accounting function for all token operations.

These are split into safe and unsafe variants because sending to contract addresses is currently disallowed (until contract-to-contract interactions are supported).

“Safe” circuits enforce that policy; “unsafe” ones let you bypass it—explicitly marked as dangerous in comments.

  • transfer(to, value)Boolean
    Safe user-initiated transfer: rejects if to is a ContractAddress. Internally, it just forwards to the unsafe variant after the check.

  • _unsafeTransfer(to, value)Boolean
    Owner is the caller (left(ownPublicKey())). Moves value using the unchecked internal mover, then returns true.

  • _transfer(from, to, value)[]
    Admin/extension hook that moves tokens from an arbitrary from (not necessarily the caller). Still enforces the “no contracts as to” rule and then uses the same mover underneath.

  • _unsafeUncheckedTransfer(from, to, value)[]
    The low-level mover checks that neither side is the zero/burn address and then delegates the actual accounting to _update.

  • _update(from, to, value)[]
    Central accounting function used by all mint/burn/transfer paths. It’s an internal circuit; it cannot be called via a transaction.

    • If from is zero, the mint circuit is called, it asserts no uint128 overflow, and increases _totalSupply.

    • Else, it deducts from from balance (or reverts on insufficient funds).

    • If to is zero, the burn circuit is called, and it decreases _totalSupply.

    • Else, it adds to to balance.
      This single function guarantees the invariants for every movement of value.

The "transfer family" circuits ensure secure token movement, with "safe" variants disallowing transfers to contract addresses and "unsafe" variants providing lower-level control.
This leads us to explore how allowances function, enabling delegated token spending.


Allowances (approve / spend / transferFrom)

This section details the allowance mechanisms within the FungibleToken contract, which enable users to delegate spending permissions to other addresses. These circuits facilitate secure, approved transfers on behalf of an owner without directly exposing their private keys.

  • allowance(owner, spender)
    Read the nested _allowances map, returning 0 when keys are missing (no revert).

  • approve(spender, value)Boolean
    The owner is the caller. Forwards to _approve(owner, spender, value) and returns true.

  • transferFrom(from, to, value)Boolean
    Safe delegated transfer: enforces the “no contract receiver” rule, then defers to _unsafeTransferFrom.

  • _unsafeTransferFrom(from, to, value)Boolean
    The spender is the caller. First spends allowance via _spendAllowance(from, spender, value), then moves value using _unsafeUncheckedTransfer. Returns true.

  • _approve(owner, spender, value)[]
    It ensures that both the owner and the spender are valid, creates the owner’s entry in the map if needed, and then writes the allowance. (This mirrors OZ’s ERC-20 pattern of public approve() → internal _approve().)

  • _spendAllowance(owner, spender, value)[]
    It deducts from the allowance unless it’s “infinite.” The implementation treats MAX_UINT128 as infinite: if currentAllowance == MAX, it doesn’t decrement; otherwise, it asserts currentAllowance ≥ value and writes back currentAllowance - value.
    This is important because it supports “no-friction approvals” by letting apps set MAX once.

So, we just covered how allowances let people delegate token spending—basically, giving others permission to move their tokens. Up next, we'll dive into how we create and delete tokens in the contract.


Minting and Burning

Here's how the FungibleToken contract handles making and destroying tokens. We'll dive into the _mint and _burn functions, showing what they do and how they link up with the main accounting system.

  • _mint(account, value) (safe) → []
    It forbids minting to a contract address (same contract-to-contract restriction), then forwards to _unsafeMint.

  • _unsafeMint(account, value)[]
    It validates the receiver’s address, then calls _update(burnAddress(), account, value)—i.e., mint is modelled as a transfer from the burn/zero address.

  • _burn(account, value)[]
    It validates the sender’s address, then calls _update(account, burnAddress(), value)—i.e., burn is a transfer to the burn/zero address.
    Note: The actual notion of “zero/burn” address is standardized in the Utils module; you can also see helpers like Utils_isKeyOrAddressZero and Utils_isContractAddress.

Because mint and burn also route through _update, total supply is adjusted in exactly one place, and the same safety checks apply across all flows (including the uint128 overflow check on mint).

The mint and burn circuits, by using the _update function, make sure the total supply adjustments are always consistent and that all token flows get the same safety checks.
Now, let's dive into the extra safety and utility stuff that the Utils and Initializable modules bring to the table.


Safety and Utility Glue (from Utils and Initializable)

This section explores how the Utils and Initializable modules provide essential safeguards and helpful functionalities. These components are vital for ensuring the contract's integrity and enabling secure, well-managed operations.

  • Initialization guards: The Initializable_initialize and Initializable_assertInitialized functions serve as crucial initialization guards within the Initializable contract. These safeguards ensure that a contract's state is properly set up only once and that subsequent operations only proceed if the contract has been correctly initialized. Every circuit that interacts with or modifies the contract's state is designed to invoke the assert function, reinforcing the integrity of the initialization process.

  • Address helpers:

    • Utils_isContractAddress(either) distinguishes user keys from contract addresses.

    • Utils_isKeyOrAddressZero(either) detects the zero/burn address used in _update, _unsafeUncheckedTransfer, etc.
      These support the temporary “no contract receiver” policy and zero-address checks.

The Utils and Initializable modules provide crucial safety and utility functions, ensuring the contract's proper setup and secure operation. Now, let's look at how all these different parts of the FungibleToken contract work together.


How the Pieces Fit Together

This part shows how everything in the FungibleToken contract is hooked up. Whether it's you sending tokens, someone else doing it for you, or tokens being created or destroyed, it all funnels through a few key functions and ultimately lands in the main _update function to keep track of everything.

  • User transfer:
    transfer(safe check)_unsafeTransfer_unsafeUncheckedTransfer_update (balances/supply)

  • Delegated transfer:
    transferFrom(safe check)_unsafeTransferFrom_spendAllowance_unsafeUncheckedTransfer_update

  • Mint/Burn:
    _mint/_unsafeMint or _burn_update (with zero/burn address on one side)

This section illustrates how various token operations, from user transfers to minting and burning, ultimately funnel through the central _update function for consistent accounting. Now, let's summarize the key takeaways of the FungibleToken contract on Midnight.


Conclusion

The FungibleToken Compact contract on Midnight is a privacy-aware reimagining of the ERC-20 standard. It maintains the familiar token interfaces—balances, transfers, approvals—but encodes them as ZK-validated circuits within Compact, enabling private, verifiable execution. The contract’s state and logic are shielded by design, exposing only proofs rather than raw data to the blockchain.

The ERC-20 standard revolutionized the crypto landscape by providing a common framework for creating and managing digital assets, fostering interoperability, and accelerating the growth of decentralized applications. For Midnight, an ERC-20-based token is crucial as it leverages this established standard while integrating ZK-privacy, offering a familiar yet enhanced experience for developers and users seeking both functionality and confidentiality.

This model contrasts sharply with ERC-20 on Ethereum, where all token movements and balances are fully transparent. Here, Midnight allows selective disclosure: users and applications choose what to reveal. The FungibleToken contract thus balances fungibility, usability, and privacy—providing an essential building block for privacy-preserving DeFi, identity, and tokenized assets on Midnight.


Delve deeper into the code, contracts, and comprehensive documentation to enhance your understanding and development skills. These resources are invaluable for building robust and innovative solutions.

From Frustration to Framework: Building on Midnight

· 9 min read
Kaleab Abayneh
Kaleab Abayneh
Guest Contributor

After participating in three hackathons this year, I thought I had a good idea of what to expect. Then came the fourth. It began like the others: an idea, a challenge, a blank terminal screen. But this one took an unexpected turn, one I didn't see coming.

I had recently left my software engineering role to explore zero-knowledge protocols, an area in which I didn’t have much experience. It was a risk, but one I felt I had to take. I chose to pursue this path not only because zero-knowledge cryptography is built on one of the most elegant branches of mathematics, but also I believe privacy on the blockchain or on the internet is fundamental and should never be an afterthought.

One day, my mentor casually mentioned the African Blockchain Championship and suggested I look into the Zero-Knowledge track. That small nudge became a turning point.

That’s when I discovered Midnight!

Meet Midnight: Privacy on the Blockchain

Midnight is a zero-knowledge proof-based privacy chain developed by Input Output, the parent company of Cardano. As a partner chain of Cardano, it aims to bring rational privacy through zero-knowledge technology to the broader ecosystem. They weren’t just building another blockchain—they were laying the foundation for private, decentralized computation. After diving into the documentation and setting up the tools, everything was finally in place. All that remained… was to begin. Only later did I realize: this wasn’t just the start of another hackathon—it was the beginning of something much bigger.

Building on Midnight

Hacking on Midnight was challenging, as building in a testnet environment requires constant learning and adaptation to major changes. Within just a few weeks, significant network updates rendered the existing examples incompatible with the latest SDK versions. As a result, an example had to be cloned and debugged line by line. The example repository was dense, filled with layers of files and directories, and figuring out where to start was a challenge in itself.

Fortunately, Midnight’s smart contract language, Compact, shares a syntax similar to TypeScript, which eased the learning curve. I pushed forward, determined to build something meaningful and make my mark—but in the end, my attempt fell short. With midterm exams approaching and the hackathon submission deadline close by, I had to make a difficult decision. I decided not to submit a project to the African Blockchain Championship hackathon. Still, a quiet sense of regret lingered. I couldn’t help but wonder: what if I had just pushed a little harder?

The Extended Deadline

About a week later, I received an email from the ABC team announcing that the hackathon deadline had been extended. I was thrilled, and I promised myself I’d give it everything this time. It wasn’t long before I was back at it. A few weeks had passed since I’d last touched the project, and during that time, both the network and SDKs had been updated. I gathered my gear, updated my environment, and set out with renewed determination to finally build something solid. I wrote my contract and began debugging, but each change introduced a new layer of complexity. Every update required changes across multiple files. It was frustrating, exhausting, but strangely thrilling. I began thinking of ways to simplify the chaos, to make the process less painful, more intuitive. That’s when I started creating small scripts to streamline the workflow.

Earlier that month, I had joined the Midnight Discord to ask questions. One week, I jumped on a Discord community call, and to my surprise, I learned about an ongoing Midnight tooling hackathon that was happening asynchronously. Right then, it clicked: this was the hackathon I was meant to be part of from the start.

From Personal Tool to Shared Project

What started as a personal tool suddenly took on a bigger purpose. The hackathon provided me with the opportunity to share my work with others and contribute to shaping the future of smart contract development on Midnight. I took a little detour from my original hackathon submission—promising myself I’d return to it—and began refining the tool, aiming not just to build something functional, but something genuinely useful for newcomers to the ecosystem.

Scaffold Midnight & Create Midnight App

That’s how Scaffold Midnight was born, a cloneable GitHub starter template. But I didn’t stop there. I realized it would be even more useful as an npm library, something developers could install with all dependencies bundled and start using right away. So, I polished the project and released the first version as create-midnight-app. But I didn’t want to stop at solving just one problem. Every issue I fixed revealed another. Through multiple iterations, I kept refining the library. It’s now at version 2.1.7, and I’m actively working to make it compatible with the latest release of Compact. It’s far from perfect, and that’s what makes it exciting. Every day, I wake up with a new idea, a new feature to add, or a better way to improve the developer experience!

Create Midnight App

At its core, create-midnight-app is a scaffolding npm library for Midnight that automates the entire setup process. Developers can focus solely on writing their Compact contracts; the tool takes care of the rest. It handles wallet creation, faucet requests, balance checks, API configuration, CLI updates, and even function and file change imports are updated automatically. It reduces initial application setup time from over half an hour to just a few minutes.

I had several euphoric moments while working on this project, but one instance in particular, stands out.

One feature I was eager to build was a way for developers to request testnet tokens without ever leaving their code editor. The simplest idea was to send a request from the terminal directly to the official Midnight faucet. But there was a catch—the site is protected by Cloudflare Turnstile, which blocks automated requests to prevent spam and abuse. As a hacker, I started looking for a workaround. After some Googling and tips from friends, I tested several paid tools that claimed to bypass CAPTCHA protections. I spent an entire afternoon trying them, but none worked reliably. That’s when I remembered the genesis wallet, whose private key is publicly available. So, I implemented a workaround: instead of calling the faucet, I simulated a faucet transaction by transferring tokens directly from the genesis wallet to the user’s wallet. It’s not a long-term solution, and I know it won’t scale—but for now, it works and helps streamline the developer experience.

Submission and Recognition

I finally submitted my project, and the judges’ reactions were incredibly encouraging—it gave me a renewed sense of inspiration. With that momentum, I turned my focus back to my original hackathon: the African Blockchain Championship. For this challenge, I built an anonymous, censorship-resistant voting platform. In many African countries—across 34 nations—roughly 30–40% of citizens no longer view voting as a fair or trustworthy process (Afrobarometer, 2023). That’s why anonymous voting isn’t just a feature—it’s a necessity. My project, Privote, aims to address this critical issue by leveraging zero-knowledge technology to protect voter identity and integrity.

As the submission deadline approached, I ran into a major hurdle: integrating the wallet into the frontend. I reached out to one of the Developer Relations Engineers, who kindly shared some example code. Unfortunately, I couldn’t get it working in time. With the clock ticking, I had to improvise. I quickly built a custom Chrome extension that enabled wallet interaction—running through the terminal on the backend—just in time for submission. While the current version still has some privacy limitations, the smart contract compiles successfully, and users can interact with it on the Midnight testnet—offering a real glimpse of what decentralized, anonymous voting could look like in practice.

Private Hackathon

The Midnight Community

Even though there aren’t many resources available yet, the Midnight community more than makes up for it. Almost every question asked on Discord gets a response. It’s a space where people genuinely support each other—even during hackathons, when we’re technically competing and have little incentive to help other teams. In fact, during the hackathon, I often found myself answering questions in the Telegram group. I even hosted a workshop at the ABC hackathon on how to use Create Midnight App, helping others get started quickly and navigate the ecosystem more easily.

I also want to take a moment to thank the Midnight team for the recognition they gave me—it truly meant a lot. Beyond placing first in the CLI track, the support and appreciation I received from the community were genuinely heartwarming. I even had the chance to join a community call—not just as a participant this time, but as a speaker—to share my journey and the lessons I’d learned along the way.

Why Now is the Time to Build on Midnight

And for you—my readers—this is honestly the best time to get involved in the Midnight ecosystem. There’s almost always a hackathon happening (seriously, it's kind of wild!). Right now, they’re even running a mini DApp hackathon. It’s an excellent opportunity to learn, build, and get your project noticed.

Midnight Dapp Hackathon

As for me, I’m currently building something new for the hackathon. While I enjoy working on projects, I’ve found even more fulfillment in developing infrastructure tools. I have a long-term goal of building a web-based Midnight playground where anyone can start writing Compact code and begin interacting with the Midnight testnet without needing to install anything. I hope to continue collaborating with the Midnight team and growing my skills—especially in zero-knowledge protocols—as I continue this journey.

How to query the blockchain?

· 4 min read
Claude Barde
Developer Relations

The Midnight blockchain introduces a novel approach to data privacy and compliance in decentralized applications (DApps), leveraging zero-knowledge proofs (ZKPs) and a dual-token system.

This article provides a comprehensive guide on connecting to the Midnight blockchain, and you will learn how to listen for new blocks and extract data from these blocks using the Polkadot API and the various methods it provides to connect to Substrate-based blockchains, including Midnight.