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.
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.
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].
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)
}));
}
#[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.