Skip to content

ZK Architecture

SIP uses Noir as the primary ZK circuit language for zero-knowledge proofs.

SIP requires ZK proofs for three core operations:

  1. Funding Proof - Prove sufficient funds without revealing balance
  2. Validity Proof - Prove intent authorization without revealing sender
  3. Fulfillment Proof - Prove correct execution without revealing path

Noir provides the best balance of:

  • Developer experience (Rust-like syntax)
  • Backend flexibility (UltraPlonk, Groth16, Halo2)
  • Universal setup (no per-circuit trusted setup)
  • Rich standard library
CriteriaCircomNoirHalo2
Security9/107/107/10
DX5/109/104/10
Performance9/107/108/10
Trust Assumptions5/108/1010/10
Weighted Score7.057.756.55

Proves user has sufficient balance without revealing actual amount.

fn funding_proof(
// Public inputs
commitment_hash: pub Field,
minimum_amount: pub u64,
// Private inputs
actual_balance: u64,
blinding_factor: Field,
) {
// Prove balance is sufficient
assert(actual_balance >= minimum_amount);
// Prove commitment is correctly formed
let computed = pedersen_commit(
actual_balance as Field,
blinding_factor
);
assert(commitment_hash(computed) == commitment_hash);
}

Proves intent is authorized without revealing sender.

fn validity_proof(
// Public inputs
intent_hash: pub Field,
sender_commitment: pub Field,
// Private inputs
sender_address: Field,
signature: [u8; 64],
blinding: Field,
) {
// Verify signature
let is_valid = ecdsa_verify(
sender_address,
intent_hash,
signature
);
assert(is_valid);
// Verify sender commitment
let address_hash = poseidon::hash_1([sender_address]);
let computed = pedersen_commit(address_hash, blinding);
assert(commitment_hash(computed) == sender_commitment);
}

Proves solver correctly executed the swap.

fn fulfillment_proof(
// Public inputs
intent_id: pub Field,
output_commitment: pub Field,
min_output: pub u64,
recipient_stealth: pub Field,
// Private inputs
output_amount: u64,
output_blinding: Field,
tx_hash: Field,
) {
// Prove output meets minimum
assert(output_amount >= min_output);
// Prove commitment correctness
let computed = pedersen_commit(
output_amount as Field,
output_blinding
);
assert(commitment_hash(computed) == output_commitment);
// Prove delivery to stealth address
// (binding to tx_hash proves execution)
}

Noir provides built-in support for:

PrimitiveUsage
Pedersen hashCommitment scheme
SHA256Compatibility
PoseidonField-efficient hashing
ECDSAWallet signatures
EdDSAAlternative signatures
SchnorrStealth derivation
┌────────────────────────────────────────┐
│ SDK (TypeScript) │
│ ┌──────────────────────────────────┐ │
│ │ ProofProvider │ │
│ │ ┌────────────┐ ┌─────────────┐ │ │
│ │ │MockProvider│ │NoirProvider │ │ │
│ │ └────────────┘ └─────────────┘ │ │
│ └──────────────────────────────────┘ │
│ │ │
└───────────────────┼─────────────────────┘
┌───────────┴───────────┐
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ Mock Proofs │ │ Noir/NoirJS │
│ (Testing) │ │ (Production) │
└───────────────┘ └───────────────┘
┌─────────┴─────────┐
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│UltraPlonk│ │ Groth16 │
│ Backend │ │ Backend │
└──────────┘ └──────────┘
interface ProofProvider {
generateFundingProof(params: FundingProofParams): Promise<ProofResult>
generateValidityProof(params: ValidityProofParams): Promise<ProofResult>
generateFulfillmentProof(params: FulfillmentProofParams): Promise<ProofResult>
verifyProof(proof: ZKProof, publicInputs: Field[]): Promise<boolean>
}

For testing and development:

const sip = new SIP({
network: 'testnet',
proofProvider: new MockProofProvider()
})

For production:

const sip = new SIP({
network: 'mainnet',
proofProvider: new NoirProofProvider({
wasmPath: '/circuits/funding.wasm',
circuitPath: '/circuits/'
})
})
OperationMockNoir (estimated)
Funding proof gen<1ms2-5s
Validity proof gen<1ms2-5s
Fulfillment proof gen<1ms2-5s
Verification<1ms~10ms

NoirJS enables client-side proof generation:

import { compile, createProver } from '@noir-lang/noir_js'
// Load circuit
const circuit = await compile('/circuits/funding.nr')
const prover = await createProver(circuit)
// Generate proof in browser
const proof = await prover.prove({
actual_balance: 1000n,
blinding_factor: randomField(),
commitment_hash: commitmentHash,
minimum_amount: 500n
})
RiskMitigation
Less battle-testedSimple circuits, formal verification
Performance gapsStart with UltraPlonk, can switch
Smaller ecosystemCore primitives well-supported
  • MockProofProvider for testing
  • Circuit specifications defined
  • ProofProvider interface stable
  • Noir circuit implementation
  • NoirJS integration
  • Browser proof generation
  • Recursive proofs for batching
  • Alternative backends
  • Formal verification