Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
505f6d9
-
kumawatkaran523 Jun 4, 2025
5a178ba
removed env
kumawatkaran523 Jun 9, 2025
61894ff
removed chainlink
kumawatkaran523 Jun 9, 2025
a6c2fac
removed chainlink
kumawatkaran523 Jun 9, 2025
3231f30
added env-copy
kumawatkaran523 Jun 9, 2025
8a05a03
removed unecessary folder
kumawatkaran523 Jun 9, 2025
91c1925
changed app.jsx
kumawatkaran523 Jun 9, 2025
86982f7
readme update
kumawatkaran523 Jun 9, 2025
e7c3f41
readme update
kumawatkaran523 Jun 9, 2025
77f3b14
updated contract
kumawatkaran523 Jun 16, 2025
3c9220a
updated UI and contract ABI
kumawatkaran523 Jun 16, 2025
1b7aecd
refactor: optimize Lit client init and invoice fetch
kumawatkaran523 Jul 3, 2025
ce51cbe
update readme
kumawatkaran523 Jul 3, 2025
e412537
updated contract
kumawatkaran523 Jul 3, 2025
54fefc3
removed calculation error
kumawatkaran523 Jul 3, 2025
3aa274f
removed calculation error
kumawatkaran523 Jul 3, 2025
4bc2776
correct typo
kumawatkaran523 Jul 3, 2025
ab1bafc
Security concern: Insufficient access control validation
kumawatkaran523 Jul 3, 2025
673ffa1
Updated dashboard,treasury account and routes
kumawatkaran523 Jul 4, 2025
4cd4795
correct some error
kumawatkaran523 Jul 5, 2025
86c54ef
Merge branch 'main' into main
kumawatkaran523 Jul 5, 2025
a54324c
added mobile menu
kumawatkaran523 Jul 8, 2025
538e82c
added erc20 token,updated contract,erc20abi,frontend
kumawatkaran523 Jul 10, 2025
1ce69bb
updated contract
kumawatkaran523 Jul 10, 2025
e390b0f
added erc20 logic
kumawatkaran523 Jul 12, 2025
c98624e
update ui
kumawatkaran523 Jul 12, 2025
99defe6
treasure fee set
kumawatkaran523 Jul 13, 2025
545031b
contract update with cancel property
kumawatkaran523 Aug 14, 2025
e46a4f3
frontend update with cancel property
kumawatkaran523 Aug 14, 2025
9a5ae47
frontend update with cancel property
kumawatkaran523 Aug 14, 2025
7717396
file conflict resolve
kumawatkaran523 Aug 14, 2025
c43085e
-
kumawatkaran523 Aug 14, 2025
363b696
-
kumawatkaran523 Aug 14, 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
133 changes: 124 additions & 9 deletions contracts/src/Chainvoice.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.13;

interface IERC20 {
function transferFrom(
address sender,
address recipient,
uint256 amount
) external returns (bool);
function balanceOf(address account) external view returns (uint256);
function allowance(
address owner,
address spender
) external view returns (uint256);
}

