Skip to content

Conversation

@kumawatkaran523
Copy link
Contributor

@kumawatkaran523 kumawatkaran523 commented Jun 9, 2025

Summary by CodeRabbit

  • New Features

    • Added example environment configuration files for both backend and frontend to guide setup for blockchain connections.
    • Included deployment instructions for Ethereum Classic using Foundry in the documentation.
    • Integrated Lit Protocol encryption and decryption for secure handling of invoice data in the frontend components.
  • Chores

    • Integrated external dependencies via new Git submodules for blockchain contract libraries.
    • Updated .gitignore to exclude sensitive frontend environment files.
    • Added new Lit Protocol packages to frontend dependencies.
  • Refactor

    • Updated the contract ABI to reflect changes in function names, parameters, and removed obsolete methods.
    • Streamlined the main smart contract to handle encrypted invoice data only, removing detailed invoice fields and item data.
    • Refactored frontend invoice components to support encrypted invoice data and access-controlled decryption with Lit Protocol.
  • Bug Fixes

    • Removed unused frontend environment and mock contract files to clean up the project.
  • Documentation

    • Enhanced the README with detailed deployment steps for Ethereum Classic.

@coderabbitai
Copy link

coderabbitai bot commented Jun 9, 2025

Walkthrough

This update introduces two new Git submodules for external contract dependencies, adds example environment configuration files for both backend and frontend, and modifies the documentation to include Ethereum Classic deployment instructions. It removes a Solidity mock contract, refactors the Chainvoice smart contract and its ABI, updates environment file handling and import statements, and adjusts frontend environment management. The frontend components are enhanced to encrypt and decrypt invoice data using Lit Protocol, integrating access control and secure data handling.

Changes

File(s) Change Summary
.gitmodules, contracts/lib/forge-std, contracts/lib/chainlink-brownie-contracts Added two new Git submodules for Foundry standard library and Chainlink Brownie contracts.
contracts/.env.example Added example environment variable file for contract deployment configuration including Ethereum Sepolia and Ethereum Classic settings.
frontend/.env.example, frontend/.env, frontend/.gitignore Added frontend environment example file, deleted actual .env, and updated .gitignore to exclude .env from version control.
README.md Added a section with step-by-step instructions for deploying contracts to Ethereum Classic using Foundry.
contracts/src/MockV3Aggregator.sol Deleted the MockV3Aggregator Solidity contract, which provided a mock Chainlink price feed for testing.
contracts/src/Chainvoice.sol Refactored Chainvoice contract to handle encrypted invoice data only, removed detailed user/item structs, updated invoice creation and payment logic, renamed and standardized modifiers and functions, and added fee accumulation and withdrawal logic.
frontend/src/contractsABI/ChainvoiceABI.js Modified contract ABI: removed constructor input, renamed and removed functions, changed function parameters and names, and updated interface to match contract changes in Chainvoice.sol.
frontend/src/App.jsx Removed import statement for App.css.
frontend/package.json Added four new Lit Protocol dependencies for authentication, encryption, and node client functionality.
frontend/src/components/CreateInvoice.jsx Enhanced to encrypt invoice data using Lit Protocol before submitting to the contract; added dynamic invoice item management and updated form submission logic.
frontend/src/page/ReceivedInvoice.jsx Updated to decrypt encrypted invoice data using Lit Protocol with access control based on sender and receiver addresses; adjusted payment logic and UI rendering accordingly.
frontend/src/page/SentInvoice.jsx Updated to decrypt encrypted invoice data using Lit Protocol; adjusted UI rendering and state management to handle decrypted invoice details.
frontend/vite.config.js Changed import statement quotes for vite-plugin-node-polyfills from double to single quotes; no functional change.

Sequence Diagram(s)

sequenceDiagram
    participant User as User (Frontend)
    participant Lit as Lit Protocol
    participant Contract as Chainvoice Contract
    participant Blockchain as Ethereum Network

    User->>Lit: Connect and authenticate (SIWE)
    User->>Lit: Encrypt invoice data with access control
    Lit-->>User: Return encrypted data and encrypted hash
    User->>Contract: Call createInvoice(encryptedData, encryptedHash)
    Contract->>Blockchain: Store encrypted invoice on-chain

    User->>Contract: Request received/sent invoices (encrypted)
    Contract-->>User: Returns encrypted invoice data
    User->>Lit: Decrypt invoice data with access control
    Lit-->>User: Returns decrypted invoice details
    User->>UI: Display decrypted invoice details
Loading

Poem

🐇 In code’s green meadow, new submodules sprout,
Encrypting our invoices, no secrets about.
The mock’s taken leave, replaced by the light,
Of Lit Protocol’s magic, keeping data tight.
From Sepolia to Classic, the contracts now dance,
A rabbit’s quick hop in blockchain’s expanse!
✨📜🔐

✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 2

🧹 Nitpick comments (7)
contracts/.env.example (1)

1-3: Remind users to secure their private key
The placeholder PRIVATE_KEY=your_private_key_here is fine, but consider adding a comment or README note that .env must never be committed and private keys should be stored securely.

frontend/src/App.jsx (1)

16-16: Remove unused CSS import
The removal of import './App.css'; is appropriate given the Tailwind setup. Consider deleting the now-unused App.css file to keep the codebase clean.

frontend/.env.example (1)

1-1: Clarify the purpose of the "env-copy" comment.

The comment # env-copy at the top is unclear. Consider replacing it with a more descriptive comment explaining the file's purpose.

-# env-copy
+# Frontend Environment Variables Template
+# Copy this file to .env and update the values for your deployment
README.md (3)

59-59: Add missing comma before abbreviation.

Grammar improvement needed before "i.e.".

-- Create a .env in your project root i.e. `contracts/`
+- Create a .env in your project root, i.e. `contracts/`
🧰 Tools
🪛 LanguageTool

[typographical] ~59-~59: Consider putting a comma before the abbreviation “i.e.”.
Context: ...ts - Create a .env in your project root i.e. contracts/ - Copy all the varible...

(IE_COMMA)


79-79: Add missing article and improve grammar.

Two grammar improvements needed for better readability.

-5. Finally add Contract Address to Frontend `.env`
+5. Finally add the Contract Address to the Frontend `.env`
🧰 Tools
🪛 LanguageTool

[uncategorized] ~79-~79: Possible missing comma found.
Context: ...y $PRIVATE_KEY \ --broadcast \ ``` 5. Finally add Contract Address to Frontend .env...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~79-~79: You might be missing the article “the” here.
Context: ...EY \ --broadcast \ ``` 5. Finally add Contract Address to Frontend .env - Creat...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


90-90: Add comma for compound sentence clarity.

Grammar improvement for better sentence flow.

-- Restart your frontend development server so the new environment variables are loaded.
+- Restart your frontend development server, so the new environment variables are loaded.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~90-~90: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...Restart your frontend development server so the new environment variables are loade...

(COMMA_COMPOUND_SENTENCE_2)

frontend/src/contractsABI/ChainvoiceABI.js (1)

770-770: Potential typo in parameter name newTreauserAdd.

The parameter name newTreauserAdd appears to be a typo. It should likely be newTreasuryAdd or newTreasuryAddress for clarity and consistency.

Consider updating the ABI if this represents an actual contract interface issue, or verify that this parameter name is intentional.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 188df46 and e7c3f41.

📒 Files selected for processing (10)
  • .gitmodules (1 hunks)
  • README.md (1 hunks)
  • contracts/.env.example (1 hunks)
  • contracts/lib/forge-std (1 hunks)
  • contracts/src/MockV3Aggregator.sol (0 hunks)
  • frontend/.env (0 hunks)
  • frontend/.env.example (1 hunks)
  • frontend/.gitignore (2 hunks)
  • frontend/src/App.jsx (1 hunks)
  • frontend/src/contractsABI/ChainvoiceABI.js (6 hunks)
💤 Files with no reviewable changes (2)
  • frontend/.env
  • contracts/src/MockV3Aggregator.sol
🧰 Additional context used
🪛 LanguageTool
README.md

[typographical] ~59-~59: Consider putting a comma before the abbreviation “i.e.”.
Context: ...ts - Create a .env in your project root i.e. contracts/ - Copy all the varible...

(IE_COMMA)


[uncategorized] ~79-~79: Possible missing comma found.
Context: ...y $PRIVATE_KEY \ --broadcast \ ``` 5. Finally add Contract Address to Frontend .env...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~79-~79: You might be missing the article “the” here.
Context: ...EY \ --broadcast \ ``` 5. Finally add Contract Address to Frontend .env - Creat...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~90-~90: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...Restart your frontend development server so the new environment variables are loade...

(COMMA_COMPOUND_SENTENCE_2)

🪛 markdownlint-cli2 (0.17.2)
README.md

73-73: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🔇 Additional comments (12)
frontend/.gitignore (2)

14-14: Ignore base .env file
Adding .env ensures local environment variables won’t be accidentally committed, which is critical for protecting sensitive data.


26-26: Add Vim swap file patterns
The wildcard *.sw? correctly ignores Vim swap files (e.g., .swp, .swo).

contracts/.env.example (2)

4-8: Sepolia network placeholders look correct
The RPC URL, chain ID 11155111, and explorer URL follow a consistent naming convention and align with Sepolia testnet settings.


9-12: Ethereum Classic network placeholders look correct
Chain ID 61 and the RPC URL placeholder match ETC mainnet configuration.

contracts/lib/forge-std (1)

1-1: Verify submodule configuration in .gitmodules.

This gitlink commit pins the contracts/lib/forge-std submodule to a specific commit. Please confirm that the root .gitmodules file has been updated with the correct path and url for this submodule, and that CI workflows include git submodule update --init --recursive to ensure reproducible builds.

.gitmodules (1)

1-6: LGTM! Well-configured submodules for Foundry and Chainlink integration.

The Git submodules are properly configured to integrate official dependencies:

  • forge-std provides Foundry's standard testing library
  • chainlink-brownie-contracts offers official Chainlink contract interfaces

This setup supports the transition from mock contracts to real Chainlink dependencies as mentioned in the AI summary.

frontend/.env.example (1)

3-14: Well-structured environment template with clear examples.

The environment variables are properly configured with:

  • Appropriate VITE_ prefix for client-side exposure
  • Clear descriptive comments with network examples
  • Logical grouping of related configuration

This template provides excellent guidance for users setting up their frontend environment.

frontend/src/contractsABI/ChainvoiceABI.js (5)

4-4: Breaking change: Constructor no longer accepts parameters.

The constructor now has an empty inputs array, which is a significant change from the previous interface. Ensure that any deployment scripts and frontend initialization code are updated accordingly.


757-760: Parameter renamed in setFeeAmount function.

The parameter has been renamed from the previous name to _fee and the type changed to uint256. Verify that frontend code calling this function uses the correct parameter structure.


174-180:

Details

✅ Verification successful

Breaking change: getReceivedInvoices now requires an address parameter.

The function signature has changed from getMyReceivedInvoices() to getReceivedInvoices(address _address). This breaking change affects how the frontend retrieves received invoices.


🏁 Script executed:

#!/bin/bash
# Description: Verify frontend code has been updated for the new function signature

# Search for references to old function names
rg -i "getMyReceivedInvoices|getReceivedInvoices" frontend/

# Check for proper address parameter usage
rg -A 3 -B 3 "getReceivedInvoices" frontend/

Length of output: 1337


Confirmed: Frontend Updated for getReceivedInvoices(address)

All calls to the old getMyReceivedInvoices() have been removed, and ReceivedInvoice.jsx now correctly invokes:

const res = await contract.getReceivedInvoices(address);

No further changes are needed—this breaking change has been fully addressed.


161-161:

Details

✅ Verification successful

Function renamed from feeAmountInUSD to fee.

This is a breaking change that requires updating all frontend code that previously called feeAmountInUSD().


🏁 Script executed:

#!/bin/bash
# Description: Check if frontend code has been updated to use the new function name

# Search for any remaining references to the old function name
rg -i "feeAmountInUSD" frontend/

Length of output: 32


Function rename to fee validated

  • Searched the entire frontend (rg -i "feeAmountInUSD" frontend/) and found no remaining references.
  • ABI entry at frontend/src/contractsABI/ChainvoiceABI.js:161 correctly uses "name": "fee".

All calls have been updated—approving these changes.


341-348:

Details

✅ Verification successful

Breaking change: getSentInvoices now requires an address parameter.

Similar to getReceivedInvoices, this function has been renamed from getMySentInvoices() and now requires an address parameter. Ensure frontend code passes the appropriate address when calling this function.


🏁 Script executed:

#!/bin/bash
# Description: Verify frontend usage of getSentInvoices function

# Search for references to old and new function names
rg -i "getMySentInvoices|getSentInvoices" frontend/

# Check for proper address parameter usage
rg -A 3 -B 3 "getSentInvoices" frontend/

Length of output: 1304


getSentInvoices usage confirmed
The front-end code in SentInvoice.jsx already calls contract.getSentInvoices(address), so the address parameter is being passed as required. No further changes are needed.

• frontend/src/page/SentInvoice.jsx: const res = await contract.getSentInvoices(address);

