Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions packages/contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"ethers": "^6.13.4",
"ky": "^1.7.2",
"lodash-es": "^4.17.21",
"smol-toml": "^1.3.1",
"ts-essentials": "^9.4.1",
"zod": "^3.23.8"
}
Expand Down
111 changes: 105 additions & 6 deletions packages/contracts/sdk/LobService.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { type AsyncOrSync } from "ts-essentials";
import { uniq } from "lodash";
import { assert, type AsyncOrSync } from "ts-essentials";
import { type PoolERC20 } from "../typechain-types";
import { NoteInputStruct } from "../typechain-types/contracts/PoolERC20";
import { MpcProverService, type Side } from "./mpc/MpcNetworkService.js";
import { splitInput } from "./mpc/utils.js";
import { type ITreesService } from "./RemoteTreesService.js";
import {
CompleteWaAddress,
Erc20Note,
getRandomness,
TokenAmount,
type NoirAndBackend,
type PoolErc20Service,
Expand All @@ -16,6 +20,7 @@ export class LobService {
private contract: PoolERC20,
private trees: ITreesService,
private poolErc20: PoolErc20Service,
private mpcProver: MpcProverService,
private circuits: AsyncOrSync<{
swap: NoirAndBackend;
}>,
Expand All @@ -29,11 +34,9 @@ export class LobService {
buyerNote: Erc20Note;
buyerAmount: TokenAmount;
}) {
const { Fr } = await import("@aztec/aztec.js");

const swapCircuit = (await this.circuits).swap;
const sellerRandomness = Fr.random().toString();
const buyerRandomness = Fr.random().toString();
const sellerRandomness = await getRandomness();
const buyerRandomness = await getRandomness();

const sellerChangeNote = await Erc20Note.from({
owner: await CompleteWaAddress.fromSecretKey(params.sellerSecretKey),
Expand Down Expand Up @@ -76,7 +79,6 @@ export class LobService {
),
seller_order,
seller_randomness: sellerRandomness,

buyer_secret_key: params.buyerSecretKey,
buyer_note: await this.poolErc20.toNoteConsumptionInputs(
params.buyerSecretKey,
Expand Down Expand Up @@ -109,4 +111,101 @@ export class LobService {
const receipt = await tx.wait();
console.log("swap gas used", receipt?.gasUsed);
}

async requestSwap(params: {
secretKey: string;
note: Erc20Note;
sellAmount: TokenAmount;
buyAmount: TokenAmount;
}) {
const swapCircuit = (await this.circuits).swap;
const randomness = await getRandomness();

const changeNote = await Erc20Note.from({
owner: await CompleteWaAddress.fromSecretKey(params.secretKey),
amount: params.note.amount.sub(params.sellAmount),
randomness,
});
const swapNote = await Erc20Note.from({
owner: await CompleteWaAddress.fromSecretKey(params.secretKey),
amount: params.buyAmount,
randomness,
});

const order = {
sell_amount: await params.sellAmount.toNoir(),
buy_amount: await params.buyAmount.toNoir(),
randomness,
};

// deterministic side
const side: Side =
params.sellAmount.token.toLowerCase() <
params.buyAmount.token.toLowerCase()
? "seller"
: "buyer";
const input = {
[`${side}_secret_key`]: params.secretKey,
[`${side}_note`]: await this.poolErc20.toNoteConsumptionInputs(
params.secretKey,
params.note,
),
[`${side}_order`]: order,
[`${side}_randomness`]: randomness,
};
console.log("side", side, randomness);
// only one trading party need to provide public inputs
const inputPublic =
side === "seller"
? {
tree_roots: await this.trees.getTreeRoots(),
}
: undefined;
const inputsShared = await splitInput(swapCircuit.circuit, {
// merge public inputs into first input because it does not matter how public inputs are passed
...input,
...inputPublic,
});
const orderId = randomness; // TODO: is randomness a good order id?
const proofs = await this.mpcProver.prove(inputsShared, {
orderId,
side,
circuit: swapCircuit.circuit,
numPublicInputs: 8,
});
assert(uniq(proofs).length === 1, "proofs mismatch");
const proof = proofs[0]!;
return {
proof,
side,
changeNote: await changeNote.toSolidityNoteInput(),
swapNote: await swapNote.toSolidityNoteInput(),
nullifier: (
await params.note.computeNullifier(params.secretKey)
).toString(),
};
}

async commitSwap(sellerSwap: SwapResult, buyerSwap: SwapResult) {
assert(
sellerSwap.proof === buyerSwap.proof,
"seller & buyer proof mismatch",
);
const proof = sellerSwap.proof;

const tx = await this.contract.swap(
proof,
[
sellerSwap.changeNote,
buyerSwap.swapNote,
buyerSwap.changeNote,
sellerSwap.swapNote,
],
[sellerSwap.nullifier, buyerSwap.nullifier],
);
const receipt = await tx.wait();
console.log("swap gas used", receipt?.gasUsed);
}
}

type SwapResult = Awaited<ReturnType<LobService["requestSwap"]>>;
24 changes: 12 additions & 12 deletions packages/contracts/sdk/RollupService.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Fr } from "@aztec/aztec.js";
import type { UltraHonkBackend } from "@aztec/bb.js";
import type { Noir } from "@noir-lang/noir_js";
import type { CompiledCircuit, Noir } from "@noir-lang/noir_js";
import { utils } from "@repo/utils";
import { ethers } from "ethers";
import { compact, orderBy, times } from "lodash-es";
Expand Down Expand Up @@ -69,8 +69,7 @@ export class PoolErc20Service {
amount: bigint;
secretKey: string;
}) {
const { Fr } = await import("@aztec/aztec.js");
const randomness = Fr.random().toString();
const randomness = await getRandomness();
const note = await Erc20Note.from({
owner: await CompleteWaAddress.fromSecretKey(secretKey),
amount: await TokenAmount.from({
Expand Down Expand Up @@ -110,10 +109,8 @@ export class PoolErc20Service {
to: string;
amount: bigint;
}) {
const { Fr } = await import("@aztec/aztec.js");

assert(utils.isAddressEqual(token, fromNote.amount.token), "invalid token");
const change_randomness = Fr.random().toString();
const change_randomness = await getRandomness();
const changeNote = await Erc20Note.from({
owner: fromNote.owner,
amount: await TokenAmount.from({
Expand Down Expand Up @@ -166,10 +163,9 @@ export class PoolErc20Service {
notes: Erc20Note[];
to?: WaAddress;
}) {
const { Fr } = await import("@aztec/aztec.js");
assert(notes.length === MAX_NOTES_TO_JOIN, "invalid notes length");

const join_randomness = Fr.random().toString();
const join_randomness = await getRandomness();

to ??= (
await CompleteWaAddress.fromSecretKey(secretKey)
Expand Down Expand Up @@ -220,12 +216,10 @@ export class PoolErc20Service {
to: CompleteWaAddress;
amount: TokenAmount;
}) {
const { Fr } = await import("@aztec/aztec.js");

const nullifier = await fromNote.computeNullifier(secretKey);

const to_randomness = Fr.random().toString();
const change_randomness = Fr.random().toString();
const to_randomness = await getRandomness();
const change_randomness = await getRandomness();
const input = {
tree_roots: await this.trees.getTreeRoots(),
from_note_inputs: await this.toNoteConsumptionInputs(secretKey, fromNote),
Expand Down Expand Up @@ -538,6 +532,7 @@ export class CompleteWaAddress {
}

export type NoirAndBackend = {
circuit: CompiledCircuit;
noir: Noir;
backend: UltraHonkBackend;
};
Expand All @@ -560,3 +555,8 @@ function sortEvents<
(e) => `${e.blockNumber}-${e.transactionIndex}-${e.index}`,
);
}

export async function getRandomness() {
const { Fr } = await import("@aztec/aztec.js");
return Fr.random().toString();
}
12 changes: 6 additions & 6 deletions packages/contracts/sdk/backendSdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ export function createBackendSdk(
rollup: utils.iife(async () => {
const { Noir } = await import("@noir-lang/noir_js");
const { UltraHonkBackend } = await import("@aztec/bb.js");
const noir = new Noir(await compiledCircuits.rollup);
const circuit = await compiledCircuits.rollup;
const noir = new Noir(circuit);
// TODO(perf): write and use a NativeUltraHonkBackend
// const backend = new NativeUltraPlonkBackend(
// `${process.env.HOME}/.bb/bb`,
// await compiledCircuits.rollup,
// ) as unknown as UltraPlonkBackend;
const backend = new UltraHonkBackend(
(await compiledCircuits.rollup).bytecode,
{ threads: os.cpus().length },
);
return { noir, backend };
const backend = new UltraHonkBackend(circuit.bytecode, {
threads: os.cpus().length,
});
return { circuit, noir, backend };
}),
});
return {
Expand Down
2 changes: 2 additions & 0 deletions packages/contracts/sdk/mpc/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
work-dirs
configs
Loading