Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
680b109
feat: setup co-noir & switch to U252 (#9)
olehmisar Feb 6, 2025
4512652
feat: dark pool router POC (#10)
olehmisar Feb 6, 2025
88d348a
Merge branch 'main' into dark-pool
olehmisar Feb 6, 2025
c332570
feat: add simplest order matching (#12)
olehmisar Feb 6, 2025
53550c0
feat: mpc swap (#13)
olehmisar Feb 6, 2025
9c60956
feat: pre-verify mpc proofs (#14)
olehmisar Feb 7, 2025
b2d6741
refactor: remove numPublicInputs param
olehmisar Feb 7, 2025
327139d
refactor: simpler native proof decoding (#16)
olehmisar Feb 8, 2025
eb42205
Merge branch 'main' into dark-pool
olehmisar Feb 8, 2025
adb0116
refactor: use spawn instead of exec
olehmisar Feb 8, 2025
2a2faab
feat: order matching queue (#21)
olehmisar Feb 8, 2025
c8d8fe9
Merge branch 'main' into dark-pool
olehmisar Feb 10, 2025
c7111d2
chore(docs): explain what the project is about
olehmisar Feb 14, 2025
81c07ea
Merge branch 'main' into dark-pool
olehmisar Feb 27, 2025
51cc360
feat: switch to U253 (#23)
olehmisar Feb 27, 2025
00720c1
chore: update README.md
olehmisar Mar 12, 2025
33280ff
Merge branch 'main' into dark-pool
olehmisar Jun 3, 2025
7b07a93
chore(ci): bump ubuntu & nodejs
olehmisar Jun 4, 2025
db641e7
Merge branch 'main' into dark-pool
olehmisar Jun 4, 2025
ed2f720
fix hasher
olehmisar Jun 4, 2025
a99c9b4
show coSnarks installation
olehmisar Jun 4, 2025
6a24b6f
Merge branch 'main' into dark-pool
olehmisar Jun 4, 2025
adc146d
chore(ci): downgrade node
olehmisar Jun 4, 2025
b97f5e7
Merge branch 'main' into dark-pool
olehmisar Jul 8, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 10 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
<img align="right" width="150" height="150" top="100" src="https://github.com/user-attachments/assets/c80982e6-103e-45b0-8bd1-b6c38c5debe5">
<img align="right" width="150" height="150" top="100" src="https://i.ibb.co/4ZFHPTNc/411361781-c80982e6-103e-45b0-8bd1-b6c38c5debe5-Large.jpg">

# Mezcal

Mezcal (Nahuatl: mexcalli) - agave booze.
Mezcal (Nahuatl: mexcalli - agave booze) - on-chain dark pool implementation using [Noir](https://noir-lang.org) and [Taceo coNoir](https://taceo.io). Hides EVERYTHING about orders and traders(tokens, amounts and addresses of traders are completely hidden). Trades settled on an EVM chain using a very simplified version of [Aztec Protocol](https://aztec.network). The tradeoff is O(N^2) order matching engine.

The code is highly experimental. The core code is located in `packages/contracts`.

## Install coSnarks

```sh
cargo install --git https://github.com/TaceoLabs/co-snarks co-noir --rev 1b2db005ee550c028af824b3ec4e811d6e8a3705
```

## TODO

Expand All @@ -21,22 +29,3 @@ Mezcal (Nahuatl: mexcalli) - agave booze.
- [ ] deploy as proxy
- [ ] test contracts with larger token amounts
- [ ] TODO(security): parse inputs to circuits instead of assuming they are correct. Same applies to types returned from `unconstrained` functions. <https://github.com/noir-lang/noir/issues/7181> <https://github.com/noir-lang/noir/issues/4218>

### Backend

- [x] prove using native bb
- [ ] persist merkle trees
- [ ] return pending tree roots

### UI

- [x] shield
- [x] transfer
- [ ] join (maybe behind the scenes, multicall)
- [ ] unshield

### compliance

- [ ] unshield only mode
- [ ] set shield limit to 10 USDC
- [ ] disclaimer that the rollup is not audited
40 changes: 38 additions & 2 deletions packages/contracts/contracts/PoolERC20.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,22 @@ contract PoolERC20 is PoolGeneric {
IVerifier unshieldVerifier;
IVerifier joinVerifier;
IVerifier transferVerifier;
IVerifier swapVerifier;
}

constructor(
IVerifier shieldVerifier,
IVerifier unshieldVerifier,
IVerifier joinVerifier,
IVerifier transferVerifier,
IVerifier swapVerifier,
IVerifier rollupVerifier
) PoolGeneric(rollupVerifier) {
_poolErc20Storage().shieldVerifier = shieldVerifier;
_poolErc20Storage().unshieldVerifier = unshieldVerifier;
_poolErc20Storage().joinVerifier = joinVerifier;
_poolErc20Storage().transferVerifier = transferVerifier;
_poolErc20Storage().swapVerifier = swapVerifier;
}

function shield(
Expand All @@ -43,7 +46,7 @@ contract PoolERC20 is PoolGeneric {
) external {
token.safeTransferFrom(msg.sender, address(this), amount);

PublicInputs.Type memory pi = PublicInputs.create(1 + 2 + U256_LIMBS);
PublicInputs.Type memory pi = PublicInputs.create(1 + 2 + 1);
pi.push(getNoteHashTree().root);
pi.push(address(token));
pi.pushUint256Limbs(amount);
Expand Down Expand Up @@ -73,7 +76,7 @@ contract PoolERC20 is PoolGeneric {
// TODO(security): bring back unshield. It was removed because nullifiers are no longer checked on tx level. Only when the tx is rolled up.
require(false, "not implemented");

PublicInputs.Type memory pi = PublicInputs.create(6 + U256_LIMBS);
PublicInputs.Type memory pi = PublicInputs.create(6 + 1);
// params
pi.push(getNoteHashTree().root);
pi.push(getNullifierTree().root);
Expand Down Expand Up @@ -155,6 +158,39 @@ contract PoolERC20 is PoolGeneric {
}
}

// TODO: move to a separate contract
function swap(
bytes calldata proof,
NoteInput[4] calldata notes,
bytes32[2] calldata nullifiers
) external {
PublicInputs.Type memory pi = PublicInputs.create(1 + 6);
pi.push(getNoteHashTree().root);
pi.push(notes[0].noteHash);
pi.push(notes[1].noteHash);
pi.push(notes[2].noteHash);
pi.push(notes[3].noteHash);
pi.push(nullifiers[0]);
pi.push(nullifiers[1]);
require(
_poolErc20Storage().swapVerifier.verify(proof, pi.finish()),
"Invalid swap proof"
);

{
NoteInput[] memory noteInputs = new NoteInput[](4);
noteInputs[0] = notes[0];
noteInputs[1] = notes[1];
noteInputs[2] = notes[2];
noteInputs[3] = notes[3];
bytes32[] memory nullifiersDyn = new bytes32[](nullifiers.length);
for (uint256 i = 0; i < nullifiers.length; i++) {
nullifiersDyn[i] = nullifiers[i];
}
_PoolGeneric_addPendingTx(noteInputs, nullifiersDyn);
}
}

function _poolErc20Storage()
private
pure
Expand Down
9 changes: 5 additions & 4 deletions packages/contracts/contracts/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,11 @@ library PublicInputs {
Type memory publicInputs,
uint256 value
) internal pure {
uint256[U256_LIMBS] memory limbs = toNoirU256(value);
for (uint256 i = 0; i < limbs.length; i++) {
push(publicInputs, limbs[i]);
}
push(publicInputs, value);
// uint256[U256_LIMBS] memory limbs = toNoirU256(value);
// for (uint256 i = 0; i < limbs.length; i++) {
// push(publicInputs, limbs[i]);
// }
}

function finish(
Expand Down
5 changes: 5 additions & 0 deletions packages/contracts/deploy/00_deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ const deploy: DeployFunction = async ({
"Erc20TransferVerifier",
"erc20_transfer",
);
const swapVerifier = await deployVerifier(
"LobRouterSwapVerifier",
"lob_router_swap",
);
const rollupVerifier = await deployVerifier("RollupVerifier", "rollup");

const pool = await typedDeployments.deploy("PoolERC20", {
Expand All @@ -44,6 +48,7 @@ const deploy: DeployFunction = async ({
unshieldVerifier.address,
joinVerifier.address,
transferVerifier.address,
swapVerifier.address,
rollupVerifier.address,
],
});
Expand Down
2 changes: 2 additions & 0 deletions packages/contracts/noir/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ members = [
"erc20_unshield",
"erc20_join",
"erc20_transfer",
"lob_router",
"lob_router_swap",
"rollup",
"common",
]
1 change: 0 additions & 1 deletion packages/contracts/noir/common/Nargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,4 @@ authors = ["Oleh Misarosh <[email protected]>"]

[dependencies]
protocol_types = { git = "https://github.com/AztecProtocol/aztec-packages/", tag = "v0.86.0", directory = "noir-projects/noir-protocol-circuits/crates/types" }
bignum = { tag = "v0.7.3", git = "https://github.com/noir-lang/noir-bignum/" }
nodash = { tag = "v0.41.2", git = "https://github.com/olehmisar/nodash/" }
6 changes: 3 additions & 3 deletions packages/contracts/noir/common/src/erc20_note.nr
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ impl Erc20Note {
}
}

impl crate::Serialize<6> for Erc20Note {
fn serialize(self) -> [Field; 6] {
impl crate::Serialize<4> for Erc20Note {
fn serialize(self) -> [Field; 4] {
self
.owner
.serialize()
.concat(self.amount.token.serialize())
.concat(self.amount.amount.limbs.map(|x| x.into()))
.concat([self.amount.amount.to_integer()])
.concat([self.randomness])
}
}
Expand Down
11 changes: 9 additions & 2 deletions packages/contracts/noir/common/src/lib.nr
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use bignum::BigNum;
use protocol_types::hash::poseidon2_hash_with_separator;

mod context;
mod uint253;
mod erc20_note;
pub(crate) mod note;
mod owned_note;
Expand Down Expand Up @@ -46,7 +46,7 @@ pub global GENERATOR_INDEX__NOTE_HASH: Field = 3;
// Note: keep in sync with other languages
pub global U256_LIMBS: u32 = 3;

pub type U256 = bignum::U256;
pub type U256 = uint253::U253;

/// User address within the rollup
#[derive(Eq, Serialize)]
Expand Down Expand Up @@ -96,6 +96,13 @@ impl std::ops::Sub for TokenAmount {
}
}

impl std::cmp::Ord for TokenAmount {
fn cmp(self, other: Self) -> std::cmp::Ordering {
self._check(other);
self.amount.cmp(other.amount)
}
}

pub struct TreeRoots {
pub note_hash_root: Field,
}
Loading