You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Designing a decentralized exchange (DEX) dApp on the Midnight blockchain involves leveraging its privacy-focused features (Zswap for private transactions and Kachina-inspired smart contracts via Compact), integrating with the Midnight Lace wallet, and ensuring a user-friendly experience.
Below is a detailed system design covering architecture, components, data flow, implementation considerations, and development roadmap for LunarSwap (Permissioned Private DEX) dApp.
LunarSwap is a permissioned, private decentralized exchange (DEX) dApp on the Midnight blockchain network, designed as a starter example to showcase complex interactions between tokens, roles, and privacy. Unlike permissionless (fully-open) DEX, LunarSwap restricts access to a predefined group of users who agree off-chain to form a private AMM pool. This group designates an Admin (who manages permissions), Liquidity Providers (LPs) (who supply tokens), and Traders (who swap tokens), ensuring that only permitted accounts can interact with the pool. All pool details—tokens, liquidity, constant $k$, and swaps—are private, visible only to authorized users via shielded transactions and client-side utilites. This approach offers value through enhanced privacy (hiding balances, swaps, and reserves) and anti-MEV (Miner Extractable Value) protection by keeping intents and pool state confidential, making it ideal for exclusive, trust-based trading groups. The design leverages Midnight’s Zswap protocol for shielded swaps and Compact for zero-knowledge smart contracts, integrating with the Midnight Lace Wallet for a user-friendly experience.
2. Architecture
Chosen Solution for Shared Private State (SPS): For managing the shared private state (e.g., PoolState reserves and LP tokens) among authorized users, the design adopts a Centralized Encryptor/Decryptor approach. This off-chain service encrypts and decrypts PoolState using a private key, with role-based access enforced via signed requests, and the smart contract verifies updates against poolCommits. For a detailed exploration of this and alternative solutions, refer to the Challenges and Limitations section.
2.1 Front-end (UI Layer)
Built with TypeScript/React for a responsive, web-based dashboard.
Connects to Midnight Lace wallet via a wallet adapter.
Lightweight TS utilities and packages running on the client side for wallet interaction, transaction construction, and real-time status polling.
Interfaces with Midnight’s blockchain via a client library (e.g., a TS SDK generated from Compact contracts), executed locally in the user’s environment (browser or CLI).
dexState: Enum { active, inactvie } (optional, if state tracking added):
Description: Contract operational state.
Purpose: Controls access to functions (e.g., swaps only in active).
Visibility: Public for contract lifecycle management.
Private Data
Private data is managed off-chain via witnesses or shielded by Zswap, accessible only to authorized parties (e.g., client-side) and proven via ZK-SNARKs:
User Balances:
Description: Token holdings per user (e.g., NIGHT/DUST, TokenA, TokenB).
Storage: Off-chain in private state (via witness getBalance(), witness updateBalance()) and shielded UTXOs in Zswap.
Access: User-specific, queried via client-side; never stored on ledger.
Proof: Updates committed to balanceCommits Merkle tree, proven via ZK circuits.
Liquidity Pool Reserves
Description: Exact amounts of TokenA and TokenB in each liquidity pool (within PoolState).
Storage: Off-chain in private state (via witness getPoolState(), witness updatePoolState() ).
Access: Managed by client-side; not exposed on ledger.
Proof: Updates committed to poolCommits Merkle tree, verified via ZK-SNARKs.
Liquidity Provider (LP) Token Balances
Description: LP tokens representing user shares in liquidity pools.
Storage: Off-chain in private state (via witness getLpBalance(), witness updateLpBalance()).
Access: Only the user who provided liquidity can access their LP balance.
Proof: Updates are committed to the lpTokenCommits Merkle tree, ensuring LP holdings remain private.
Roles
Description: User permissions (e.g., admin, lp, trader) assigned to each UserID.
Storage: Off-chain in private state (via witness getRole(), witness updateRole()) and shielded via Zswap key derivation.
Access: User-specific, queried via client-side; not directly stored on ledger.
Proof: Updates committed to roleCommits Merkle tree, proven via zero-knowledge circuits.
Transaction Details (Swaps)
Description: Specific amounts, token types, and user identities in swaps.
Storage: Handled by Zswap as shielded transactions; only imbalance maps reach the ledger.
Access: Private to transacting parties; obscured from public view.
Proof: Zswap’s ZK-SNARKs ensure validity.
Merkle Tree Paths
Description: Proofs of inclusion for balance or pool commitments (e.g., MerkleTreePath<10, Bytes<32>>).
Storage: Off-chain via witnesses (e.g., witness getBalancePath(), witness getPoolPath()).
Access: Provided by client-side for verification when needed.
Proof: Used in circuits to validate against public Merkle roots.
Order Details (Stretch)
Description: Market/limit order specifics (type, amount, price).
Storage: Off-chain until matched; committed to witness orderCommits() Merkle tree.
Access: Private until revealed for settlement.
Proof: ZK circuits prove commitment integrity.
Circuits
NOTE:
Compacts contracts cannot talk to each other at the time, so the design will be all held in one module.
3.3.2 Liquidity Pool Circuit
Purpose: Manages liquidity pools privately, minting LP tokens as rewards.
Ledger: MerkleTree<10, Bytes<32>> for pool state commitments; Map<TokenPair, Cell<Uint<64>>> for public LP token supply.
Circuit:
addLiquidity(tokenA: TokenID, tokenB: TokenID, amountA: Uint<64>, amountB: Uint<64>, coin: CoinInfo) updates private pool state and commits to the Merkle tree, minting LP tokens via mint_token(),
removeLiquidity(tokenA: TokenID, tokenB: TokenID, lpAmount: Uint<64>, coin: CoinInfo): [] removes liquidity from a token pair pool, burning LP tokens and returning assets.
3.3.3 Swap Circuit
Purpose: Executes private token swaps using Zswap, ensuring amounts and identities remain shielded.
Ledger: MerkleTree<10, Bytes<32>> for balance commitments; QualifiedCoinInfo for transient NIGHT fees.
Circuit: swap(from: TokenID, to: TokenID, amount: Uint<64>, coin: CoinInfo) proves validity via ZK-SNARKs, updating private balances and Merkle tree commitments.
3.3.4 Roles Circuit
Purpose: Assigns and verifies user roles (e.g., admin, lp, trader) privately.
Ledger: MerkleTree<10, Bytes<32>> for role commitments.
Circuit:
addRole(target: UserID, role: Role) updates private role state via witness and commits the hashed role to the Merkle tree.
hasRole(role: Role): Bool proves the caller’s role matches the requested role via ZK proof, checking against the Merkle tree.
3.3.5 Order Book (Stretch) Circuit
Purpose: Handles market/limit orders with private commitments.
Ledger: MerkleTree<10, Bytes<32>> for order commitments.
Circuit: commitOrder(type: OrderType, token: TokenID, amount: Uint<64>, price: Uint<64>) adds order commitments to the tree for off-chain matching.
Key Compact Code Example (Abstract):
// DISCLAIMER:// This abstract code is subject to change and may require modifications based// based on updates to the ERC-20 "compatible" contract.importCompactStandardLibrary;
module LunarSwap{typeTokenID=Opaque<"string">;typeUserID=Bytes<32>;enumRole{admin,lp,trader}structTokenPair{tokenA: TokenID,tokenB: TokenID}structPoolState{encryptedState: Bytes}// Encrypted representation of { reserveA, reserveB, lpTokens }structDecryptedPoolState{reserveA: Uint<64>,reserveB: Uint<64>,lpTokens: Uint<64>}// Public ledgerexportledger roleCommits: MerkleTree<10,Bytes<32>>;exportledger balanceCommits: MerkleTree<10,Bytes<32>>;// Balance commitmentsexportledger poolStateCommits: MerkleTree<10,Bytes<32>>;// Pool state commitmentsexportledger lpTokenCommits: MerkleTree<10,Bytes<32>>;// Commitments to private LP token balances
export ledgerpot: QualifiedCoinInfo;// NIGHT/(DUST) fee storageexportledger potHasCoin: Boolean;exportledger dexState: Enum{active,inactive};// Contract stateexportledger encryptorPublicKey: Bytes<32>;// Public key of Centralized Encryptor/Decryptor// Witnesses for private statewitnessgetBalance(user: UserID,token: TokenID): Uint<64>;witnessupdateBalance(user: UserID,token: TokenID,newBal: Uint<64>);// That will be replaced by Encrypted verion of PoolState.// witness getPoolState(pair: TokenPair): PoolState;// witness updatePoolState(pair: TokenPair, state: PoolState);witnessgetDecryptedPoolState(pair: TokenPair,encrypted_state: Bytes): DecryptedPoolState;witnessencryptPoolState(pair: TokenPair,decrypted_state: DecryptedPoolState): PoolState;witnessgetLpBalance(user: UserID,pair: TokenPair): Uint<64>;witnessupdateLpBalance(user: UserID,pair: TokenPair,newBal: Uint<64>);witnessgetBalancePath(user: UserID): Maybe<MerkleTreePath<10,Bytes<32>>>;witnessgetPoolPath(pair: TokenPair): Maybe<MerkleTreePath<10,Bytes<32>>>;witnessgetLpBalancePath(user: UserID): Maybe<MerkleTreePath<10,Bytes<32>>>;witnessgetRole(user: UserID): Role;witnessupdateRole(user: UserID,role: Role);/** * @description Initializes the DEX contract with an initial NIGHT fee and Admin. * @param initialFee CoinInfo - NIGHT token fee to seed the contract's pot. * @param initialAdmin UserID - Shielded address of the initial Admin. * @param encryptorKey Bytes<32> - Public key of the Centralized Encryptor/Decryptor. */constructor(initialFee: CoinInfo,initialAdmin: UserID,encryptorKey: Bytes<32>);/** * @description Grants a role (Admin, LP, Trader) to a target user, callable only by Admins. * @param target UserID - Shielded address of the user to grant the role to. * @param role Role - The role to assign (admin, lp, trader). * @param coin CoinInfo - NIGHT fee for transaction processing. * @return [] - No direct return; updates roleCommits. */exportcircuitaddRole(target: UserID,role: Role,coin: CoinInfo): [];/** * @description Revokes a role from a target user, callable only by Admins. * @param target UserID - Shielded address of the user to revoke the role from. * @param coin CoinInfo - NIGHT fee for transaction processing. * @return [] - No direct return; updates roleCommits. */exportcircuitremoveRole(target: UserID,coin: CoinInfo): [];/** * @description Checks if the caller has a specific role, restricted to permitted users. * @param role Role - The role to check (admin, lp, trader). * @return Bool - True if the caller has the specified role, false otherwise. */exportcircuithasRole(role: Role): Bool;/** * @description Queries the private exchange rate for a token pair, restricted to LPs and Traders. * @param from TokenID - Token to swap from. * @param to TokenID - Token to swap to. * @param amount Uint<64> - Amount of 'from' token to swap. * @return Uint<64> - Expected amount of 'to' token (toAmount). */exportcircuitcheckExchangeRate(from: TokenID,to: TokenID,amount: Uint<64>): Uint<64>;/** * @description Adds liquidity to a token pair pool, minting LP tokens, restricted to LPs. * @param tokenA TokenID - First token in the pair. * @param tokenB TokenID - Second token in the pair. * @param amountA Uint<64> - Amount of tokenA to add. * @param amountB Uint<64> - Amount of tokenB to add. * @param coin CoinInfo - NIGHT fee for transaction processing. * @return CoinInfo - Minted LP tokens returned to the LP. */exportcircuitaddLiquidity(tokenA: TokenID,tokenB: TokenID,amountA: Uint<64>,amountB: Uint<64>,coin: CoinInfo): CoinInfo;/** * @description Removes liquidity from a token pair pool, burning LP tokens, restricted to LPs. * @param tokenA TokenID - First token in the pair. * @param tokenB TokenID - Second token in the pair. * @param lpAmount Uint<64> - Amount of LP tokens to burn. * @param coin CoinInfo - NIGHT fee for transaction processing. * @return [] - No direct return; updates pool state and balances. */exportcircuitremoveLiquidity(tokenA: TokenID,tokenB: TokenID,lpAmount: Uint<64>,coin: CoinInfo): [];/** * @description Executes a private token swap, restricted to Traders. * @param from TokenID - Token to swap from. * @param to TokenID - Token to swap to. * @param amount Uint<64> - Amount of 'from' token to swap. * @param coin CoinInfo - NIGHT fee for transaction processing. * @return Uint<64> - Amount of 'to' token received (toAmount). */exportcircuitswap(from: TokenID,to: TokenID,amount: Uint<64>,coin: CoinInfo): Uint<64>;/** * @description Calculates the exchange rate. * @param amount Uint<64> - Amount of tokens to be swapped. * @param reserveA Uint<64> - The reserve amount of TokenA. * @param reserveB Uint<64> - The reserve amount of TokenB. * @return Uint<64> - Exchange rate. */purecircuit_calculateExchangeRate(amount: Uint<64>,reserveA: Uint<64>,reserveB: Uint<64>): Uint<64>;/** * @description Hashes a user's role for commitment to roleCommits. * @param user UserID - Shielded address of the user. * @param role Role - The user's role (admin, lp, trader, none). * @return Bytes<32> - Hash of the role assignment. */purecircuit_hashRole(user: UserID,role: Role): Bytes<32>;/** * @description Hashes a user's balances for commitment to balanceCommits. * @param user UserID - Shielded address of the user. * @param from TokenID - First token in the pair. * @param to TokenID - Second token in the pair. * @return Bytes<32> - Hash of the balances. */purecircuit_hashBalances(user: UserID,from: TokenID,to: TokenID): Bytes<32>;/** * @description Hashes a pool state for commitment to poolCommits. * @param state PoolState - Current state of the pool (reserves, LP tokens). * @return Bytes<32> - Hash of the pool state. */purecircuit_hashPoolState(state: PoolState): Bytes<32>;/** * @description Provides the domain separator for LP token minting. * @return Bytes<32> - Fixed "LUNAR" padded to 32 bytes. */exportcircuitdexLpTokenDomainSeparator(): Bytes<32>{returnpad(32,"LUNAR");}/** * @description Defines the minimum NIGHT fee threshold. * @return Uint<64> - Minimum fee in NIGHT subunits (e.g., 1000000). */exportcircuittdust(): Uint<64>{
return 1000000;// NIGHT subunit}}
3.4 Midnight Blockchain Integration
Zswap: Handles shielded swaps, exposing only imbalance maps (net asset changes) on the ledger, integrated with swap circuit for private token transfers.
Compact: Compiles circuits into ZK-SNARKs, ensuring private execution of swap and liquidity logic; uses MerkleTree roots for state verification.
Ledger: Stores minimal public state (e.g., balanceCommits and poolCommits roots, lpTokenSupply); private state (balances, pool reserves) managed via witnesses.
3.5 Midnight Lace Wallet
Role: Signs transactions, manages NIGHT/DUST for fees, and provides user identity (shielded address).
Integration: Front-end uses wallet SDK to request signatures for Zswap transactions and query NIGHT balances for fee payments.
4. Data Flow
4.1 Swap
User selects tokens and amount in UI.
Client-side decrypts current PoolState via Centralized Encryptor/Decryptor.
Client-side calls checkExchangeRate circuit to display private exchange rate ratio, with sending decrypted poolState as a witness to the contract.
Client-side signs transaction via Midnight Lace Wallet, submits to Midnight.
Blockchain processes via swap circuit, retrieves decrypted PoolState with getDecryptedPoolState, computes new state, encrypts with encryptPoolState, updates poolCommits and balanceCommits Merkle trees.
Client-side statusPoller utility queries blockchain for transaction status (e.g., Merkle root update), updates UI (e.g., “Confirmed”).
4.2 Liquidity Provision
User inputs token pair and amounts in UI.
Front-end calls client-side liquidityManager utility with token pair and amounts.
Client-side constructs addLiquidity transaction with DUST fee, signs via Midnight Lace Wallet, submits to Midnight.
Blockchain processes via addLiquidity circuit, retrieves decrypted PoolState with getDecryptedPoolState, computes new state (e.g., updates reserves, mints LP tokens), encrypts with encryptPoolState, updates poolCommits, lpTokenCommits, and lpTokenSupply.
UI reflects new LP tokens via client-side update.
4.3 Transaction Status
Client-side statusPoller utility subscribes to blockchain events or polls MerkleTree roots.
Front-end calls addRoleTxBuilder or removeRoleTxBuilder, submits tx.
addRole/removeRole updates private roles and roleCommits.
Front-end confirms role change.
4.5 Stretch: Orders
User places market/limit order via Front-end.
Front-end calls the client-side orderManager utility, which constructs a transaction for commitOrder circuit; order commitment stored in orderCommits Merkle tree.
Matching executed off-chain, settled via Zswap and a reveal circuit (TBD).
Use Zswap for all swaps to shield amounts and identities.
Compact circuits with witnesses and MerkleTree commitments ensure balances and pool reserves remain private, exposing only roots.
5.2 Scalability
Optimize ZK proof generation (e.g., batch Merkle tree updates if Midnight supports it).
Limit ledger writes to MerkleTree insertions and lpTokenSupply updates to reduce NIGHT fee costs.
The limited depth size of the Merkle Tree.
5.3 Security
Audit Compact contracts for ZK soundness, Merkle tree integrity, and witness trustworthiness.
Validate wallet signatures to prevent spoofing.
5.4 Stretch Goals
Market/Limit Orders: Extend with orderCommits Merkle tree and off-chain matching logic; settle via Zswap.
Fee Choice: Adjust NIGHT fee (coin.value) in transaction construction; expose options (e.g., Low, Medium, High).
6. Challenges and Limitations
6.1 Handling Shared Private State (SPS) Challenge
The permissioned private DEX requires efficient sharing and synchronization of the shared private state (e.g., PoolState containing reserves and LP tokens) among authorized users (e.g., LPs, traders) in a decentralized manner, ensuring privacy and consistency without relying on a central server (as possible). Midnight’s current design, with a public ledger and ZK proofs suited for Personal Private State (PPS), lacks native support for dynamic SPS. This section explores the problem and evaluates possible solutions.
Problem Statement
Need: Authorized users must access and update PoolState privately and consistently, verified against on-chain poolCommits.
Possible Solutions
WebRTC-based P2P Network
Description: Clients join a P2P network via WebRTC, encrypt PoolState, and broadcast updates to authorized peers, who verify against poolCommits.
Cons: Complex to implement and debug, prone to network issues, less scalable for a starter dApp.
Feasibility: Challenging because of the sync and network issues in a P2P network that could occur.
Distributed Hash Table (DHT)
Description: Encrypted PoolState fragments are stored in a DHT (e.g., IPFS), accessible to role-holding users, with updates verified via on-chain proofs.
Pros: Decentralized storage, avoids central server, scalable with proper indexing.
Cons: Storage management complexity, less real-time than P2P, overkill for a small user base.
Feasibility: Viable but resource-intensive for a starter dApp.
Multi-Party Computation (MPC)
Description: An MPC group of authorized users computes PoolState updates, encrypts results, and shares via P2P. The ts-mpc-framework (early-stage TS library) could assist.
Pros: Balances privacy and decentralization, aligns with permissioned access.
Feasibility: Suitable for mainnet but complex for a starter dApp.
FHE-based On-Chain Private DEX
Description: PoolState is encrypted on-chain with FHE, allowing authorized users to compute updates (e.g., swaps), with validators handling operations and broadcasting results.
Pros: Strong composability, on-chain efficiency, privacy via encryption.
Cons: High computational cost (1-5 TPS).
Feasibility: Promising long-term but impractical for a starter dApp, and it is not clear yet from Midnight documentation how to deal with encryption.
Centralized Data Store
Description: A hosted data store holds encrypted PoolState, accessible to authorized users via signed requests, with locking to prevent collisions.
Pros: Simple to implement, ensures consistency with locking, fits permissioned model.
Cons: Central point of failure, trust in store operator, scalability limits.
Feasibility: Highly feasible for a starter dApp, though less secure.
Centralized Encryptor/Decryptor (Chosen Solution)
Description: A hosted service encrypts/decrypts PoolState with a private key, accessible via signed requests, with locking and on-chain signature verification.
Pros: Simplifies locking, secure with smart contract validation, reusable for other dApps.
Cons: Centralization risk, trust in service operator, extra API calls.
Feasibility: Ideal for a starter dApp, aligning with controlled user base and timeline.
Recommendation
Chosen Approach: The Centralized Encryptor/Decryptor is selected for its simplicity, collision protection, and alignment with a permissioned starter dApp, implemented as a new component (Section 2.6).
7. Development Roadmap
Sprint 1 - Requirements gathering, design, and environment setup.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
System Design Document: LunarSwap-v1 🌑 A Permissioned Private DEX on Midnight Network.
Designing a decentralized exchange (DEX) dApp on the Midnight blockchain involves leveraging its privacy-focused features (Zswap for private transactions and Kachina-inspired smart contracts via Compact), integrating with the Midnight Lace wallet, and ensuring a user-friendly experience.
Below is a detailed system design covering architecture, components, data flow, implementation considerations, and development roadmap for LunarSwap (Permissioned Private DEX) dApp.
1. Overview
LunarSwap is a permissioned, private decentralized exchange (DEX) dApp on the Midnight blockchain network, designed as a starter example to showcase complex interactions between tokens, roles, and privacy. Unlike permissionless (fully-open) DEX, LunarSwap restricts access to a predefined group of users who agree off-chain to form a private AMM pool. This group designates an Admin (who manages permissions), Liquidity Providers (LPs) (who supply tokens), and Traders (who swap tokens), ensuring that only permitted accounts can interact with the pool. All pool details—tokens, liquidity, constant$k$ , and swaps—are private, visible only to authorized users via shielded transactions and client-side utilites. This approach offers value through enhanced privacy (hiding balances, swaps, and reserves) and anti-MEV (Miner Extractable Value) protection by keeping intents and pool state confidential, making it ideal for exclusive, trust-based trading groups. The design leverages Midnight’s Zswap protocol for shielded swaps and Compact for zero-knowledge smart contracts, integrating with the Midnight Lace Wallet for a user-friendly experience.
2. Architecture
Chosen Solution for Shared Private State (SPS): For managing the shared private state (e.g.,
PoolStatereserves and LP tokens) among authorized users, the design adopts a Centralized Encryptor/Decryptor approach. This off-chain service encrypts and decryptsPoolStateusing a private key, with role-based access enforced via signed requests, and the smart contract verifies updates againstpoolCommits. For a detailed exploration of this and alternative solutions, refer to the Challenges and Limitations section.2.1 Front-end (UI Layer)
2.2 Client-Side Utilities (Off-chain Logic)
2.3 On-Chain Logic (Smart Contracts)
2.4 Midnight Blockchain
2.5 Midnight Lace Wallet
2.6 Centralized Encryptor/Decryptor
PoolStateusing a private key, accessible only to authorized users via signed requests.2.7 Architecture Diagram
3. Detailed Component Design
3.1 Front-end
3.2 Client-Side Utilities (Off-chain Service)
swapTxBuilder: Constructs swap txs for Traders.liquidityManager: Manages liquidity txs for LPs.addRoleTxBuilder/removeRoleTxBuilder: Handles role updates for Admins.checkExchangeRate: Queries exchange rates privately for permitted users.statusPoller: Tracks tx status via ledger reads.3.3 On-chain Logic (Compact contracts)
3.3.1 Public and Private Data
Public Data
Public data is stored on the Midnight blockchain ledger and visible to anyone inspecting the chain:
roleCommits: MerkleTree<10, Bytes<32>>balanceCommits: MerkleTree<10, Bytes<32>>poolCommits: MerkleTree<10, Bytes<32>>lpTokenCommits: MerkleTree<10, Bytes<32>>pot: QualifiedCoinInfopotHasCoin: BooleandexState: Enum { active, inactvie }(optional, if state tracking added):Private Data
Private data is managed off-chain via witnesses or shielded by Zswap, accessible only to authorized parties (e.g., client-side) and proven via ZK-SNARKs:
witness getBalance(),witness updateBalance()) and shielded UTXOs in Zswap.balanceCommitsMerkle tree, proven via ZK circuits.TokenAandTokenBin each liquidity pool (withinPoolState).witness getPoolState(),witness updatePoolState()).poolCommitsMerkle tree, verified via ZK-SNARKs.witness getLpBalance(),witness updateLpBalance()).lpTokenCommitsMerkle tree, ensuring LP holdings remain private.admin,lp,trader) assigned to eachUserID.witness getRole(),witness updateRole()) and shielded via Zswap key derivation.roleCommitsMerkle tree, proven via zero-knowledge circuits.MerkleTreePath<10, Bytes<32>>).witness getBalancePath(),witness getPoolPath()).witness orderCommits()Merkle tree.Circuits
3.3.2 Liquidity Pool Circuit
MerkleTree<10, Bytes<32>>for pool state commitments;Map<TokenPair, Cell<Uint<64>>>for public LP token supply.addLiquidity(tokenA: TokenID, tokenB: TokenID, amountA: Uint<64>, amountB: Uint<64>, coin: CoinInfo)updates private pool state and commits to the Merkle tree, minting LP tokens viamint_token(),removeLiquidity(tokenA: TokenID, tokenB: TokenID, lpAmount: Uint<64>, coin: CoinInfo): []removes liquidity from a token pair pool, burning LP tokens and returning assets.3.3.3 Swap Circuit
MerkleTree<10, Bytes<32>>for balance commitments;QualifiedCoinInfofor transient NIGHT fees.swap(from: TokenID, to: TokenID, amount: Uint<64>, coin: CoinInfo)proves validity via ZK-SNARKs, updating private balances and Merkle tree commitments.3.3.4 Roles Circuit
admin,lp,trader) privately.MerkleTree<10, Bytes<32>>for role commitments.addRole(target: UserID, role: Role)updates private role state via witness and commits the hashed role to the Merkle tree.hasRole(role: Role): Boolproves the caller’s role matches the requested role via ZK proof, checking against the Merkle tree.3.3.5 Order Book (Stretch) Circuit
MerkleTree<10, Bytes<32>>for order commitments.commitOrder(type: OrderType, token: TokenID, amount: Uint<64>, price: Uint<64>)adds order commitments to the tree for off-chain matching.Key Compact Code Example (Abstract):
3.4 Midnight Blockchain Integration
swapcircuit for private token transfers.MerkleTreeroots for state verification.balanceCommitsandpoolCommitsroots,lpTokenSupply); private state (balances, pool reserves) managed via witnesses.3.5 Midnight Lace Wallet
4. Data Flow
4.1 Swap
PoolStatevia Centralized Encryptor/Decryptor.checkExchangeRatecircuit to display private exchange rate ratio, with sending decryptedpoolStateas a witness to the contract.swapcircuit, retrieves decryptedPoolStatewithgetDecryptedPoolState, computes new state, encrypts withencryptPoolState, updatespoolCommitsandbalanceCommitsMerkle trees.statusPollerutility queries blockchain for transaction status (e.g., Merkle root update), updates UI (e.g., “Confirmed”).4.2 Liquidity Provision
liquidityManagerutility with token pair and amounts.addLiquiditytransaction with DUST fee, signs via Midnight Lace Wallet, submits to Midnight.addLiquiditycircuit, retrieves decryptedPoolStatewithgetDecryptedPoolState, computes new state (e.g., updates reserves, mints LP tokens), encrypts withencryptPoolState, updatespoolCommits,lpTokenCommits, andlpTokenSupply.4.3 Transaction Status
statusPollerutility subscribes to blockchain events or polls MerkleTree roots.4.4 Role Management
addRoleTxBuilderorremoveRoleTxBuilder, submits tx.addRole/removeRoleupdates private roles androleCommits.4.5 Stretch: Orders
orderManagerutility, which constructs a transaction forcommitOrdercircuit; order commitment stored inorderCommitsMerkle tree.4.6 Sequence Diagrams
5. Implementation Considerations
5.1 Privacy
MerkleTreecommitments ensure balances and pool reserves remain private, exposing only roots.5.2 Scalability
MerkleTreeinsertions andlpTokenSupplyupdates to reduce NIGHT fee costs.5.3 Security
5.4 Stretch Goals
orderCommitsMerkle tree and off-chain matching logic; settle via Zswap.6. Challenges and Limitations
6.1 Handling Shared Private State (SPS) Challenge
The permissioned private DEX requires efficient sharing and synchronization of the shared private state (e.g.,
PoolStatecontaining reserves and LP tokens) among authorized users (e.g., LPs, traders) in a decentralized manner, ensuring privacy and consistency without relying on a central server (as possible). Midnight’s current design, with a public ledger and ZK proofs suited for Personal Private State (PPS), lacks native support for dynamic SPS. This section explores the problem and evaluates possible solutions.Problem Statement
PoolStateprivately and consistently, verified against on-chainpoolCommits.Possible Solutions
PoolState, and broadcast updates to authorized peers, who verify againstpoolCommits.PoolStatefragments are stored in a DHT (e.g., IPFS), accessible to role-holding users, with updates verified via on-chain proofs.PoolStateupdates, encrypts results, and shares via P2P. Thets-mpc-framework(early-stage TS library) could assist.PoolStateis encrypted on-chain with FHE, allowing authorized users to compute updates (e.g., swaps), with validators handling operations and broadcasting results.PoolState, accessible to authorized users via signed requests, with locking to prevent collisions.PoolStatewith a private key, accessible via signed requests, with locking and on-chain signature verification.Recommendation
7. Development Roadmap
Sprint 1 - Requirements gathering, design, and environment setup.
Time.
DEADLINE: <2025-03-10 Mon> SCHEDULED: <2025-02-24 Mon>
Sprint 2 - Smart-contract dev, React Scaffolding, initial integration.
Time
DEADLINE: <2025-03-24 Mon> SCHEDULED: <2025-03-11 Tue>
Sprint 3 - Continue contract dev, UI components, integration
Time
DEADLINE: <2025-04-07 Mon> SCHEDULED: <2025-03-25 Tue>
Sprint 4 - Finalize contracts, front-end polish, user testing.
Time
DEADLINE: <2025-04-21 Mon> SCHEDULED: <2025-04-08 Tue>
Sprint 5 - Final testing, bug fixes, deployment.
Time
DEADLINE: <2025-04-30 Wed> SCHEDULED: <2025-04-22 Tue>
8. Risks & Mitigations
9. Sequence Diagrams
9.1 Add-Liquidity Process
Toggle Diagram
9.2 Remove-Liquidity Process
Toggle Diagram
9.3 Swap Process
Toggle Diagram
All reactions