Talk to the team
MenuClose

Live on Midnight stagenet

Every app has a night mode.

Every app can be made private. Sig.Network gives Midnight contracts real accounts on Ethereum, so your users trade on Uniswap, lend on Aave and deposit from any wallet or exchange exactly as they do today, with nobody’s name on any of it.

Read the docs View examples Talk to the team

The Sig.Network demo app: balances in ETH, SOL and BTC, a swap panel and an activity list of deposits, swaps and sends
Try the demo · deposit, swap, send

How it works

Your Midnight contract gets its own account on Ethereum.

Sig.Network is a signing network. It holds the key to an Ethereum account on behalf of your contract, and signs only what your contract asks it to. To Ethereum it is an ordinary account doing ordinary things. To your users it is your app, with their balances and their identities shielded on Midnight.

  • 01 · Midnight

    Your contract decides.

    A Compact circuit checks the request, burns the user’s shielded claim and queues one Ethereum transaction.

  • 02 · sig.network

    The network signs.

    A threshold of independent signers produces a standard Ethereum signature. No single signer can act alone, and nothing is bridged or wrapped.

  • 03 · Ethereum

    The chain settles.

    Uniswap, Aave or a plain transfer executes from your account at the real price. The result is attested back to Midnight and the user is credited.

Use cases

Three things to build this quarter.

  • A dark pool on any DEX

    Orders net inside your pool on Midnight. Only the net hits Uniswap, from one account, at Uniswap’s price. You set the privacy fee.

    For trading venues and aggregators
  • Private positions on a public book

    Users take positions on Polymarket through your pool. The market sees the pool, the pool knows the client, and the venue can ask.

    For prediction and perps front ends
  • Shielded in, shielded out

    Deposits arrive from any wallet or exchange to a one-time address, earn on Ethereum, and leave to a fresh address. No reusable address anywhere.

    For wallets, payments and ZEC

Adopt it one layer at a time.

Start with a private pool in front of an app people already use. Then move your logic onto Midnight piece by piece: the strategy, then routing, then lending. Every layer below the line keeps Ethereum’s liquidity, and every app draws its own line.

Privacy poolEthereumMidnight
Vault logicEthereumMidnight
RoutingEthereumMidnight
Lending poolEthereumMidnight
DEX liquidityEthereumMidnight

Developers

Write it in Rust, or in Compact.

Import the Signet module and queue Ethereum transactions from your circuits. Keep your own ledger and your own rules. The examples repo has a complete ERC-20 vault with deposit, withdraw, swap and supply, deploy tooling and integration tests. Prefer Rust? MinoCrab is an experimental eDSL that compiles the same contracts with cargo, rust-analyzer and #[test].

erc20-vault.compact · startDeposit, abridgedView on GitHub
struct DepositRequest { erc20Address: Bytes<20>; amount: Uint<128>; }

export circuit startDeposit(
  inIndex: Uint<64>, evmNonce: Uint<64>, gas: GasParams, depositRequest: DepositRequest
): [] {
  requireInitialised();
  assert(allowedTokens.member(disclose(depositRequest.erc20Address)), "ERC20 not allowed");
  assert(depositRequest.amount > 0 as Uint<128>, "Amount must be positive");

  // The depositor's own account pays: the signing path is their commitment, not the vault's.
  const args = disclose(DepositArgs {
    request: depositRequest, path: userCommitment(callerSecretKey()), gas: gas
  });
  depositArgsMap.insert(disclose(inIndex), args);

  // Queue it. Only flushQueue touches shared state, so deposits never contend with each other.
  // sendDeposit then builds transfer(vaultEvmAddress, amount) and hands it to the signing network.
  inputRequestBuffer.insert(disclose(inIndex), disclose(RequestBufferEntry {
    action: Action.deposit, useNextVaultAccountNonce: false, evmNonce: evmNonce, inIndex: inIndex,
    commitment: ownershipCommitment(inIndex, callerSecretKey()), argsHash: depositArgsHash(args)
  }));
}
erc20_vault.rs · the same circuit in MinoCrab, abridgedView on GitHub
#[derive(CircuitArg)]
struct DepositRequest { erc20_address: Bytes<20>, amount: Uint<128> }

#[circuit]
pub fn start_deposit(
    c: &mut Circuit3,
    in_index: Uint<64>, evm_nonce: Uint<64>, gas: GasParamsArg, deposit_request: DepositRequest,
) -> Discloses<(DepositedErc20, InIndex, DepositedAmount, DepositorCommitment,
                GasParams, EvmNonce, OwnershipCommitment)> {
    require_initialised(c);
    let erc20 = deposit_request.erc20_address.disclose_as::<DepositedErc20>(c);
    let allowed = VAULT.allowed_tokens.member(c, &erc20);
    c.assert(is_true(allowed).message("ERC20 not allowed"));
    c.assert(deposit_request.amount.gt(0u64).message("Amount must be positive"));

    // Every disclosure is typed, and the return type lists exactly what leaves the proof.
    let in_index = in_index.disclose_as::<InIndex>(c);
    let sk = common::witness_sk(c);
    let path = user_commitment(c, &sk).disclose_as::<DepositorCommitment>(c);
    let amount = deposit_request.amount.disclose_as::<DepositedAmount>(c);
    let args = DepositArgs {
        request: StoredDepositRequest { erc20_address: erc20, amount },
        path,
        gas: StoredGas { gas_limit: gas.gas_limit.disclose_as::<GasParams>(c), /* and the two fee fields */ },
    };
    VAULT.deposit_args_map.insert(c, &in_index, &args);

    // Queue it. Only flush_queue touches shared state, so deposits never contend with each other.
    // send_deposit then builds transfer(vault_evm_address, amount) and hands it to the signing network.
    let evm_nonce = evm_nonce.disclose_as::<EvmNonce>(c);
    let digest = args_hash(c, DEPOSIT_ARGS_PAD, &args);
    queue_request(c, action::DEPOSIT, false, evm_nonce.field(), in_index, digest);
    Discloses::of(())
}

Compliance

Compliance, built in.

  • Deposit rules

    Your contract defines what it accepts, holds or returns. Screen on arrival and credit only what passes.

  • Screening

    Your pool is an ordinary Ethereum account. The tools regulated venues already use work on it unchanged.

  • Segregation

    Each app has its own accounts. Nothing sits in a shared pot with actors you don’t trust.

  • Disclosure

    Define in your contract what users share to take part, and who may ask. Your Midnight ledger is a complete record.

Roadmap

One pool, every chain.

The pool lives on Midnight and holds an account on each chain it reaches. Moving between chains never leaves the pool, so every chain added makes every crossing rarer.

  • Stagenet · nowMidnight → EthereumDeposits, Uniswap swaps, Aave supply, withdrawals to any address. Open examples and a full-stack demo.
  • Mainnet · with ledger v9Midnight mainnetThe same contracts and the same accounts, with real funds.
  • December 2026TronWhere the dollars move. One pool, two chains, no crossing between them.

FAQ

Questions people ask first.

Is this a bridge?

No. Nothing is locked, minted or wrapped. Your contract holds a real Ethereum account, and assets stay on Ethereum the whole time. What moves is a signature.

Who holds the keys?

A threshold network of independent signers. No single signer has the key, and the network signs only requests that come from your contract on Midnight. Your contract is the policy.

What does Ethereum see?

One ordinary account doing ordinary things: a transfer in, a swap, a supply, a transfer out. Not who, not how much of whose, not why. Amounts and timing on the public leg are visible, as they are for any Ethereum account.

How does compliance work?

Your pool is a normal Ethereum account, so the screening tools venues already use apply unchanged. Your contract sets the deposit rules, and you decide who may ask what, written in code rather than negotiated afterwards.

What does it cost?

Signing is priced per request and the gas is paid from your account on Ethereum. Builders typically charge a privacy fee on top; the dark pool pattern earns around 50 basis points. Talk to us for current pricing.

Build on Midnight

Why wouldn’t your users want privacy?

Read the docs View examples