Comment on lines 73 to 78
```
forge create src/Contract.sol:Contract \
--rpc-url $SEPOLIA_RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast \
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix code block and variable naming issues.

Two issues need addressing:

  1. Missing language specification for the code block
  2. Using SEPOLIA_RPC_URL variable in an Ethereum Classic deployment guide
-```
+```bash
 forge create src/Contract.sol:Contract \
-  --rpc-url $SEPOLIA_RPC_URL \
+  --rpc-url $ETC_RPC_URL \
   --private-key $PRIVATE_KEY \
   --broadcast \
-```
+```
🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

73-73: Fenced code blocks should have a language specified
null

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In README.md around lines 73 to 78, fix the code block by specifying the
language as bash for proper formatting and replace the variable SEPOLIA_RPC_URL
with ETC_RPC_URL to correctly reflect the Ethereum Classic deployment
environment. Also, ensure the closing code block uses triple backticks without a
language specifier.

Copy link

@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: 2

🧹 Nitpick comments (1)
contracts/src/Chainvoice.sol (1)

122-130: Setters lack events – hinder off-chain indexing & audits.

setFeeAmount and setTreasuryAddress mutate critical economic parameters yet emit no events.
Emit FeeUpdated(oldFee, newFee) and TreasuryUpdated(oldAddr, newAddr) so indexers / UIs can react to governance changes and historical state can be audited.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e7c3f41 and 77f3b14.

📒 Files selected for processing (1)
  • contracts/src/Chainvoice.sol (1 hunks)
🔇 Additional comments (1)
contracts/src/Chainvoice.sol (1)

131-140: Anyone can trigger withdrawFees – verify this is intended.

withdrawFees is publicly callable; the caller cannot redirect funds, but they can force a withdrawal at arbitrary times (e.g., front-run an owner who prefers batching to save gas).
If you want only governance control over timing, add onlyOwner; otherwise leave as-is but document the open callable nature.

Copy link

@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: 6

🧹 Nitpick comments (2)
frontend/src/page/ReceivedInvoice.jsx (1)

528-545: Add a stable React key in the items.map loop

<tbody> rendered without a key prop triggers React warnings and was flagged by static analysis.

-{drawerState.selectedInvoice?.items?.map((item, index) => (
-  <tbody>
+{drawerState.selectedInvoice?.items?.map((item, index) => (
+  <tbody key={index}>
frontend/src/components/CreateInvoice.jsx (1)

187-216: Unnecessary session sigs during encryption

encryptString does not require decryption abilities; you can drop the getSessionSigs call to save one SIWE round-trip.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 77f3b14 and 3c9220a.

📒 Files selected for processing (6)
  • frontend/package.json (1 hunks)
  • frontend/src/components/CreateInvoice.jsx (6 hunks)
  • frontend/src/contractsABI/ChainvoiceABI.js (14 hunks)
  • frontend/src/page/ReceivedInvoice.jsx (8 hunks)
  • frontend/src/page/SentInvoice.jsx (6 hunks)
  • frontend/vite.config.js (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • frontend/package.json
  • frontend/vite.config.js
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/src/page/ReceivedInvoice.jsx

[error] 529-529: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🔇 Additional comments (3)
frontend/src/page/ReceivedInvoice.jsx (1)

59-66: Missing address in useEffect dependency array

If the connected wallet address changes, invoices stay stale. Add address (and maybe fee) to the dependency list.

-}, [walletClient]);
+}, [walletClient, address]);
frontend/src/page/SentInvoice.jsx (1)

57-60: Refresh when wallet address changes

Add address to the useEffect dependency array so sent-invoice data stays in sync with the active account.

frontend/src/contractsABI/ChainvoiceABI.js (1)

328-339: Minor naming mismatch

Frontend calls contract.fee() but ABI exposes setFeeAmount. Ensure constant getter is still named fee (it is). No action needed.

Comment on lines 550 to 566
Fee for invoice pay : {parseFloat(
ethers.formatUnits(fee)
)}{" "}
ETH
</p>
<p className="text-right font-semibold">
{" "}
Amount: {drawerState.selectedInvoice.amountDue}{" "}
{/* {ethers.formatUnits(drawerState.selectedInvoice.amountDue)}{" "} */}
ETH
</p>
<p className="text-right font-semibold">
Total Amount:{" "}
{parseFloat(drawerState.selectedInvoice.amountDue) +
parseFloat(ethers.formatUnits(fee))}{" "}
ETH
</p>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Avoid parseFloat for ETH arithmetic

Floating-point math on token amounts can introduce rounding errors. Keep values in Wei (BigInt) and format only for display.

const totalWei = BigInt(invoice.amountDueWei) + fee; // all BigInt
const totalEth = ethers.formatUnits(totalWei);
🤖 Prompt for AI Agents
In frontend/src/page/ReceivedInvoice.jsx around lines 550 to 566, the code uses
parseFloat on ETH amounts for arithmetic, which can cause rounding errors. To
fix this, keep all token amounts in Wei as BigInt for calculations, add them
directly as BigInt values, and only convert to ETH string format using
ethers.formatUnits when displaying the result.

Comment on lines 105 to 108
const ciphertext = atob(encryptedStringBase64);

const accessControlConditions = [
{
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Decryption likely fails – drop the atob call

decryptToString expects the same base-64 string produced by encryptString. Converting with atob() mutates the data and causes “could not decrypt” errors.

-const ciphertext = atob(encryptedStringBase64);
+const ciphertext = encryptedStringBase64; // already base-64
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const ciphertext = atob(encryptedStringBase64);
const accessControlConditions = [
{
// Before decrypting, use the original Base64 string
- const ciphertext = atob(encryptedStringBase64);
+ const ciphertext = encryptedStringBase64; // already base-64
const accessControlConditions = [
{
// …
🤖 Prompt for AI Agents
In frontend/src/page/ReceivedInvoice.jsx around lines 105 to 108, the code
incorrectly uses atob() to decode the base64 encrypted string before passing it
to decryptToString, which expects the original base64 string. Remove the atob()
call and pass the encrypted base64 string directly to decryptToString to fix the
decryption errors.

Comment on lines +511 to +513
{parseFloat(drawerState.selectedInvoice.amountDue) +
parseFloat(ethers.formatUnits(fee))}{" "}
ETH
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Do ETH maths in Wei, not parseFloat

Convert both amountDue and fee to Wei, add, then format for display to prevent rounding drift.

🤖 Prompt for AI Agents
In frontend/src/page/SentInvoice.jsx around lines 511 to 513, the addition of
amountDue and fee is done using parseFloat on ETH values, which can cause
rounding errors. Convert both amountDue and fee to Wei units first, perform the
addition in Wei as integers, then convert the result back to ETH for display
using ethers utility functions. This ensures precise arithmetic without floating
point drift.

Comment on lines +103 to +106
const ciphertext = atob(encryptedStringBase64);
const accessControlConditions = [
{
contractAddress: "",
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Use the original base-64 ciphertext

Remove atob() to prevent decryption failures (same rationale as in ReceivedInvoice).

-const ciphertext = atob(encryptedStringBase64);
+const ciphertext = encryptedStringBase64;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const ciphertext = atob(encryptedStringBase64);
const accessControlConditions = [
{
contractAddress: "",
const ciphertext = encryptedStringBase64;
const accessControlConditions = [
{
contractAddress: "",
🤖 Prompt for AI Agents
In frontend/src/page/SentInvoice.jsx around lines 103 to 106, the code uses
atob() to decode the base64 encrypted string before decryption, which causes
decryption failures. Remove the atob() call and use the original base64
ciphertext string directly to fix the issue, consistent with the approach in
ReceivedInvoice.

Comment on lines +166 to +176
contractAddress: "",
standardContractType: "",
chain: "ethereum",
method: "",
parameters: [":userAddress"],
returnValueTest: {
comparator: "=",
value: data.clientAddress.toLowerCase(),
},
},
];
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Validate recipient address before encrypting

A malformed data.clientAddress will pass straight to the contract and later revert. Add a checksum test:

if (!ethers.isAddress(data.clientAddress)) {
  alert("Invalid client address");
  return;
}
🤖 Prompt for AI Agents
In frontend/src/components/CreateInvoice.jsx around lines 166 to 176, the code
uses data.clientAddress without validating it, which can cause contract call
failures if the address is malformed. Add a validation step before using
data.clientAddress by checking if it is a valid Ethereum address using
ethers.isAddress. If the address is invalid, show an alert with "Invalid client
address" and return early to prevent further processing.

const invoiceString = JSON.stringify(invoicePayload);

// 2. Setup Lit
const litNodeClient = new LitNodeClient({

Choose a reason for hiding this comment

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

why are we setting up the lit client on every function call, instead of once, just when the component renders?

Copy link

@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

♻️ Duplicate comments (4)
frontend/src/page/ReceivedInvoice.jsx (2)

114-114: Decryption likely fails – drop the atob call

decryptToString expects the same base-64 string produced by encryptString. Converting with atob() mutates the data and causes "could not decrypt" errors.

-const ciphertext = atob(encryptedStringBase64);
+const ciphertext = encryptedStringBase64; // already base-64

557-573: Avoid parseFloat for ETH arithmetic

Floating-point math on token amounts can introduce rounding errors. Keep values in Wei (BigInt) and format only for display.

const totalWei = BigInt(invoice.amountDueWei) + fee; // all BigInt
const totalEth = ethers.formatUnits(totalWei);
frontend/src/components/CreateInvoice.jsx (2)

51-62: Floating-point totals will bite you

totalAmountDue is built with parseFloat, which can mis-round (e.g., 0.3 + 0.6). Prefer ethers.parseUnits per line item or at least use BigInt.


183-193: Validate recipient address before encrypting

A malformed data.clientAddress will pass straight to the contract and later revert. Add a checksum test:

if (!ethers.isAddress(data.clientAddress)) {
  alert("Invalid client address");
  return;
}
🧹 Nitpick comments (1)
frontend/src/components/CreateInvoice.jsx (1)

515-521: Consider precision for amount calculation display

While the calculation logic is correct, consider using Number.parseFloat().toFixed(2) or similar for consistent decimal display in the amount field.

value={
- (parseFloat(itemData[index].qty) || 0) *
-   (parseFloat(itemData[index].unitPrice) || 0) -
- (parseFloat(itemData[index].discount) || 0) +
- (parseFloat(itemData[index].tax) || 0)
+ ((parseFloat(itemData[index].qty) || 0) *
+   (parseFloat(itemData[index].unitPrice) || 0) -
+ (parseFloat(itemData[index].discount) || 0) +
+ (parseFloat(itemData[index].tax) || 0)).toFixed(2)
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c9220a and 1b7aecd.

📒 Files selected for processing (3)
  • frontend/src/components/CreateInvoice.jsx (6 hunks)
  • frontend/src/page/ReceivedInvoice.jsx (8 hunks)
  • frontend/src/page/SentInvoice.jsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/src/page/SentInvoice.jsx
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/src/page/ReceivedInvoice.jsx

[error] 536-536: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🔇 Additional comments (5)
frontend/src/page/ReceivedInvoice.jsx (2)

59-74: LGTM: Proper Lit client initialization

The Lit client is correctly initialized once on component mount and stored in a ref, avoiding unnecessary re-initializations on every function call.


76-200: Well-structured decryption flow with proper error handling

The invoice fetching logic correctly waits for both wallet client and Lit readiness, implements proper access control conditions, and includes comprehensive error handling.

frontend/src/components/CreateInvoice.jsx (3)

64-77: Excellent: Lit client initialized once on mount

This addresses the previous feedback about setting up the Lit client on every function call. The client is now properly initialized once and reused.


79-118: Well-implemented dynamic item management

The item handling logic correctly calculates amounts dynamically and maintains proper state updates. The real-time calculation provides good UX.


134-257: Robust encryption implementation with comprehensive flow

The invoice encryption process is well-structured:

  • Proper payload construction
  • Correct access control conditions (sender OR receiver)
  • Session signature generation
  • Error handling for encryption/transaction failures

The integration with the updated contract ABI is seamless.

Copy link

@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

♻️ Duplicate comments (1)
README.md (1)

73-78: Add language identifier to fenced code block

Markdown-lint (MD040) still flags this block. Prefix the opening fence with bash for syntax highlighting.

-```
+```bash
 forge create contracts/src/Chainvoice.sol:Chainvoice \
   --rpc-url $ETC_RPC_URL \
   --private-key $PRIVATE_KEY \
   --broadcast
-```
+```
🧹 Nitpick comments (1)
README.md (1)

59-63: Fix typo and punctuation for clarity

Small language issues hinder readability.

-    - Create a .env in your project root i.e. `contracts/`
-    - Copy all the varible from `contracts/.env.example` to newly created `.env`
+    - Create a `.env` in your project root, i.e., `contracts/`
+    - Copy all the variables from `contracts/.env.example` to the newly created `.env`
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1b7aecd and ce51cbe.

📒 Files selected for processing (1)
  • README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[typographical] ~59-~59: Consider putting a comma before the abbreviation “i.e.”.
Context: ...ts - Create a .env in your project root i.e. contracts/ - Copy all the varible...

(IE_COMMA)


[uncategorized] ~79-~79: Possible missing comma found.
Context: ...key $PRIVATE_KEY \ --broadcast ``` 5. Finally add Contract Address to Frontend .env...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~90-~90: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...Restart your frontend development server so the new environment variables are loade...