contract Chainvoice {
struct InvoiceDetails {
uint256 id;
address from;
address to;
uint256 amountDue;
address tokenAddress;
bool isPaid;
bool isCancelled;
string encryptedInvoiceData; // Base64-encoded ciphertext
string encryptedHash;
}
Expand All @@ -25,14 +40,23 @@ contract Chainvoice {
event InvoiceCreated(
uint256 indexed id,
address indexed from,
address indexed to
address indexed to,
address tokenAddress
);

event InvoicePaid(
uint256 indexed id,
address indexed from,
address indexed to,
uint256 amount
uint256 amount,
address tokenAddress
);

event InvoiceCancelled(
uint256 indexed id,
address indexed from,
address indexed to,
address tokenAddress
);

constructor() {
Expand All @@ -48,12 +72,20 @@ contract Chainvoice {
function createInvoice(
address to,
uint256 amountDue,
address tokenAddress,
string memory encryptedInvoiceData,
string memory encryptedHash
) external {
require(to != address(0), "Recipient address is zero");
require(to != msg.sender, "Self-invoicing not allowed");

if (tokenAddress != address(0)) {
require(tokenAddress.code.length > 0, "Not a contract address");
(bool success, ) = tokenAddress.staticcall(
abi.encodeWithSignature("balanceOf(address)", address(this))
);
require(success, "Not an ERC20 token");
}
uint256 invoiceId = invoices.length;

invoices.push(
Expand All @@ -62,7 +94,9 @@ contract Chainvoice {
from: msg.sender,
to: to,
amountDue: amountDue,
tokenAddress: tokenAddress,
isPaid: false,
isCancelled: false,
encryptedInvoiceData: encryptedInvoiceData,
encryptedHash: encryptedHash
})
Expand All @@ -71,7 +105,20 @@ contract Chainvoice {
sentInvoices[msg.sender].push(invoiceId);
receivedInvoices[to].push(invoiceId);

emit InvoiceCreated(invoiceId, msg.sender, to);
emit InvoiceCreated(invoiceId, msg.sender, to, tokenAddress);
}

function cancelInvoice(uint256 invoiceId) external {
require(invoiceId < invoices.length, "Invalid invoice ID");
InvoiceDetails storage invoice = invoices[invoiceId];

require(msg.sender == invoice.from, "Only invoice creator can cancel");
require(
!invoice.isPaid && !invoice.isCancelled,
"Invoice not cancellable"
);
invoice.isCancelled = true;
emit InvoiceCancelled(invoiceId, invoice.from, invoice.to, invoice.tokenAddress);
}

function payInvoice(uint256 invoiceId) external payable {
Expand All @@ -80,16 +127,84 @@ contract Chainvoice {
InvoiceDetails storage invoice = invoices[invoiceId];
require(msg.sender == invoice.to, "Not authorized");
require(!invoice.isPaid, "Already paid");
require(msg.value == invoice.amountDue + fee, "Incorrect payment amount");
require(!invoice.isCancelled, "Invoice is cancelled");

if (invoice.tokenAddress == address(0)) {
// Native token (ETH) payment
require(
msg.value == invoice.amountDue + fee,
"Incorrect payment amount"
);
accumulatedFees += fee;

uint256 amountToSender = msg.value - fee;
(bool sent, ) = payable(invoice.from).call{value: amountToSender}(
""
);
require(sent, "Transfer failed");
} else {
// ERC20 token payment
require(msg.value == fee, "Must pay fee in native token");
require(
IERC20(invoice.tokenAddress).allowance(
msg.sender,
address(this)
) >= invoice.amountDue,
"Insufficient allowance"
);

accumulatedFees += fee;
bool transferSuccess = IERC20(invoice.tokenAddress).transferFrom(
msg.sender,
invoice.from,
invoice.amountDue
);
require(transferSuccess, "Token transfer failed");
}

accumulatedFees += fee;
invoice.isPaid = true;
emit InvoicePaid(
invoiceId,
invoice.from,
invoice.to,
invoice.amountDue,
invoice.tokenAddress
);
}

uint256 amountToSender = msg.value - fee;
(bool sent, ) = payable(invoice.from).call{value: amountToSender}("");
require(sent, "Transfer failed");
function getPaymentStatus(
uint256 invoiceId,
address payer
)
external
view
returns (bool canPay, uint256 payerBalance, uint256 allowanceAmount)
{
require(invoiceId < invoices.length, "Invalid invoice ID");
InvoiceDetails memory invoice = invoices[invoiceId];
if (invoice.isCancelled) {
return (false, (payer).balance, 0);
}

emit InvoicePaid(invoiceId, invoice.from, invoice.to, msg.value);
if (invoice.tokenAddress == address(0)) {
return (
payer.balance >= invoice.amountDue + fee,
payer.balance,
type(uint256).max // Native token has no allowance
);
} else {
return (
IERC20(invoice.tokenAddress).balanceOf(payer) >=
invoice.amountDue &&
IERC20(invoice.tokenAddress).allowance(
payer,
address(this)
) >=
invoice.amountDue,
IERC20(invoice.tokenAddress).balanceOf(payer),
IERC20(invoice.tokenAddress).allowance(payer, address(this))
);
}
}

function getSentInvoices(
Expand Down
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"@mui/material": "^6.4.6",
"@radix-ui/react-label": "^2.1.1",
"@radix-ui/react-popover": "^1.1.5",
"@radix-ui/react-select": "^2.2.5",
"@radix-ui/react-slot": "^1.1.1",
"@rainbow-me/rainbowkit": "^2.2.2",
"@tanstack/react-query": "^5.64.1",
Expand All @@ -42,6 +43,7 @@
"react-icons": "^5.5.0",
"react-router-dom": "^7.1.1",
"react-to-print": "^3.0.5",
"react-toastify": "^11.0.5",
"tailwind-merge": "^2.6.0",
"tailwindcss-animate": "^1.0.7",
"viem": "^2.22.9",
Expand Down
Binary file added frontend/public/dashboard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/lit-protocol-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/lit-protocol-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/token-select.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/tokenImages/cin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/tokenImages/eth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/public/tokenImages/generic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import Feature from "./page/Feature";
import About from "./page/About";
import Working from "./page/Working";
import Treasure from "./page/Treasure";
import CreateInvoice from "./components/CreateInvoice";
import CreateInvoice from "./page/CreateInvoice";
import SentInvoice from "./page/SentInvoice";
import ReceivedInvoice from "./page/ReceivedInvoice";

Expand All @@ -41,7 +41,7 @@ function App() {
<QueryClientProvider client={queryClient}>
<RainbowKitProvider
coolMode
initialChain={citreaTestnet}
// initialChain={citreaTestnet}
theme={darkTheme({
accentColor: "#22c55e",
accentColorForeground: "white",
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/Footer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ function Footer() {
<img src="logo-animated.gif" alt="" width={50} className='mb-2'/>
<img src="horizontal-nexus.png" alt="" />
</div> */}
<p className="text-sm my-3">
{/* <p className="text-sm my-3">
&copy; {new Date().getFullYear()} Chainvoice. All rights reserved.
</p>
</p> */}
</div>
</footer>
</>
Expand Down
48 changes: 26 additions & 22 deletions frontend/src/components/Navbar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,28 +75,34 @@ function Navbar() {
}
};

const navItems = [
// const navItems = [
// {
// name: "Home",
// icon: <HomeIcon className="text-current" />,
// action: () => handleScroll("home-section"),
// path: "/",
// },
// {
// name: "Features",
// icon: <FeaturedPlayListIcon className="text-current" />,
// action: () => handleScroll("feature-section"),
// path: "/#feature-section",
// },
// {
// name: "Services",
// icon: <DesignServicesIcon className="text-current" />,
// action: () => handleScroll("service-section"),
// path: "/#service-section",
// },
// ];

const appItems = [
{
name: "Home",
icon: <HomeIcon className="text-current" />,
action: () => handleScroll("home-section"),
path: "/",
},
{
name: "Features",
icon: <FeaturedPlayListIcon className="text-current" />,
action: () => handleScroll("feature-section"),
path: "/#feature-section",
},
{
name: "Services",
icon: <DesignServicesIcon className="text-current" />,
action: () => handleScroll("service-section"),
path: "/#service-section",
},
];

const appItems = [
{
name: "Dashboard",
icon: <DashboardIcon className="text-current" />,
Expand Down Expand Up @@ -152,7 +158,7 @@ function Navbar() {

{/* Desktop Navigation */}
<div className="hidden md:flex items-center space-x-2">
{navItems.map((item) => (
{/* {navItems.map((item) => (
<motion.button
key={item.name}
whileHover={{ y: -2 }}
Expand All @@ -167,8 +173,7 @@ function Navbar() {
<span className="mr-2">{item.icon}</span>
{item.name}
</motion.button>
))}

))} */}
{isConnected &&
appItems.map((item) => (
<motion.div
Expand Down Expand Up @@ -261,7 +266,7 @@ function Navbar() {
>
<div className="pt-2 pb-4 space-y-2">
{/* Navigation Links */}
{navItems.map((item) => (
{/* {navItems.map((item) => (
<motion.button
key={`mobile-${item.name}`}
whileTap={{ scale: 0.95 }}
Expand All @@ -278,8 +283,7 @@ function Navbar() {
<span className="mr-3">{item.icon}</span>
{item.name}
</motion.button>
))}

))} */}
{isConnected &&
appItems.map((item) => (
<Link
Expand Down
Loading