Fluxe: A Universal Privacy Protocol for Cross-Chain Stablecoin Payments with Programmable Compliance
Abstract
We present Fluxe, a universal privacy protocol enabling private stablecoin transfers across multiple blockchain networks with regulatory compliance. The protocol combines hybrid proof architectures (client-side Groth16, server-side SP1), confidential UTXO models with indexed merkle trees, and zk-promises for asynchronous compliance callbacks. The system supports cross-chain private payments with automatic liquidity balancing and programmable payment rails.
1. Introduction
Current privacy protocols face the trilemma of privacy, compliance, and cross-chain interoperability. Fluxe solves this through zk-promises integration enabling asynchronous compliance determination without compromising transaction privacy.
2. Technical Architecture
2.1 Confidential UTXO Model
Each note N is defined as:
N = \{asset\_type, value, owner, \psi, chain\_hint, compliance\_data, lineage\_hash, pool\_id, callbacks\}
The note commitment is computed as:
cm = H(asset\_type \parallel value \parallel owner \parallel \psi \parallel chain\_hint \parallel compliance\_data \parallel lineage\_hash \parallel pool\_id \parallel H(callbacks))
The nullifier derivation prevents double-spending:
nf = H(auth\_secret \parallel \psi \parallel cm)
where auth\_secret is derived from the ownerβs private key and \psi is per-note entropy.
2.2 Indexed Merkle Trees
Traditional sparse merkle trees require d = 256 hash operations for membership proofs. Fluxe uses indexed merkle trees with sorted linked-list structure reducing this to d = 64 operations.
Each leaf node stores:
leaf = \{value, next\_index, next\_value\}
Non-membership Proof Implementation:
Using indexed Merkle trees, non-membership is proven by finding the low nullifier (the sanctioned address with the largest value less than the target address):
\text{NonMembershipProof}(addr, root, proof) =
\begin{cases} \text{MerkleProof}(low\_addr, root, path) \land \\ low\_addr.value < addr \land \\ (addr < low\_addr.next\_value \lor low\_addr.next\_value == 0) \end{cases}
where low\_addr = \{value, next\_index, next\_value\} is the sanctioned address entry that the target address would fall after in the sorted list.
The proof consists of:
- Merkle path for node v_i (64 hashes)
- Verification that v_i.next\_value = v_{i+1}
- Range check: v_i < v < v_{i+1}
2.3 Hybrid Proof System
Client-side circuits (Groth16):
Deposit Circuit:
Public: deposit_amount, asset_type, tx_hash, cm_out
Private: owner_secret, Ο
Constraints:
- Verify deposit transaction validity
- cm_out = H(asset_type || deposit_amount || owner || Ο || ...)
Transfer Circuit:
Public: nf_in, cm_out, merkle_root
Private: note_in, auth_secret, note_out
Constraints:
- nf_in = H(auth_secret || note_in.Ο || note_in.cm)
- MerkleProof(note_in.cm, merkle_root)
- value_in β₯ value_out + fee
- cm_out = H(note_out)
Withdrawal Circuit:
Public: nf, amount, dest_chain, recipient
Private: note, auth_secret
Constraints:
- nf = H(auth_secret || note.Ο || note.cm)
- amount β€ note.value
- dest_chain β supported_chains
Server-side verification (SP1):
SP1 verifies client Groth16 proofs using the groth16_bn254
precompile and performs batch state updates:
fn verify_and_update_state(
old_root: [u8; 32],
groth16_proofs: Vec<Groth16Proof>,
public_inputs: Vec<PublicInputs>
) -> [u8; 32] {
for (proof, inputs) in groth16_proofs.zip(public_inputs) {
groth16::verify(proof, inputs);
}
let new_nullifiers = extract_nullifiers(public_inputs);
indexed_merkle_tree::batch_insert(old_root, new_nullifiers)
}
2.4 Basic Client-Side Sanctions Screening
For basic compliance, the most fundamental requirement is proving that transaction addresses are not sanctioned. This is implemented through client-side zero-knowledge proofs against a commitment to flagged addresses.
Sanctions List Commitment:
The protocol maintains a Merkle tree commitment to sanctioned addresses:
sanctions\_root = \text{MerkleRoot}(\{addr_1, addr_2, ..., addr_n\})
where each addr_i is a sanctioned address hash.
Basic Sanctions Screening Circuit:
Public: sanctions_root, tx_valid
Private: sender_addr, recipient_addr, sanctions_proof_sender, sanctions_proof_recipient
Constraints:
- NonMembershipProof(sender_addr, sanctions_root, sanctions_proof_sender)
- NonMembershipProof(recipient_addr, sanctions_root, sanctions_proof_recipient)
- tx_valid = (sender_proof_valid β§ recipient_proof_valid)
NOTE: While basic sanctions screening happens client-side, the systemβs true power lies in its ability to enforce compliance retroactively through callbacks. See Section 3.10 for how assets can be frozen post-transaction if compliance issues are discovered.
2.5 Cross-Chain Bridge Protocol
Bridge contracts maintain the invariant:
\sum_{i} deposits_i = \sum_{j} withdrawals_j + \sum_{k} liquidity\_k
For cross-chain operations, the protocol coordinates:
- Source chain withdrawal: Burn note, emit event with proof
- Message passing: CCTP (for USDC) or LayerZero (for others)
- Destination chain deposit: Mint equivalent note after verification
The liquidity routing algorithm selects optimal paths based on:
cost(path) = gas\_cost + bridge\_fee + time\_penalty \cdot delay
This is done through external keepers that help pay gas and perform this actions on observed events, although the balancing functions are permissionless and can be called by anybody.
2.6 Integration with Twine Multi-Settlement Network
Fluxe operates as an application layer on top of Twineβs multi-settlement infrastructure, leveraging its light client architecture for cross-chain functionality:
- Light Client Verification: Twine maintains light clients for each supported chain to verify state transitions
- Cross-Chain Proofs: State updates are verified through light client proofs before acceptance on remote chains
- Asynchronous Settlement: Each chain processes Fluxe state updates upon light client verification
- Trust-Minimized Bridges: Asset transfers verified through light client state proofs
This architecture allows Fluxe to maintain privacy while achieving cross-chain interoperability through cryptographic verification rather than consensus.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β TWINE β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β FLUXE β β
β β β β
β β β’ Nullifier Tree (IMT) β β
β β β’ Commitment Tree β β
β β β’ Transfer Proofs (Groth16) β β
β β β’ Compliance Callbacks β β
β β β’ SP1 Verification β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β Light Clients β
βββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββ
β
ββββββββββββ ββββββββββββ
β Ethereum β β Solana β
β Bridge β β Bridge β
ββββββββββββ ββββββββββββ
(Deposit/ (Deposit/
Withdraw) Withdraw)
3. zk-Promises Integration for Asynchronous Compliance
3.1 zk-Objects Foundation
Fluxe extends the zk-objects model where each user maintains a compliance object containing state and a unique nullifier for updates. The object structure:
obj = \{state, serial, cbList\}
where state contains compliance information, serial is a unique identifier preventing replay attacks, and cbList maintains pending callbacks.
The object commitment:
cm_{obj} = \text{Commit}(state \parallel serial \parallel H(cbList), r_{obj})
Object updates follow copy-on-write semantics: creating new commitments with fresh nullifiers while proving valid state transitions.
3.2 Dual Bulletin Board Architecture
The system maintains two global bulletin boards:
Object Bulletin Board (bb_{obj}): Stores object commitments using Merkle trees for membership proofs. The tree root rt_{obj} represents all valid object states.
Callback Bulletin Board (bb_{cb}): Stores callback invocations requiring both membership and non-membership proofs. Uses complement set approach partitioning unused ticket space into ranges.
Non-membership proofs implemented through signed range sets:
\text{NonMember}(ticket) \iff \exists (a,b) \in \text{RangeSet}: a < ticket < b
3.3 Callback Creation Protocol
Users create compliance callbacks during transaction execution through the \text{ExecMethodAndCreateCallback} algorithm:
Step 1: Generate rerandomized ticket from compliance providerβs public key:
ticket = pk_{compliance} \cdot r^{sk_{user}} \mod p
where r is cryptographically random and sk_{user} is the userβs signing key.
Step 2: Create callback entry with expiration and encryption:
cb_{entry} = \{ticket, exp\_time, enc\_key, method\_id\}
where enc\_key = \text{KDF}(user\_secret, ticket) for per-callback encryption.
Step 3: Update object with new callback list:
obj'.cbList = obj.cbList \parallel cb_{entry}
obj'.serial = \text{Fresh}()
Step 4: Generate ZK proof of valid transition:
Public: cm_old, cm_new, method_id
Private: obj_old, obj_new, r_old, r_new, cb_entry
Constraints:
- cm_old = Commit(obj_old, r_old)
- cm_new = Commit(obj_new, r_new)
- obj_new.cbList = obj_old.cbList || cb_entry
- obj_new.state = ValidTransition(obj_old.state, method_id)
3.4 Callback Invocation and Processing
Compliance providers invoke callbacks by posting to bb_{cb}:
invocation = \{ticket, \text{Enc}_{enc\_key}(args), timestamp, \sigma\}
where \sigma is a signature over (ticket, args, timestamp) using the ticket as public key.
Users process callbacks through \text{ScanOne} algorithm:
Step 1: Iterate through pending callbacks in cbList
Step 2: Check membership in bb_{cb}:
\exists invocation \in bb_{cb}: invocation.ticket = cb_{entry}.ticket
Step 3: If found and valid:
- Decrypt arguments: args = \text{Dec}_{enc\_key}(invocation.args)
- Execute method: state' = method(state, args)
- Remove from callback list: cbList' = cbList \setminus \{cb_{entry}\}
Step 4: Generate proof of correct processing:
Public: cm_old, cm_new, timestamp
Private: obj_old, obj_new, args, cb_entry
Constraints:
- ValidDecryption(cb_entry.enc_key, invocation.args, args)
- obj_new.state = ExecuteMethod(obj_old.state, args)
- obj_new.cbList = obj_old.cbList \ {cb_entry}
- timestamp < cb_entry.exp_time
3.5 Security Properties and Unlinkability
Unlinking Creation and Invocation: The rerandomization scheme ensures:
\Pr[\text{Link}(ticket_{create}, ticket_{invoke}) = 1] \leq \text{negl}(\lambda)
without knowledge of sk_{user} or r.
Authenticity via Signature-based Tickets: Tickets function as verification keys in signature scheme:
\text{Verify}(ticket, (args, timestamp), \sigma) = 1 \iff \text{valid invocation}
Confidentiality: Per-callback encryption prevents unauthorized access:
\text{Enc}_{enc\_key}(args) \text{ computationally indistinguishable from random}
3.6 Compliance State Machine Implementation
Enhanced compliance state for payment protocols:
S = \{
\quad compliance\_level \in \{0,1,2,3\},
\quad last\_review\_time \in \mathbb{N},
\quad pending\_callbacks: \text{List}[CallbackEntry],
\quad risk\_score \in [0, 2^{32}),
\quad jurisdiction\_flags: \text{BitSet},
\quad transaction\_limits: \{daily, monthly, yearly\},
\quad reputation\_vector: \mathbb{R}^n
\}
State Transition Functions:
\text{CreateTxCallback}(S, tx\_data) \rightarrow S':
- Analyze transaction risk: risk = \text{RiskScore}(tx\_data, S.reputation\_vector)
- Generate callbacks based on risk thresholds
- Update pending list: S'.pending = S.pending \cup \{\text{new callbacks}\}
\text{ProcessCallback}(S, result) \rightarrow S':
- Update compliance level: S'.level = f(S.level, result.determination)
- Adjust risk score: S'.risk\_score = \text{UpdateRisk}(S.risk\_score, result)
- Modify limits if necessary: S'.limits = \text{AdjustLimits}(S.limits, result)
\text{TimeoutCallback}(S, cb) \rightarrow S':
- Apply conservative penalty: S'.level = \max(0, S.level - 1)
- Increase risk score: S'.risk\_score = \min(2^{32} - 1, S.risk\_score \times 1.5)
3.7 Compliance Method Implementations
Sanctions Screening Method:
method_sanctions(state, args):
sanctions_determination = args.determination
entity_list = args.flagged_entities
if sanctions_determination == "CLEAR":
state.compliance_level = max(state.compliance_level, 2)
elif sanctions_determination == "FLAGGED":
state.compliance_level = 0
state.transaction_limits = RESTRICTED
state.last_review_time = current_time()
return state
AML Risk Assessment Method:
method_aml(state, args):
risk_assessment = args.risk_score // [0, 100]
volume_analysis = args.volume_flags
if risk_assessment < 20:
state.compliance_level = min(3, state.compliance_level + 1)
elif risk_assessment > 80:
state.compliance_level = max(0, state.compliance_level - 1)
state.transaction_limits.daily *= 0.5
state.risk_score = risk_assessment
return state
Jurisdiction Compliance Method:
method_jurisdiction(state, args):
user_jurisdiction = args.jurisdiction
allowed_operations = args.permitted_ops
state.jurisdiction_flags |= (1 << user_jurisdiction)
if "HIGH_VALUE_TX" not in allowed_operations:
state.transaction_limits.daily = min(state.limits.daily, 10000)
return state
3.8 Efficient Callback List Management
Callback lists use hash-chain representation for efficiency:
h_\ell := H(H(...H(H(\epsilon), cb_1), cb_2)...), cb_n)
Append Operation (O(1)):
h'_\ell = H(h_\ell, cb_{new})
Removal Operation during scanning:
State machine encoding enables amortized O(1) removal by rebuilding hash chain incrementally.
Batch Processing:
For k callbacks, proving complexity is O(k \log k) using recursive proof composition.
3.9 Privacy-Preserving Compliance Attestations
Users generate ZK proofs demonstrating compliance without revealing transaction details:
Enhanced Sanctions Compliance Circuit:
Public: attestation_hash, time_period
Private: tx_history, sanctions_root, compliance_callbacks
Constraints:
- β tx β tx_history: tx.timestamp β time_period
- β tx β tx_history: CheckSanctions(tx.counterparty, sanctions_root) = CLEAR
- β cb β compliance_callbacks: cb.result = "SANCTIONS_CLEAR"
- attestation_hash = H(tx_history || compliance_callbacks)
Multi-Jurisdiction AML Circuit:
Public: jurisdictions[], thresholds[], attestation
Private: transactions, amounts, callback_results
Constraints:
- β j β jurisdictions:
Ξ£(amounts | tx.jurisdiction = j) β€ thresholds[j]
- β result β callback_results: result.aml_status = "COMPLIANT"
- attestation = CommitToCompliance(transactions, callback_results)
Source of Funds Verification:
Public: legitimacy_threshold, source_categories
Private: fund_sources, verification_callbacks, lineage_proof
Constraints:
- β source β fund_sources: source.category β source_categories
- β verification β verification_callbacks:
verification.source_legitimacy β₯ legitimacy_threshold
- ValidLineage(lineage_proof, fund_sources)
3.10 Post-Transaction Compliance Enforcement
A key innovation of the callback system is enabling retroactive compliance actions. Unlike traditional systems that must block transactions upfront, Fluxe allows transactions to proceed while maintaining the ability to freeze or recover assets if compliance issues are discovered later.
3.10.1 Asset Freezing Mechanism
When a transaction is flagged post-facto, compliance callbacks can freeze the recipientβs assets:
method_freeze_assets(state, args):
freeze_reason = args.reason // "SANCTIONS_HIT", "AML_FLAG", etc.
freeze_timestamp = args.timestamp // block number
authority = args.compliance_authority
if verify_authority(authority):
state.frozen = true
state.freeze_reason = freeze_reason
state.freeze_time = freeze_timestamp
state.compliance_level = 0
// Prevent all outgoing transfers
state.transaction_limits = {
daily: 0,
monthly: 0,
yearly: 0
}
return state
3.10.2 Advantages of Post-Transaction Enforcement
1. Better User Experience:
- Legitimate transactions arenβt delayed by compliance checks
- Users can transact immediately while compliance runs asynchronously
- Reduces false positives that block legitimate users
2. More Comprehensive Screening:
- Can use advanced ML models that take longer to run
- Cross-reference with multiple databases without blocking transactions
- Perform deep chain analysis that would be too slow for real-time
3. Network Effects:
- Bad actors can be identified through transaction patterns
- Entire clusters of related addresses can be frozen together
- Compliance improves over time as more data is collected
3.10.3 Recovery and Unfreezing
Assets can be unfrozen if compliance checks clear:
method_unfreeze_assets(state, args):
clearance_proof = args.proof
reviewing_authority = args.authority
if verify_clearance(clearance_proof, reviewing_authority):
state.frozen = false
state.compliance_level = determine_new_level(clearance_proof)
state.transaction_limits = standard_limits(state.compliance_level)
return state
3.10.4 Time-Bounded Compliance
To balance compliance with usability, callbacks have expiration times:
- Initial Grace Period: 24-48 hours for basic compliance checks
- Extended Review: Up to 30 days for complex cases
- Auto-Release: If no callback invoked within expiry, transaction is considered cleared
This prevents indefinite asset freezing while giving compliance providers reasonable time to act.
4. Programmable Payment Rails
4.1 Recurring Payment Protocol
Recurring payments implemented through zk-promises scheduling:
RecurringPayment = \{recipient, amount, frequency, max\_payments, conditions\}
The payment schedule creates callbacks at intervals:
callback\_times = [start\_time + i \cdot frequency \mid i \in [0, max\_payments]]
Each callback verifies:
- Service conditions met: \text{VerifyConditions}(service\_state, conditions) = \text{true}
- Payment authorization valid: current\_time \leq start\_time + max\_payments \cdot frequency
- Sufficient balance: user\_balance \geq amount
4.2 Payment Streaming
Continuous payment streams with rate limiting:
Stream = \{recipient, rate, duration, start\_time\}
Available amount at time t:
available(t) = \min(rate \cdot (t - start\_time), total\_amount)
Withdrawal constraints:
\sum_{i} withdrawn_i \leq available(current\_time)
4.3 Conditional Payments
Multi-party escrow with programmable conditions:
ConditionalPayment = \{parties, conditions, timeout, resolution\_method\}
Release conditions encoded as circuit constraints:
Public: condition_hash, resolution
Private: condition_data, signatures
Constraints:
- H(condition_data) = condition_hash
- VerifyCondition(condition_data) = resolution
- VerifySignatures(parties, signatures)
5. Security Analysis
5.1 Privacy Guarantees
Unlinkability: Given note commitments cm_1, cm_2, without knowledge of secrets, the probability of linking them is negligible:
\Pr[\text{Link}(cm_1, cm_2) = \text{true} \mid \text{no secrets}] \leq \text{negl}(\lambda)
Amount Confidentiality: Transaction amounts remain computationally hidden under the commitment schemeβs hiding property.
Temporal Privacy: Transaction timing unlinkable to user identity through batching with anonymity set size |A| \geq 2^k for security parameter k.
5.2 Compliance Integrity
Callback Authenticity: Signatures prevent forgery with security reduction to underlying signature scheme.
Evasion Resistance: Users cannot transact without processing callbacks due to state machine constraints.
Audit Trail: Complete compliance history maintained with selective disclosure through ZK proofs.
5.3 Cross-Chain Consistency via light clients
Global State: All chains maintain consistent view of global nullifier set N_{global} and note set C_{global} through Twineβs multi settlement.
State Verification:
Chain A β State proof β Verified by Light Client on Twine β Generate execution proof β Verify on Chain B β Update State
Since Fluxe operates entirely on Twine, state consistency is straightforward:
- Single State Tree: One global nullifier set and commitment tree
- Atomic Updates: All state transitions are atomic within Twine
- No Synchronization: No cross-chain state to synchronize
- Instant Finality: Transfers are final immediately on Twine
The only cross-chain interaction within Fluxe is verifying deposit/withdrawal events through Twineβs light clients.
6. Implementation Details
6.1 Circuit Constraints
The transfer circuit has constraint count:
- Merkle proof verification: 64 \times \text{hash_constraints}
- Nullifier computation: \text{hash_constraints}
- Value conservation: 1 constraint
- Range checks: 2 \times \log_2(\text{max_value}) constraints
6.2 Finality
Finality on settlement layer: Expected E[\text{finality}] = \max_i (E[\text{finality}_i] + E[\text{batch_posting}_i]) where i ranges over involved chains.
7. Comparison with Existing Protocols
Protocol | Privacy | Compliance | Cross-Chain |
---|---|---|---|
Tornado Cash | β | β | β |
Aztec | β | Partial | β |
Fluxe | β | β | β |
8. Future Work
Recursive SNARKs: Transition to Halo2/Nova for transparent setup and better recursion.
Sharded State: Parallel processing with state sharding for higher throughput.
Advanced Compliance: ML-based risk assessment with privacy-preserving inference.
Cross-Chain Messaging: Integration with additional bridge protocols and interoperability standards.
9. Conclusion
Fluxe provides a comprehensive solution to the privacy-compliance-interoperability trilemma through novel integration of zk-promises with cross-chain infrastructure.
References
- Kattis, A. et al. (2024). βzk-Promises: Making Zero-Knowledge Objects Accountableβ
- Buterin, V. (2022). βPrivacy Poolsβ
- Succinct Labs (2024). βSP1: The Performant, Open-Source ZK Virtual Machineβ
- Moore, C. & Gandhi, S. (2024). βL2 Ethereum ZK Rollup for Private and Compliant Transactionsβ
- Circle (2023). βCross-Chain Transfer Protocol (CCTP) Documentationβ
- LayerZero (2023). βOmnichain Fungible Token (OFT) Standardβ
- Ioanna Tzialla, Abhiram Kothapalli, Bryan Parno, and Srinath Setty (2023). Transparency dictionaries with succinct proofs of correct operatio