Skip to content

Add BatcherConfidential#293

Merged
arr00 merged 72 commits intomasterfrom
feat/batcher
Mar 5, 2026
Merged

Add BatcherConfidential#293
arr00 merged 72 commits intomasterfrom
feat/batcher

Conversation

@arr00
Copy link
Copy Markdown
Member

@arr00 arr00 commented Jan 22, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Added confidential batch token swapping with deposit and claim functionality
    • Implemented batch lifecycle management including join, claim proportional returns, and cancel operations
    • Introduced mock utilities for integration testing
  • Documentation

    • Updated package documentation with new utility references

✏️ Tip: You can customize this high-level summary in your review settings.

@netlify
Copy link
Copy Markdown

netlify bot commented Jan 22, 2026

Deploy Preview for confidential-tokens ready!

Name Link
🔨 Latest commit 9be290f
🔍 Latest deploy log https://app.netlify.com/projects/confidential-tokens/deploys/69a8e75d6f5ce700083a33f5
😎 Deploy Preview https://deploy-preview-293--confidential-tokens.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 22, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: eb54987a-3c6e-4b75-9f10-3ccb0f1c0f0a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This PR introduces a confidential token batching system (BatcherConfidential) enabling batch deposit management, swaps, and claims using encrypted balances and confidential transfers. Supporting mock contracts for testing and comprehensive test coverage are included.

Changes

Cohort / File(s) Summary
Confidential Batching Core
contracts/utils/BatcherConfidential.sol
New abstract contract orchestrating confidential token batches with state management for deposits, unwrap amounts, and exchange rates. Implements join/claim/cancel/dispatch flows with virtual extension points for route execution and exchange rate setting. Includes batch finalization callbacks and FHE-integrated balance tracking.
Mock Exchange Implementation
contracts/mocks/finance/ExchangeMock.sol
New mock exchange contract supporting bidirectional token swaps (swapAToB, swapBToA) with configurable exchange rates and event emission for rate changes.
Mock Batcher Integration
contracts/mocks/utils/BatcherConfidentialSwapMock.sol
New concrete mock extending BatcherConfidential, delegating joins and implementing route execution via ExchangeMock with token approvals, unwrapping, and swapping.
Test Suite
test/utils/BatcherConfidential.test.ts
Comprehensive TypeScript test suite covering deposit routing, join/claim/cancel flows, batch dispatching, unwrap finalization, and end-to-end scenarios with encrypted balance verification.
Documentation
contracts/utils/README.adoc
Documentation update referencing new BatcherConfidential contract in utilities listing.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant BC as BatcherConfidential
    participant Wrapper as ERC7984<br/>Wrapper
    participant Exchange as ExchangeMock
    participant Route as Route<br/>Execution

    User->>BC: join(encryptedAmount)
    BC->>Wrapper: confidentialTransfer(fromToken)
    Wrapper-->>BC: ✓ deposited
    BC->>BC: update deposits & totalDeposits

    User->>BC: dispatchBatch()
    BC->>BC: compute unwrapAmount
    BC->>Wrapper: unwrap(fromToken)
    Wrapper-->>BC: ✓ unwrap initiated

    BC->>BC: dispatchBatchCallback()
    BC->>Route: _executeRoute()
    Route->>Exchange: swapAToB()
    Exchange-->>Route: swapped toToken
    Route->>Wrapper: wrap(toToken)
    Wrapper-->>Route: ✓ wrapped
    Route->>BC: setExchangeRate()

    User->>BC: claim(batchId)
    BC->>BC: compute share (deposit × exchangeRate)
    BC->>Wrapper: transfer(toToken)
    Wrapper-->>User: ✓ claimed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • james-toussaint

Poem

🐰 A batching burrow now takes shape,
With confidential deposits to drape,
Through encrypted swaps and claims so bright,
BatcherConfidential hops into sight! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add BatcherConfidential' directly and clearly describes the main change: introducing a new BatcherConfidential contract and related mock utilities.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/batcher

Comment @coderabbitai help to get the list of available commands and usage tips.