(COMMA_COMPOUND_SENTENCE_2)

🪛 markdownlint-cli2 (0.17.2)
README.md

73-73: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Copy link

@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: 2

♻️ Duplicate comments (4)
frontend/src/page/ReceivedInvoice.jsx (3)

118-121: Decryption likely fails – drop the atob call

decryptToString expects the same base-64 string produced by encryptString. Converting with atob() mutates the data and causes "could not decrypt" errors.

-const ciphertext = atob(encryptedStringBase64);
+const ciphertext = encryptedStringBase64; // already base-64

539-556: Add missing key property for React list items

The map iteration is missing a key property, which can cause React rendering issues when the order of items changes.

-{drawerState.selectedInvoice?.items?.map((item, index) => (
-  <tbody>
+{drawerState.selectedInvoice?.items?.map((item, index) => (
+  <tbody key={index}>

561-577: Avoid parseFloat for ETH arithmetic

Floating-point math on token amounts can introduce rounding errors. Keep values in Wei (BigInt) and format only for display.

const totalWei = BigInt(invoice.amountDueWei) + fee; // all BigInt
const totalEth = ethers.formatUnits(totalWei);
frontend/src/components/CreateInvoice.jsx (1)

186-196: Validate recipient address before encrypting

A malformed data.clientAddress will pass straight to the contract and later revert. Add a checksum test:

if (!ethers.isAddress(data.clientAddress)) {
  alert("Invalid client address");
  return;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce51cbe and 54fefc3.

📒 Files selected for processing (5)
  • contracts/src/Chainvoice.sol (1 hunks)
  • frontend/src/components/CreateInvoice.jsx (6 hunks)
  • frontend/src/contractsABI/ChainvoiceABI.js (1 hunks)
  • frontend/src/page/ReceivedInvoice.jsx (8 hunks)
  • frontend/src/page/SentInvoice.jsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/page/SentInvoice.jsx
  • contracts/src/Chainvoice.sol
🧰 Additional context used
🪛 Biome (1.9.4)
frontend/src/page/ReceivedInvoice.jsx

[error] 540-540: Missing key property for this element in iterable.

The order of the items may change, and having a key can help React identify which item was moved.
Check the React documentation.

(lint/correctness/useJsxKeyInIterable)

🔇 Additional comments (5)
frontend/src/page/ReceivedInvoice.jsx (2)

56-78: LGTM! Lit client initialization properly moved to mount.

The Lit client is now correctly initialized once on component mount rather than on every function call, addressing the previous feedback about inefficient initialization.


219-225: LGTM! Payment calculation uses proper BigInt arithmetic.

The payment function now correctly converts amounts to Wei and performs BigInt addition, preventing precision errors in financial calculations.

frontend/src/components/CreateInvoice.jsx (2)

52-66: LGTM! Total calculation uses precise BigInt arithmetic.

The total amount calculation now uses parseUnits and BigInt operations, preventing floating-point precision errors in financial calculations.


68-81: LGTM! Lit client initialization properly moved to mount.

The Lit client is now correctly initialized once on component mount, addressing the previous feedback about inefficient initialization patterns.

frontend/src/contractsABI/ChainvoiceABI.js (1)

1-454: LGTM! ABI correctly reflects the updated contract interface.

The ABI has been properly updated to support the new encrypted invoice approach, with simplified function signatures and appropriate event indexing. The changes are consistent with the contract's shift from storing detailed invoice data to encrypted payloads.

Copy link

@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: 3

♻️ Duplicate comments (2)
frontend/src/page/ReceivedInvoice.jsx (2)

118-121: Still using atob() - this will cause decryption failures

This is the same issue flagged in previous reviews. The decryptToString function expects the original base64 string, but atob() decodes it into binary data, causing decryption to fail.

-const ciphertext = atob(encryptedStringBase64);
+const ciphertext = encryptedStringBase64;

561-577: Still using parseFloat for ETH arithmetic - precision issues remain

This is the same issue flagged in previous reviews. Using parseFloat on ETH amounts can introduce rounding errors in financial calculations.

-Fee for invoice pay : {parseFloat(
-  ethers.formatUnits(fee)
-)}{" "}
+Fee for invoice pay : {ethers.formatUnits(fee)}{" "}
 ETH
-Total Amount:{" "}
-{parseFloat(drawerState.selectedInvoice.amountDue) +
-  parseFloat(ethers.formatUnits(fee))}{" "}
-ETH
+Total Amount:{" "}
+{(
+  BigInt(ethers.parseUnits(drawerState.selectedInvoice.amountDue.toString(), 18)) +
+  BigInt(fee)
+).toString() / 1e18}{" "}
+ETH
🧹 Nitpick comments (1)
frontend/src/page/ReceivedInvoice.jsx (1)

194-200: Improve error handling granularity

The current error handling catches all errors broadly. Consider handling specific error types differently (network errors, decryption failures, contract errors).

     } catch (error) {
-      console.error("Decryption error:", error);
-      alert("Failed to fetch or decrypt received invoices.");
+      console.error("Error fetching invoices:", error);
+      if (error.message?.includes('decrypt')) {
+        alert("Failed to decrypt invoice data. You may not have permission to view these invoices.");
+      } else if (error.message?.includes('network')) {
+        alert("Network error. Please check your connection and try again.");
+      } else {
+        alert("Failed to fetch received invoices. Please try again.");
+      }
     } finally {
       setLoading(false);
     }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54fefc3 and 3aa274f.

📒 Files selected for processing (1)
  • frontend/src/page/ReceivedInvoice.jsx (8 hunks)
🔇 Additional comments (2)
frontend/src/page/ReceivedInvoice.jsx (2)

216-225: Payment function correctly uses Wei calculations

Good improvement! The payment function now properly converts amounts to Wei before arithmetic operations, avoiding floating-point precision issues.


539-556: React key property correctly added

Good fix! The tbody elements now have unique keys, resolving the React rendering issue flagged in previous reviews.

Comment on lines 327 to 342
const value = invoice.user[column.id] || "";
if (column.id === "to") {
return (
<TableCell key={column.id} align={column.align} sx={{ color: 'white', borderColor: '#25272b' }}>
{invoice.to.substring(0, 10)}...{invoice.to.substring(invoice.to.length - 10)}
<TableCell
key={column.id}
align={column.align}
sx={{ color: "white", borderColor: "#25272b" }}
>
{invoice.user.address
? `${invoice.user.address.substring(
0,
10
)}...${invoice.user.address.substring(
invoice.user.address.length - 10
)}`
: "N/A"}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Potential null reference on user data

The code assumes invoice.user exists but doesn't handle cases where the decrypted data might be malformed or incomplete.

-const value = invoice.user[column.id] || "";
+const value = invoice.user?.[column.id] || "";
 if (column.id === "to") {
   return (
     <TableCell
       key={column.id}
       align={column.align}
       sx={{ color: "white", borderColor: "#25272b" }}
     >
-      {invoice.user.address
-        ? `${invoice.user.address.substring(
+      {invoice.user?.address
+        ? `${invoice.user.address.substring(
             0,
             10
           )}...${invoice.user.address.substring(
             invoice.user.address.length - 10
           )}`
         : "N/A"}
     </TableCell>
   );
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const value = invoice.user[column.id] || "";
if (column.id === "to") {
return (
<TableCell key={column.id} align={column.align} sx={{ color: 'white', borderColor: '#25272b' }}>
{invoice.to.substring(0, 10)}...{invoice.to.substring(invoice.to.length - 10)}
<TableCell
key={column.id}
align={column.align}
sx={{ color: "white", borderColor: "#25272b" }}
>
{invoice.user.address
? `${invoice.user.address.substring(
0,
10
)}...${invoice.user.address.substring(
invoice.user.address.length - 10
)}`
: "N/A"}
const value = invoice.user?.[column.id] || "";
if (column.id === "to") {
return (
<TableCell
key={column.id}
align={column.align}
sx={{ color: "white", borderColor: "#25272b" }}
>
{invoice.user?.address
? `${invoice.user.address.substring(
0,
10
)}...${invoice.user.address.substring(
invoice.user.address.length - 10
)}`
: "N/A"}
🤖 Prompt for AI Agents
In frontend/src/page/ReceivedInvoice.jsx around lines 327 to 342, the code
assumes invoice.user is always defined, which can cause null reference errors if
the user data is missing or malformed. Add a check to ensure invoice.user exists
before accessing its properties, and provide a fallback UI or default value when
invoice.user is undefined or null to prevent runtime errors.

Copy link

@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: 0

♻️ Duplicate comments (1)
README.md (1)

73-78: Add language spec to fenced code block

The Forge command block still violates MD040 (fenced-code-language) and renders without syntax highlighting.
Use bash immediately after the opening back-ticks.

-```
+```bash
 forge create contracts/src/Chainvoice.sol:Chainvoice \
   --rpc-url $ETC_RPC_URL \
   --private-key $PRIVATE_KEY \
   --broadcast
-```
+```
🧹 Nitpick comments (2)
README.md (2)

59-63: Fix typos and improve bullet wording

Minor language issues reduce professionalism and may confuse readers.

-    - Create a .env in your project root i.e. `contracts/`
-    - Copy all the varible from `contracts/.env.example` to newly created `.env`
+    - Create a `.env` file in your project root, i.e., `contracts/`
+    - Copy all the variables from `contracts/.env.example` to the newly created `.env`

88-90: Tweak sentence for readability

Consider adding “that” for smoother flow.

-    - Restart your frontend development server so the new environment variables are loaded.
+    - Restart your frontend development server so that the new environment variables are loaded.
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3aa274f and 4bc2776.

📒 Files selected for processing (1)
  • README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[typographical] ~59-~59: Consider putting a comma before the abbreviation “i.e.”.
Context: ...ts - Create a .env in your project root i.e. contracts/ - Copy all the varible...

(IE_COMMA)


[uncategorized] ~60-~60: You might be missing the article “the” here.
Context: ...arible from contracts/.env.example to newly created .env `cp .env.ex...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~79-~79: Possible missing comma found.
Context: ...key $PRIVATE_KEY \ --broadcast ``` 5. Finally add Contract Address to Frontend .env...

(AI_HYDRA_LEO_MISSING_COMMA)


[uncategorized] ~79-~79: You might be missing the article “the” here.
Context: ..._KEY \ --broadcast ``` 5. Finally add Contract Address to Frontend .env - Creat...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[uncategorized] ~90-~90: Use a comma before ‘so’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...Restart your frontend development server so the new environment variables are loade...

(COMMA_COMPOUND_SENTENCE_2)

🪛 markdownlint-cli2 (0.17.2)
README.md

73-73: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

Copy link

@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: 0

♻️ Duplicate comments (2)
frontend/src/components/CreateInvoice.jsx (2)

190-200: Client address validation still missing

The data.clientAddress is used in access control conditions without validation, which was flagged in a previous review. A malformed address will cause the encryption to fail or behave unexpectedly.

Add address validation before using it:

+if (!ethers.isAddress(data.clientAddress)) {
+  alert("Invalid client address");
+  return;
+}
+
 const accessControlConditions = [
   {
     contractAddress: "",

524-529: Individual item display calculation still uses floating-point arithmetic

The display value calculation for individual items uses parseFloat, which creates inconsistency with the precise BigInt arithmetic used elsewhere. This was flagged in a previous review.

Use the computed amount from the item data instead:

-value={
-  (parseFloat(itemData[index].qty) || 0) *
-    (parseFloat(itemData[index].unitPrice) || 0) -
-  (parseFloat(itemData[index].discount) || 0) +
-  (parseFloat(itemData[index].tax) || 0)
-}
+value={itemData[index].amount}
🧹 Nitpick comments (1)
frontend/src/components/CreateInvoice.jsx (1)

203-255: Verify encryption error handling

The encryption process should handle specific error cases to provide better user feedback. Consider distinguishing between Lit Protocol connection errors, encryption failures, and contract transaction failures.

} catch (err) {
+  if (err.message.includes('Lit')) {
+    console.error("Lit Protocol error:", err);
+    alert("Failed to encrypt invoice data. Please try again.");
+  } else if (err.message.includes('transaction')) {
+    console.error("Transaction error:", err);
+    alert("Failed to submit invoice to blockchain. Please try again.");
+  } else {
-    console.error("Encryption or transaction failed:", err);
-    alert("Failed to create invoice.");
+    console.error("Unexpected error:", err);
+    alert("An unexpected error occurred. Please try again.");
+  }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4bc2776 and ab1bafc.

📒 Files selected for processing (3)
  • frontend/src/components/CreateInvoice.jsx (6 hunks)
  • frontend/src/page/ReceivedInvoice.jsx (8 hunks)
  • frontend/src/page/SentInvoice.jsx (6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/page/ReceivedInvoice.jsx
  • frontend/src/page/SentInvoice.jsx
🔇 Additional comments (4)
frontend/src/components/CreateInvoice.jsx (4)

68-81: Good improvement: Lit client initialization moved to useEffect

The Lit client is now properly initialized once in useEffect instead of on every function call, addressing the previous feedback about efficiency.


96-105: Excellent: Precise BigInt arithmetic for individual item calculations

The individual item amount calculations now use parseUnits and BigInt arithmetic, which resolves the floating-point precision issues flagged in previous reviews.


51-66: Precise total calculation implementation looks solid

The total amount calculation properly uses BigInt arithmetic throughout, preventing floating-point precision errors. The logic correctly handles the multiplication and division scaling.


360-365: User address field correctly set to read-only

The user address input is properly set to read-only and pre-filled with the connected wallet address, preventing accidental modifications.

@adityabhattad2021 adityabhattad2021 merged commit 1685e16 into StabilityNexus:main Jul 4, 2025
1 check passed
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.

2 participants