Jayendra Ozone
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       owned this note    owned this note      
    Published Linked with GitHub
    Subscribed
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    Subscribe
    # DARKPOOL GATEWAY FOR DEFI [[SPEC]] **Version:** 0.1.0 **Date:** 2025-06-11 **Status:** Draft This Current document is specification/Implmentation Guide for building a Decentralized Darkpool gateway. users of this darkpool can interact with any defi application in a private and anonymous manner! protecting their identity and trade information. Rest of document is structured into 3 parts - spec for building financial operations over assets - spec for integrations between darkpool and existing protocols - Native tightly integration spec for building defi applications and MPC networks on Xythum Darkpool. ## Concepts ### Commitments A **commitment** a commitment represents a user's shielded assets. * **Structure:** `commitment = H_pedersen(value, asset_address, precommitment)` * `value`: The amount of the asset. * `asset_address`: The ERC20 token contract address (or a similar identifier for other asset types). * `precommitment`: A cryptographic hash binding the user's `secret` and `nullifier`. * // precommitment = pedersen_hash([nullifier, secret]); ### Secret and Nullifier Generation For each commitment, a unique `secret` and `nullifier` are generated client-side. 1. **Xythum Local Private Key:** A user-specific private key for Xythum operations is derived from their primary wallet by signing a constant string: ```javascript const signature = await signMessage("Xythum_Gateway_Derivation_Salt_v1", user_L1_privkey); const xythum_local_privkey = keccak256(signature); ``` 2. **Secret and Nullifier:** For each new note, a unique `note_nonce` is used to derive the `secret` and `nullifier`: ```javascript const secret = keccak256(abi.encodePacked(xythum_local_privkey, note_nonce, "XYTHUM_SECRET")); const nullifier = keccak256(abi.encodePacked(xythum_local_privkey, note_nonce, "XYTHUM_NULLIFIER")); ``` * `secret`: A private value known only to the user, used in generating the commitment. * `nullifier`: A value that, when revealed, marks the associated commitment as spent, preventing double-spending. It must be unique per commitment. Stack : zkps [ NOIR | Barretenberg (ultraHonk backend)], Solidity - EVM, DA layer (storing encrypted orders) Core of the protocol relies on Incremental merkle trees [Incremental Merkle Tree Semaphore v4](https://hackmd.io/@vplasencia/S1whLBN16), In one liner for every action user has to generate a zk proof stating he owns the commitment and he is invalidating it(nullifier reveal). At any point in time user would be proving they know a previous commitment but never reveal what the commiement was. All the asset management operations will also be proven with zkps. ## CASE 1: PRIVACY FOR ASSETS Flow: 1. user deposits assets into the protocol (public tracable interaction with xythum darkpool). 2. From here on all the financial actions [withdraw, split, join, transfer, topup, claim, defi interactions] are completely anonymous and untracable with original deposit. 3. every action with darkpool issues a **commitment**, anyone being able to prove their hold commitment can have authority over assets. --- > ## Deposit Action Refer [here](#Secret-and-Nullifier-Generation) for secret generation method!. Now user locally computers a pre commitment. which is user for deposit. ```rs precommitment = pedersan( nullifier , secret ) ``` User broadcasts this pre-commitment onchain for deposit. ```solidity= function deposit(precommitment, value, asset) public { require(_value > 0, "Deposit value must be positive"); require(asset != address(0), "Invalid asset address"); bytes32 commitment = pedersenHasher.hash( abi.encodePacked(uint256(value), uint256(uint160(asset)), precommitment) update_merkle_root(commitment); safetransferFrom(msg.sender, asset, value); emit NewCommitment(...); } ``` As we can see merkle tree is directly maintained onChain and it is incremental merkle tree in structure. clients can directly query for **path to root** from contract. > ## Withdraw Action In order for a user to withdraw any amount of intial deposit without revealing any link to original deposit yet maintaining the protocol integrety. user must proove following statements to the contract. - They own a valid, unspent commitment (existing_commitment) within the Merkle tree (proven via Merkle path and root). - The existing_commitment corresponds to existing_value, asset_address, existing_nullifier, and existing_secret. - The existing_nullifier has not been revealed (this is checked on-chain, but the proof links it to the commitment). - A new_commitment is correctly calculated for the remaining balance (remaining_value = existing_value - withdraw_value), using - a fresh new_nullifier and new_secret. If remaining_value is zero, no new commitment is needed, or a special zero-value commitment is handled. - The withdraw_value and remaining_value are within acceptable bounds (e.g., non-negative, fit in uint128). User has to generate a proof from a circuit whose inputs are == CIRCUITS == ```rs # psuedo code for noir cicuit use std::hash::pedersen_hash; pub fn commitment_hasher( value: Field, label: Field, nullifier: Field, / secret: Field ) -> (Field, Field) { // 1. Compute nullifier hash // Circom's pedersen(1) with one input let nullifier_hash = pedersen_hash([nullifier]); // 2. Compute precommitment // Circom's pedersen(2) with two inputs let precommitment = pedersen_hash([nullifier, secret]); // 3. Compute commitment hash // Circom's pedersen(3) with three inputs let commitment = pedersen_hash([value, label, precommitment]); // 4. Return output signals (commitment, nullifier_hash) } ``` == WITHDRAW CIRCUIT == ```rs fn withdraw<N: u32>( pub withdrawn_value: Field, pub root: Field, pub tree_depth: Field, pub existing_nullifier: Field, label: Field, existing_value: Field, existing_secret: Field, new_nullifier: Field, new_secret: Field, path: [Field; N], index: Field ) -> (Field, Field) { let (existing_commitment, _) = commitment_hasher(existing_value, label, existing_nullifier, existing_secret); let calculated_state_root = lean_imt_inclusion_proof::<N>(existing_commitment, index, path, tree_depth); assert_eq(state_root, calculated_state_root); let remaining_value = existing_value - withdrawn_value; constrain_in_128_bits(remaining_value); constrain_in_128_bits(withdrawn_value); let nullifiers_are_equal = are_equal(existing_nullifier, new_nullifier); assert_eq(nullifiers_are_equal, 0); // it has to be false let (calculated_new_commitment_hash, _) = commitment_hasher(remaining_value, label, new_nullifier, new_secret); let new_commitment_hash_output = calculated_new_commitment_hash; let _context_squared = context * context; (new_commitment_hash_output, existing_nullifier) } ``` When user intent is to withdraw certain amount from his existing balance he would have to compute withdraw_proof. Onchain solidity contract could look something like this ```solidity function withdraw(Proof withdrawProof, address reciver) public { let (uint236 transfer_value, bytes32 root, uint32 depth, bytes32 nullifier,bytes32 newcommitment,IERC20 asset) = verifier.verify(withdrawProof); validate_tree_state(root, depth); check_and_invadidate_nullifier(nullifier); // pure mapping update_merkle_tree(newcommitment); asset.safeTransfer(withdraw_value, reciver); } ``` - prevents double spending via nullifiers. - old commitment is never revealed! So this withdraw spend can never be traced back to original commitment. :::info **Compliance Considerations**: To facilitate optional regulatory compliance, Xythum can incorporate a mechanism for users to disclose their transaction history. During proof generation for any action (withdraw, transfer, etc.), the client can create an encrypted "Action Intent" data packet. This packet might contain details like operation_type, value, asset_address, and counterparty_info_if_applicable. This encrypted data can be: - Stored off-chain by the user. Optionally emitted as part of an event (if small enough and privacy implications are managed). - Stored on a dedicated Data Availability (DA) layer, retrievable by the user. However, if they choose or are required to "exit" the system transparently or comply with a lawful request, they can reveal the decryption key(s) associated with their Xythum identity. This would allow reconstruction of their transaction flow. Xythum provides privacy by default, with opt-in transparency. ::: :::warning The code blocks are too generic and only meant for understanding, lot of sanity checks are excluded. ::: > ## Split The Split action allows a user to take an existing shielded commitment and divide its value into two new shielded commitments. Statements to Prove (Zero-Knowledge): 1. Ownership of commitment_old (unspent, valid Merkle path). commitment_old corresponds to value_old, asset_address, nullifier_old, secret_old. 2. Two new commitments (commitment_new1, commitment_new2) are correctly formed for value_new1 and value_new2 respectively, using the same asset_address and new secret/nullifier pairs. 3. Value conservation: value_old == value_new1 + value_new2. 4. value_new1 > 0 and value_new2 > 0. ```rust fn split_asset_circuit<N: u32>( // Private Inputs value_old: Field, asset_address: Field, secret_old: Field, merkle_path: [Field; N], leaf_index: Field, value_new1: Field, // value_new2 is derived nullifier_new1: Field, secret_new1: Field, nullifier_new2: Field, secret_new2: Field, // Public Inputs pub merkle_root: Field, pub nullifier_old: Field, ) { let precommitment_old = compute_precommitment(nullifier_old, secret_old); let commitment_old = compute_commitment(value_old, asset_address, precommitment_old); let is_member = lean_imt_inclusion_proof::<N>(commitment_old, merkle_leaf_index_old, merkle_path_old, merkle_root); assert(is_member); let value_new2_derived = value_old - value_new1; assert value_new1 > 0; assert value_new2_derived > 0; assert value_new2_derived < 2**128; // Compute and verify first new commitment let precommitment_new1 = compute_precommitment(nullifier_new1, secret_new1); let calculated_commitment_new1 = compute_commitment(value_new1, asset_address_public, precommitment_new1); // Compute and verify second new commitment let precommitment_new2 = compute_precommitment(nullifier_new2, secret_new2); let calculated_commitment_new2 = compute_commitment(value_new2_derived, asset_address_public, precommitment_new2); // 8. Ensure nullifier uniqueness (good practice) // new1 != old, new2 != old, new1 != new2 assert(nullifier_new1, nullifier_old); assert(nullifier_new2, nullifier_old); assert(nullifier_new1, nullifier_new2); } ``` Onchain code would bascially update new commitments and invalidate old commitments > #### The Join action is the opposite of Split. It allows a user to combine two existing shielded commitments of the same asset type into a single new shielded commitment. This can help consolidate funds and reduce the number of notes a user manages. --- > ## Topup Action The Topup action allows a user to add more funds from their public L1 wallet to their shielded balance of a specific asset. This is achieved by consuming an existing shielded commitment and creating a new one with an increased value equal to old_value + topup_value. The topup_value is transferred from the user's L1 wallet to the contract during the transaction. --- > ## Transfer Action The Transfer action allows a user (Alice) to privately send assets to another user (Bob) within the Xythum Darkpool. This is a two-stage process: 1. **Transfer:** Alice spends her existing commitment and creates an encrypted "Transfer Note" on-chain. This note specifies the amount, asset, and a cryptographic identifier for Bob (e.g., hash of Bob's public receiving key). Alice may also create a change commitment for herself if the spent commitment's value exceeds the transfer amount. 2. **Claim:** Bob (or anyone with Bob's receiving secret) uses a ZK proof to claim the assets from the Transfer Note, creating a new standard commitment for themselves. **Transfer Phase Details:** Alice wishes to transfer `value_transfer` of `asset_address` to Bob. - Alice has `commitment_old_A`. - Alice creates `commitment_change_A` for `value_old_A - value_transfer` (if > 0). - Alice prepares a `hashed_receiver_info` (e.g., `H(bob_public_key)`). Bob must be able to reconstruct this with his private key and the blinding factor (which Alice must communicate to Bob off-chain,). | withdraw circuit can be user for transfer circuit as well. but onchain function changes ```solidity function transfer(Proof transferProof, bytes32 reciver_hash) public { let (uint236 transfer_value, bytes32 root, uint32 depth, bytes32 nullifier,bytes32 newcommitment,IERC20 asset) = verifier.verify(transferProof); validate_tree_state(root, depth); check_and_invadidate_nullifier(nullifier); // pure mapping update_merkle_tree(newcommitment); note_nonce++; notes[keccak(abi.encodePacked(note_nonce, reciver_hash))] = Note({ asset, transfer_value, reciver_hash isClaimed: false, claimedTimestamp: now() }) asset.safeTransfer(transfer_value, reciver); } ``` :::info One of the idea is that instead of bob reciver used raw, we could try combinations of bob_address + blinding_factor per NOTE. (this is not circuit dependent dervied private key can be tweaked with blinding_factor) This is if Bob concerned of leaking his address.! an addional privacy layer. but adds complexity of managing all blinding factors [ bob can store blinding factors on a DA and have privkey for himself ] Although bob_address is not plain HD wallet derived address. most likely it would be `SIGNATURE AGAINST SOME XYTHUM CONSTANT` ::: > ## Claim Action The Claim action allows the intended recipient (Bob) of a Transfer Note to take ownership of the assets specified in the note. Bob does this by providing a ZK proof that he possesses the necessary secret(s) to reconstruct the hashedReceiverInfo associated with an unclaimed note. ```rust // Noir (Conceptual) fn claim_note_circuit( receiver_secret_key: Field, // Bob's secret key nullifier_new_B_private: Field, // For Bob's new commitment secret_new_B_private: Field, // For Bob's new commitment // Public Inputs (some fetched from contract storage based on noteId) note_value_public: Field, // Value from the note note_asset_address_public: Field, // Asset from the note pub hashed_receiver_address: Field, // Inputs to nullify old commitments can also be used to topup existing Bob commitments ) { let calculated_hashed_address = pedersen_hash(Pubkey(receiver_secret_key_private)); // Domain sep 2 for note assert_eq(calculated_hashed_address, hashed_receiver_address); // 2. Compute Bob's new commitment let precommitment_new_B = compute_precommitment(nullifier_new_B_private, secret_new_B_private); let calculated_commitment_new_B = compute_commitment(note_value_public, note_asset_address_public, precommitment_new_B); assert(note_value_public > 0); } ``` Onchain Claim Solidity ```rust function claim(Proof claimProof) public { // check Bob zk Proof validity about his ownership over `address`. // validate order amounts and nonce from circuit vs in-storage mapping Note. // If everything looks good update the commitment tree with new commitment. } ``` :::warning SECONT 2 - 3 Pending. - Discusses about Onchain Defi Integrations. - As a spoiler. For interaction with other defi application user has to prove he owns a commitment like in any other action and also passes reciver_hash as a param to defi integration function. xthum Darkpool would validate and perform action using users funds and create NOTE. - Notes are just like precommitments its just notes offer easier ways to manage the commitments. :::

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password

    or

    By clicking below, you agree to our terms of service.

    Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully