feat(contracts): addLiquidity circuit v1 (Unpermissioned Shielded)#104
feat(contracts): addLiquidity circuit v1 (Unpermissioned Shielded)#104
addLiquidity circuit v1 (Unpermissioned Shielded)#104Conversation
addLiquidity circuit v2 UTXOaddLiquidity circuit v1 UTXO
addLiquidity circuit v1 UTXOaddLiquidity circuit v1 (Unpermissioned Shielded - UTXO)
c70ee3b to
def6798
Compare
def6798 to
6cf7376
Compare
addLiquidity circuit v1 (Unpermissioned Shielded - UTXO)addLiquidity circuit v1 (Unpermissioned Shielded)
|
@andrew-fleming this PR is ready to be reviewed, but I see that the pipeline is failing, I am thinking first to take the chance and update the whole gh workflows with the one that is in compact-contracts repo. WDYT? |
andrew-fleming
left a comment
There was a problem hiding this comment.
Looking good, @0xisk! This is a partial review so you have some feedback :)
| @@ -0,0 +1,44 @@ | |||
| { | |||
| "name": "@midnight-dapps/lunarswap-v2", | |||
There was a problem hiding this comment.
The directory name is still lunarswap-v1
| import type { CoinInfo } from "@midnight-dapps/compact-std"; | ||
| import { | ||
| SLIPPAGE_TOLERANCE, | ||
| calculateAddLiquidityAmounts, |
There was a problem hiding this comment.
| calculateAddLiquidityAmounts, | |
| calculateLiquidityAmounts, |
| calculateAddLiquidityAmounts, | ||
| calculateRemoveLiquidityMinimums, | ||
| } from "@midnight-dapps/lunarswap-sdk"; |
There was a problem hiding this comment.
| calculateAddLiquidityAmounts, | |
| calculateRemoveLiquidityMinimums, | |
| } from "@midnight-dapps/lunarswap-sdk"; | |
| calculateLiquidityAmounts, | |
| } from "@midnight-dapps/lunarswap-sdk"; |
The other isn't exported from the sdk index and afaict it isn't being used here
| } | ||
|
|
||
| /** | ||
| * @title getPairIdentity circuit |
There was a problem hiding this comment.
Nit: Any reason not to shorten the name to getPairId?
| * @title isIdentityExists circuit | ||
| * @description Checks if a trading pair exists for the given identity hash. |
There was a problem hiding this comment.
Similar to my other question: why not just idExists or isPair?
| environment: "node", | ||
| globals: true, | ||
| include: ["**/*.test.ts"], | ||
| hookTimeout: 100000, | ||
| coverage: { | ||
| provider: "v8", | ||
| reporter: ["text", "json", "html", "lcov"], | ||
| exclude: [ | ||
| "node_modules/", | ||
| "**/*.d.ts", | ||
| "**/*.test.ts", | ||
| "**/*.spec.ts", | ||
| "**/coverage/**", | ||
| "**/dist/**", | ||
| "**/build/**", | ||
| "**/.next/**", | ||
| "**/vitest.config.*", | ||
| "**/tsconfig.*", | ||
| "**/package.json", | ||
| "**/package-lock.json", | ||
| "**/pnpm-lock.yaml", | ||
| "**/yarn.lock", | ||
| ], | ||
| all: true, | ||
| clean: true, | ||
| cleanOnRerun: true, | ||
| reportsDirectory: "./coverage", | ||
| thresholds: { | ||
| global: { |
There was a problem hiding this comment.
Okay, my brain is now trying to decipher the shapes that this formatting is making. This is definitely a seahorse!
| "compact": "pnpm exec compact-compiler", | ||
| "compact:fast": "pnpm exec compact-compiler --skip-zk", | ||
| "build": "pnpm exec compact-builder && tsc", | ||
| "build": "rm -rf dist && pnpm exec compact-builder && tsc", |
There was a problem hiding this comment.
Hmm idk about this..it's destructive without warning, no? I'd suggest moving rm -rf to something like a clean script (what we do in the contracts lib)
There was a problem hiding this comment.
If you really want to keep it as is, we should at least document that build will destroy the dist because this is surprising behavior. This could also cause hard-to-find issues with CI and release pipelines once in place
| cmd: 'mkdir -p dist && find src -type f -name "*.compact" -exec cp {} dist/ \\; 2>/dev/null && rm dist/Mock*.compact 2>/dev/null || true', | ||
| /** | ||
| * Shell command to copy and clean `.compact` files from `src` to `dist`. | ||
| * - Creates `dist` directory if it doesn't exist. | ||
| * - Copies `.compact` files from `src` root to `dist` root (e.g., `src/Math.compact` → `dist/Math.compact`). | ||
| * - Copies `.compact` files from `src` subdirectories to `dist` with preserved structure (e.g., `src/interfaces/IMath.compact` → `dist/interfaces/IMath.compact`). | ||
| * - Excludes files in `src/test` and `src/src` directories. | ||
| * - Removes `Mock*.compact` files from `dist`. | ||
| * - Redirects errors to `/dev/null` and ensures the command succeeds with `|| true`. | ||
| */ | ||
| cmd: [ | ||
| 'mkdir -p dist && \\', // Create dist directory if it doesn't exist | ||
| 'find src -maxdepth 1 -type f -name "*.compact" -exec cp {} dist/ \\; && \\', // Copy .compact files from src root to dist root | ||
| 'find src -type f -name "*.compact" \\', // Find .compact files in src subdirectories | ||
| ' -not -path "src/test/*" \\', // Exclude src/test directory | ||
| ' -not -path "src/src/*" \\', // Exclude src/src directory | ||
| ' -path "src/*/*" \\', // Only include files in subdirectories | ||
| ' -exec sh -c \\', // Execute a shell command for each file |
There was a problem hiding this comment.
Are these changes necessary in this PR?
There was a problem hiding this comment.
Can we not use the simple empty object that we use elsewhere?
| @@ -0,0 +1,471 @@ | |||
| // SPDX-License-Identifier: MIT | |||
|
|
|||
| pragma language_version >= 0.14.0; | |||
There was a problem hiding this comment.
| pragma language_version >= 0.14.0; | |
| pragma language_version >= 0.15.0; |
There was a problem hiding this comment.
Sorry for the delay @0xisk. Nice work on getting this iteration done! Obviously, this is very big and quite complex in how all of the modules and circuits are supposed to interact. I left some comments and questions
Also, we should get the CI working and properly enforce formatting
| amount0: Uint<128>, | ||
| amount1: Uint<128> | ||
| ): [Pair, Uint<128>] { | ||
| // We use addChecked here because the max amount in CoinInfo is Uint<128> |
There was a problem hiding this comment.
Good call adding the comment. CoinInfo.value is Uint<64> though, no?
| // TODO: review the risk of using totalSupply here. Because of the nature of UTXO that | ||
| // a user could send the LP tokens to the burn address directly without using the burn function, | ||
| // but the totalSupply will not be updated. |
| ), liquidityTotal]; | ||
| } else { | ||
| // TODO: In case of using Uint<128> for reserves that results a conflict with | ||
| // the ERC20 Compact standard, since the result here is Uint<256> while the max min tokens are Uint<128> |
There was a problem hiding this comment.
Nit but important: there's currently no ERC20 in midnight land. The FungibleToken Compact standard, instead?
There was a problem hiding this comment.
Same with the other ERC20 refs below if you agree
| disclose(Uint128_div(Uint128_mulChecked(amount0, totalSupply), reserve0)), | ||
| disclose(Uint128_div(Uint128_mulChecked(amount1, totalSupply), reserve1)) | ||
| ); | ||
| // TODO: that is TBD later in the future after doing benchmarks on the performance of MathU256 |
There was a problem hiding this comment.
What's TBD? It's not clear what this is referring to
| const balance0 = Uint128_addChecked(reserve0, amount0); | ||
| const balance1 = Uint128_addChecked(reserve1, amount1); | ||
|
|
||
| const [isFeeOn, kLast] = _mintFee(identity, reserve0, reserve1, pair.kLast); |
There was a problem hiding this comment.
Is this always called and structured this way because of immutable vars?
| expect(lunarswap.getLiquidityTotalSupply(usdcCoin, dustCoin).value).toBe( | ||
| 14142n, | ||
| ); |
There was a problem hiding this comment.
I mentioned it in the other test file, but again, idk how 14142 is calculated and why it's correct here
| expect(identity).toBeDefined(); | ||
| expect(identity.length).toBe(32); |
There was a problem hiding this comment.
This isn't confirming that the correct pair hash is calculated though. It's just confirming that it's defined and has a len of 32. I think it's worth creating a helper to confirm the ids
| it("should generate same hash regardless of token order", () => { | ||
| const usdcCoin = usdc.mint(createEitherFromHex(LP_USER), 100n); | ||
| const nightCoin = night.mint(createEitherFromHex(LP_USER), 100n); | ||
| const hash1 = lunarswap.getPairIdentity(usdcCoin, nightCoin); | ||
| const hash2 = lunarswap.getPairIdentity(usdcCoin, nightCoin); | ||
| expect(hash1).toEqual(hash2); |
There was a problem hiding this comment.
The pairs in hash1 and hash2 are in the same order, no?
| const identity = lunarswap.getPairIdentity(usdcCoin, nightCoin); | ||
| expect(lunarswap.getLiquidityTotalSupply(usdcCoin, nightCoin).value).toBe( | ||
| 7071n, |
There was a problem hiding this comment.
identity isn't used to get the correct order
There was a problem hiding this comment.
In the PR comment under testing, it says:
- Added addLiquidity.test.ts: Tests liquidity addition for new and existing pairs, verifying optimal amounts, slippage protection, and reserve updates.
- Added Lunarswap.test.ts and LunarswapSimulator.ts: Validates addLiquidity integration with pair management and token minting.
Correct me if I'm wrong, but the setup looks pretty much the same for both. Why not combine them into a single test file?
|
From the PR description:
Just curious, how is this blocked by the compiler output fix? |
Description
Overview
Implements Lunarswap V1, a decentralized exchange protocol on the Midnight Network using Compact, with a focus on privacy-preserving features. Introduces the
addLiquiditycircuit for adding liquidity to trading pairs and minting LP tokens.Blocked by #148.
Resolves
N/A
Key Changes
addLiquidityCircuit: Enables users to add liquidity to trading pairs inLunarswap.compactandLunarswapRouter.compact. Optimizes token amounts to maintain price ratios, mints LP tokens, and returns excess tokens to users. Supports both new and existing pairs with slippage protection.LunarswapFactory.compact: Manages pair creation and storage foraddLiquidityoperations.LunarswapFee.compact: Handles fee collection for liquidity provision.LunarswapLiquidity.compact: Manages LP token minting and tracking foraddLiquidity.LunarswapPair.compact: Updates pair reserves and tracks statistics during liquidity addition.lunarswap-sdk) for liquidity calculations.Testing
addLiquidity.test.ts: Tests liquidity addition for new and existing pairs, verifying optimal amounts, slippage protection, and reserve updates.Lunarswap.test.tsandLunarswapSimulator.ts: ValidatesaddLiquidityintegration with pair management and token minting.Notes