@arr00 arr00 changed the title Add confidential batcher Add BatcherConfidential Jan 22, 2026
@arr00 arr00 linked an issue Jan 23, 2026 that may be closed by this pull request
@arr00 arr00 marked this pull request as ready for review January 24, 2026 15:35
@arr00 arr00 requested a review from a team as a code owner January 24, 2026 15:35
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@contracts/utils/BatcherConfidential.sol`:
- Around line 64-65: The call to toToken().confidentialTransfer(msg.sender,
amountToSend) must capture and handle its return value instead of ignoring it:
call confidentialTransfer and store its returned euint64 (e.g., sent), then if
sent < amountToSend adjust the stored deposit (restore remaining deposit =
previous deposit - sent) or revert the operation; follow the pattern used in
cancel() where euint64 sent = fromToken().confidentialTransfer(...) is used to
recalculate deposit so users aren't left without tokens or deposit when the
transfer fails.
🧹 Nitpick comments (5)
contracts/mocks/finance/ExchangeMock.sol (1)

26-31: Division by zero risk if exchangeRate is zero.

If exchangeRate is set to 0, swapBToA will revert with a division by zero error. While this is a mock contract, consider adding a guard or documenting this precondition.

💡 Suggested guard
 function swapBToA(uint256 amount) public returns (uint256) {
+    require(exchangeRate != 0, "Exchange rate cannot be zero");
     uint256 amountOut = (amount * 1e18) / exchangeRate;
     require(tokenB.transferFrom(msg.sender, address(this), amount), "Transfer of token B failed");
     require(tokenA.transfer(msg.sender, amountOut), "Transfer of token A failed");
     return amountOut;
 }
contracts/utils/BatcherConfidential.sol (2)

1-7: Non-standard pragma placement.

The pragma solidity directive is placed after the import statements. While valid Solidity, convention is to place pragma before imports for better readability and consistency.

♻️ Standard ordering
 // SPDX-License-Identifier: MIT
+pragma solidity ^0.8.27;

 import {FHE, externalEuint64, euint64, ebool, euint128} from "@fhevm/solidity/lib/FHE.sol";
 import {ERC7984ERC20Wrapper} from "../token/ERC7984/extensions/ERC7984ERC20Wrapper.sol";
 import {FHESafeMath} from "./../utils/FHESafeMath.sol";
-
-pragma solidity ^0.8.27;

198-201: Guard against zero exchange rate could be strengthened.

The _setExchangeRate function only checks if the exchange rate was already set but doesn't prevent setting a zero exchange rate, which would cause issues in the claim function's require check on line 52 (making the batch appear unfinalized).

💡 Suggested enhancement
 function _setExchangeRate(uint256 batchId, uint64 exchangeRate_) internal virtual {
     require(exchangeRate(batchId) == 0, ExchangeRateAlreadySet(batchId));
+    require(exchangeRate_ != 0, "Exchange rate cannot be zero");
     _batches[batchId].exchangeRate = exchangeRate_;
 }
contracts/mocks/utils/BatcherConfidentialSwapMock.sol (1)

51-68: Potential division by zero and overflow risks in _executeRoute.

  1. Line 66: If unwrapAmount is 0, division will revert. While this may be prevented by the flow (non-zero deposits required), it's worth documenting.

  2. Line 67: The cast uint64(exchangeRate) can silently truncate if exchangeRate exceeds type(uint64).max. This could occur with extreme rate ratios.

Since this is a mock for testing, these may be acceptable, but consider adding guards or using SafeCast for production-like mocks.

💡 Safer implementation
     // Set the exchange rate for the batch based on swapped amount
     uint256 exchangeRate = (amountOut * 1e18) / unwrapAmount;
-    _setExchangeRate(batchId, uint64(exchangeRate));
+    require(exchangeRate <= type(uint64).max, "Exchange rate overflow");
+    _setExchangeRate(batchId, uint64(exchangeRate));
test/utils/BatcherConfidential.test.ts (1)

140-148: Inconsistent assertion format.

Line 147 uses 0 (number) while other decrypt assertions use string format like '1000' or '3000'. This inconsistency could cause test failures depending on how userDecryptEuint returns values.

♻️ Consistent string format
       ).to.eventually.eq(0);
+      // Consider using string format for consistency:
+      // ).to.eventually.eq('0');

Comment on lines +104 to +105
SafeERC20.forceApprove(IERC20(fromToken().underlying()), address(fromToken()), type(uint256).max);
SafeERC20.forceApprove(IERC20(toToken().underlying()), address(toToken()), type(uint256).max);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we add a comment somewhere telling what we need this to call wrap in ERC7984ERC20Wrapper (required for when the underlying ERC20 is not ERC1363

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The assumption is that most ERC-20 tokens are not 1363 so for compatibility, we use the approve and wrap flow instead.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting a comment to state that?

@arr00 arr00 requested a review from james-toussaint March 4, 2026 16:46
@arr00 arr00 dismissed Amxx’s stale review March 5, 2026 15:10

Changes done in #309

@arr00 arr00 merged commit a89eee6 into master Mar 5, 2026
16 checks passed
@arr00 arr00 deleted the feat/batcher branch March 5, 2026 15:10
@github-actions github-actions bot mentioned this pull request Mar 5, 2026
@arr00 arr00 restored the feat/batcher branch March 18, 2026 14:42
@github-actions github-actions bot mentioned this pull request Mar 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BatcherConfidential

5 participants