-
- {" "}
- Start Sending Your
-
-
- Invoice Today!
+
+ {/* Token Support Section */}
+
+
+
+
+ Universal Token Support
+
+
+ Accept payments in any ERC20 token while maintaining full
+ encryption and security through Lit Protocol
-
+
+
+
+
+
+
+ Seamless Multi-Token{" "}
+ Payments
+
+
+
+ Chainvoice's smart contract architecture automatically handles
+ token conversions and verifications, while Lit Protocol ensures
+ all payment details remain encrypted until settlement. Our
+ system supports:
+
+
+
+
+ ✓
+ All standard ERC20 tokens across EVM chains
+
+
+ ✓
+ Native chain currencies (ETH, MATIC, etc.)
+
+
+ ✓
+ Stablecoins with automatic price feeds
+
+
+ ✓
+ Custom token whitelisting for enterprise clients
+
+
+
+
+
+
+
+ 1000+ ERC20 Token
+
+
+
+
+
+
+ {/* CTA Section */}
+
+
+
+
+
+
+ Ready to Experience{" "}
+ Next-Gen Invoicing?
+
+
+
+ Join thousands of Web3-native businesses already using Chainvoice
+ for secure, encrypted invoicing with support for all major ERC20
+ tokens across multiple chains.
+
+
+
+
+
-
-
+ {/* Footer */}
+
+
+
-
-
+
+
Chain
voice
-
- Secure & Smart Invoicing
+
+ The most secure Web3 invoicing platform powered by Lit
+ Protocol's decentralized encryption technology.
-
-
-
Quick Links
-
- {["Home", "Feature", "Treasure", "Service", "Invoice"].map(
- (link) => (
-
-
- {link}
-
-
- )
- )}
-
-
-
-
Services
-
- {[
- "Blog & Article",
- "Terms and Conditions",
- "Privacy Policy",
- "Contact Us",
- "Invoice",
- ].map((link) => (
-
-
- {link}
-
-
- ))}
-
-
-
-
Contact
-
- {["chainvoice@gmail.com"].map((link) => (
-
-
- {link}
-
-
- ))}
-
-
+
+
+
+ Product
+
+
+ {[
+ "Features",
+ "Security",
+ "Token Support",
+ "Pricing",
+ "API",
+ ].map((item) => (
+
+
+ {item}
+
+
+ ))}
+
+
+
+
+
+ Resources
+
+
+ {[
+ "Documentation",
+ "Developers",
+ "GitHub",
+ "Blog",
+ "Status",
+ ].map((item) => (
+
+
+ {item}
+
+
+ ))}
+
+
+
+
-
-
- >
+
+
+
+ © {new Date().getFullYear()} Chainvoice. All rights reserved.
+
+
+
+
+
+
);
}
+
export default Landing;
diff --git a/frontend/src/page/ReceivedInvoice.jsx b/frontend/src/page/ReceivedInvoice.jsx
index daade8b6..054cf636 100644
--- a/frontend/src/page/ReceivedInvoice.jsx
+++ b/frontend/src/page/ReceivedInvoice.jsx
@@ -22,17 +22,33 @@ import {
generateAuthSig,
LitAccessControlConditionResource,
} from "@lit-protocol/auth-helpers";
+import { ERC20_ABI } from "@/contractsABI/ERC20_ABI";
+import { toast } from "react-toastify";
+import "react-toastify/dist/ReactToastify.css";
+import CancelIcon from "@mui/icons-material/Cancel";
+
+import {
+ CircularProgress,
+ Skeleton,
+ Chip,
+ Avatar,
+ Tooltip,
+ IconButton,
+ Typography,
+} from "@mui/material";
+import PaidIcon from "@mui/icons-material/CheckCircle";
+import UnpaidIcon from "@mui/icons-material/Pending";
+import DownloadIcon from "@mui/icons-material/Download";
+import CurrencyExchangeIcon from "@mui/icons-material/CurrencyExchange";
+import { TOKEN_PRESETS } from "@/utils/erc20_token";
const columns = [
- { id: "fname", label: "First Name", minWidth: 100 },
- { id: "lname", label: "Last Name", minWidth: 100 },
- { id: "to", label: "Sender's address", minWidth: 200 },
- { id: "email", label: "Email", minWidth: 170 },
- // { id: 'country', label: 'Country', minWidth: 100 },
- { id: "amountDue", label: "Total Amount", minWidth: 100, align: "right" },
- { id: "isPaid", label: "Status", minWidth: 100 },
- { id: "detail", label: "Detail Invoice", minWidth: 100 },
- { id: "pay", label: "Pay / Paid", minWidth: 100 },
+ { id: "fname", label: "Client", minWidth: 120 },
+ { id: "to", label: "Sender", minWidth: 150 },
+ { id: "amountDue", label: "Amount", minWidth: 100, align: "right" },
+ { id: "status", label: "Status", minWidth: 100 },
+ { id: "date", label: "Date", minWidth: 100 },
+ { id: "actions", label: "Actions", minWidth: 150 },
];
function ReceivedInvoice() {
@@ -40,12 +56,14 @@ function ReceivedInvoice() {
const [rowsPerPage, setRowsPerPage] = useState(10);
const { data: walletClient } = useWalletClient();
const { address } = useAccount();
- const [loading, setLoading] = useState(false);
+ const [loading, setLoading] = useState(true);
const [receivedInvoices, setReceivedInvoice] = useState([]);
const [fee, setFee] = useState(0);
const [error, setError] = useState(null);
const [litReady, setLitReady] = useState(false);
const litClientRef = useRef(null);
+ const [paymentLoading, setPaymentLoading] = useState({});
+ const [networkLoading, setNetworkLoading] = useState(false);
const handleChangePage = (event, newPage) => {
setPage(newPage);
@@ -68,10 +86,9 @@ function ReceivedInvoice() {
await client.connect();
litClientRef.current = client;
setLitReady(true);
- console.log(litClientRef.current);
}
} catch (error) {
- console.error("Error while lit client initialization:", error);
+ console.error("Error initializing Lit client:", error);
} finally {
setLoading(false);
}
@@ -90,23 +107,19 @@ function ReceivedInvoice() {
const signer = await provider.getSigner();
const network = await provider.getNetwork();
- if (network.chainId != 5115) {
+ if (network.chainId != 11155111) {
setError(
- `Failed to load invoices. You're connected to the "${network.name}" network, but your invoices are on the "Citrea" testnet. Please switch to Sepolia and try again.`
+ `You're connected to ${network.name}. Please switch to Sepolia network to view your invoices.`
);
-
setLoading(false);
return;
}
- // 1. Setup Lit Node
-
const litNodeClient = litClientRef.current;
if (!litNodeClient) {
alert("Lit client not initialized");
return;
}
- // 2. Get data from contract
const contract = new Contract(
import.meta.env.VITE_CONTRACT_ADDRESS,
ChainvoiceABI,
@@ -114,7 +127,14 @@ function ReceivedInvoice() {
);
const res = await contract.getReceivedInvoices(address);
- console.log("getReceivedInvoices raw response:", res);
+ console.log("Raw invoices data:", res);
+
+ if (!res || !Array.isArray(res) || res.length === 0) {
+ console.warn("No invoices found.");
+ setReceivedInvoice([]);
+ setLoading(false);
+ return;
+ }
// First check if user has any invoices
// if (!res || !Array.isArray(res) || res.length === 0) {
@@ -128,111 +148,132 @@ function ReceivedInvoice() {
const decryptedInvoices = [];
for (const invoice of res) {
- const id = invoice[0];
- const from = invoice[1].toLowerCase();
- const to = invoice[2].toLowerCase();
- const isPaid = invoice[4];
- const encryptedStringBase64 = invoice[5]; // encryptedData
- const dataToEncryptHash = invoice[6];
-
- if (!encryptedStringBase64 || !dataToEncryptHash) continue;
- const currentUserAddress = address.toLowerCase();
- if (currentUserAddress !== from && currentUserAddress !== to) {
- console.warn(
- `User ${currentUserAddress} not authorized to decrypt invoice ${id}`
- );
- continue;
- }
- const ciphertext = atob(encryptedStringBase64);
- const accessControlConditions = [
- {
- contractAddress: "",
- standardContractType: "",
- chain: "ethereum",
- method: "",
- parameters: [":userAddress"],
- returnValueTest: {
- comparator: "=",
- value: invoice[1].toLowerCase(), // from
- },
- },
- { operator: "or" },
- {
- contractAddress: "",
- standardContractType: "",
- chain: "ethereum",
- method: "",
- parameters: [":userAddress"],
- returnValueTest: {
- comparator: "=",
- value: invoice[2].toLowerCase(), // to
+ try {
+ const id = invoice[0];
+ const from = invoice[1].toLowerCase();
+ const to = invoice[2].toLowerCase();
+ const isPaid = invoice[5];
+ const isCancelled = invoice[6];
+ const encryptedStringBase64 = invoice[7];
+ const dataToEncryptHash = invoice[8];
+
+ if (!encryptedStringBase64 || !dataToEncryptHash) continue;
+
+ const currentUserAddress = address.toLowerCase();
+ if (currentUserAddress !== from && currentUserAddress !== to) {
+ console.warn(`Unauthorized access attempt for invoice ${id}`);
+ continue;
+ }
+ const ciphertext = atob(encryptedStringBase64);
+ const accessControlConditions = [
+ {
+ contractAddress: "",
+ standardContractType: "",
+ chain: "ethereum",
+ method: "",
+ parameters: [":userAddress"],
+ returnValueTest: {
+ comparator: "=",
+ value: from,
+ },
},
- },
- ];
-
- const sessionSigs = await litNodeClient.getSessionSigs({
- chain: "ethereum",
- resourceAbilityRequests: [
+ { operator: "or" },
{
- resource: new LitAccessControlConditionResource("*"),
- ability: LIT_ABILITY.AccessControlConditionDecryption,
+ contractAddress: "",
+ standardContractType: "",
+ chain: "ethereum",
+ method: "",
+ parameters: [":userAddress"],
+ returnValueTest: {
+ comparator: "=",
+ value: to,
+ },
},
- ],
- authNeededCallback: async ({
- uri,
- expiration,
- resourceAbilityRequests,
- }) => {
- const nonce = await litNodeClient.getLatestBlockhash();
- const toSign = await createSiweMessageWithRecaps({
+ ];
+ const sessionSigs = await litNodeClient.getSessionSigs({
+ chain: "ethereum",
+ resourceAbilityRequests: [
+ {
+ resource: new LitAccessControlConditionResource("*"),
+ ability: LIT_ABILITY.AccessControlConditionDecryption,
+ },
+ ],
+ authNeededCallback: async ({
uri,
expiration,
- resources: resourceAbilityRequests,
- walletAddress: address,
- nonce,
- litNodeClient,
- });
- return await generateAuthSig({ signer, toSign });
- },
- });
-
- const decryptedString = await decryptToString(
- {
- accessControlConditions,
- chain: "ethereum",
- ciphertext,
- dataToEncryptHash,
- sessionSigs,
- },
- litNodeClient
- );
-
- const parsed = JSON.parse(decryptedString);
- parsed["id"] = id;
- parsed["isPaid"] = isPaid;
- decryptedInvoices.push(parsed);
+ resourceAbilityRequests,
+ }) => {
+ const nonce = await litNodeClient.getLatestBlockhash();
+ const toSign = await createSiweMessageWithRecaps({
+ uri,
+ expiration,
+ resources: resourceAbilityRequests,
+ walletAddress: address,
+ nonce,
+ litNodeClient,
+ });
+ return await generateAuthSig({ signer, toSign });
+ },
+ });
+ const decryptedString = await decryptToString(
+ {
+ accessControlConditions,
+ chain: "ethereum",
+ ciphertext,
+ dataToEncryptHash,
+ sessionSigs,
+ },
+ litNodeClient
+ );
+ const parsed = JSON.parse(decryptedString);
+ parsed["id"] = id;
+ parsed["isPaid"] = isPaid;
+ parsed["isCancelled"] = isCancelled;
+
+ // Enhance with token details
+ if (parsed.paymentToken?.address) {
+ const tokenInfo = TOKEN_PRESETS.find(
+ (t) =>
+ t.address.toLowerCase() ===
+ parsed.paymentToken.address.toLowerCase()
+ );
+ if (tokenInfo) {
+ parsed.paymentToken = {
+ ...parsed.paymentToken,
+ logo: tokenInfo.logo,
+ decimals: tokenInfo.decimals,
+ };
+ }
+ }
+
+ decryptedInvoices.push(parsed);
+ } catch (err) {
+ console.error(`Error processing invoice ${invoice[0]}:`, err);
+ }
}
- console.log("decrypted : ", decryptedInvoices);
setReceivedInvoice(decryptedInvoices);
-
const fee = await contract.fee();
setFee(fee);
} catch (error) {
- console.error("Decryption error:", error);
- alert("Failed to fetch or decrypt received invoices.");
+ console.error("Fetch error:", error);
} finally {
setLoading(false);
}
};
fetchReceivedInvoices();
- console.log("invoices : ", receivedInvoices);
- }, [walletClient, litReady]);
+ }, [walletClient, litReady, address]);
+
+ const payInvoice = async (invoiceId, amountDue, tokenAddress) => {
+ if (!walletClient) {
+ console.error("Wallet not connected");
+ return;
+ }
+
+ setPaymentLoading((prev) => ({ ...prev, [invoiceId]: true }));
- const payInvoice = async (id, amountDue) => {
try {
- if (!walletClient) return;
const provider = new BrowserProvider(walletClient);
const signer = await provider.getSigner();
const contract = new Contract(
@@ -240,18 +281,81 @@ function ReceivedInvoice() {
ChainvoiceABI,
signer
);
- console.log(ethers.parseUnits(String(amountDue), 18));
+ const invoice = receivedInvoices.find((inv) => inv.id === invoiceId);
+ if (invoice?.isCancelled) {
+ throw new Error("Cannot pay a cancelled invoice");
+ }
const fee = await contract.fee();
- console.log(fee);
- const amountDueInWei = ethers.parseUnits(String(amountDue), 18);
- const feeInWei = BigInt(fee);
- const total = amountDueInWei + feeInWei;
+ const isNativeToken = tokenAddress === ethers.ZeroAddress;
- const res = await contract.payInvoice(BigInt(id), {
- value: total,
- });
+ if (!ethers.isAddress(tokenAddress)) {
+ throw new Error(`Invalid token address: ${tokenAddress}`);
+ }
+
+ const tokenInfo = TOKEN_PRESETS.find(
+ (t) => t.address.toLowerCase() === tokenAddress.toLowerCase()
+ );
+ const tokenSymbol = tokenInfo?.symbol || "Token";
+
+ if (!isNativeToken) {
+ const tokenContract = new Contract(tokenAddress, ERC20_ABI, signer);
+
+ const currentAllowance = await tokenContract.allowance(
+ await signer.getAddress(),
+ import.meta.env.VITE_CONTRACT_ADDRESS
+ );
+
+ const decimals = await tokenContract.decimals();
+ const amountDueInWei = ethers.parseUnits(String(amountDue), decimals);
+
+ if (currentAllowance < amountDueInWei) {
+ const approveTx = await tokenContract.approve(
+ import.meta.env.VITE_CONTRACT_ADDRESS,
+ amountDueInWei
+ );
+
+ await approveTx.wait();
+ alert(
+ `Approval for ${tokenSymbol} completed! Now processing payment...`
+ );
+ }
+
+ const tx = await contract.payInvoice(BigInt(invoiceId), {
+ value: fee,
+ });
+
+ await tx.wait();
+ alert(`Payment successful in ${tokenSymbol}!`);
+ } else {
+ const amountDueInWei = ethers.parseUnits(String(amountDue), 18);
+ const total = amountDueInWei + BigInt(fee);
+
+ const tx = await contract.payInvoice(BigInt(invoiceId), {
+ value: total,
+ });
+
+ await tx.wait();
+ alert("Payment successful in ETH!");
+ }
+
+ // Refresh invoice status
+ const updatedInvoices = receivedInvoices.map((inv) =>
+ inv.id === invoiceId ? { ...inv, isPaid: true } : inv
+ );
+ setReceivedInvoice(updatedInvoices);
} catch (error) {
- console.log(error);
+ console.error("Payment failed:", error);
+ if (error.code === "ACTION_REJECTED") {
+ toast.error("Transaction was rejected by user");
+ } else if (error.message.includes("insufficient balance")) {
+ toast.error("Insufficient balance for this transaction");
+ } else if (error.message.includes("cancelled")) {
+ toast.error("Cannot pay a cancelled invoice");
+ } else {
+ toast.error(`Payment failed: ${error.reason || error.message}`);
+ }
+ } finally {
+ setPaymentLoading((prev) => ({ ...prev, [invoiceId]: false }));
}
};
@@ -274,344 +378,617 @@ function ReceivedInvoice() {
});
};
- const contentRef = useRef();
const handlePrint = async () => {
- const element = contentRef.current;
- if (!element) {
- return;
- }
+ const element = document.getElementById("invoice-print");
+ if (!element) return;
- const canvas = await html2canvas(element, {
- scale: 2,
- });
+ const canvas = await html2canvas(element, { scale: 2 });
const data = canvas.toDataURL("image/png");
- // download feature (implement later on)
- // const pdf = new jsPDF({
- // orientation: "portrait",
- // unit: "px",
- // format: "a4",
- // });
+ const link = document.createElement("a");
+ link.download = `invoice-${drawerState.selectedInvoice.id}.png`;
+ link.href = data;
+ link.click();
+ };
- // const imgProperties = pdf.getImageProperties(data);
- // const pdfWidth = pdf.internal.pageSize.getWidth();
+ const switchNetwork = async () => {
+ try {
+ setNetworkLoading(true);
+ await window.ethereum.request({
+ method: "wallet_switchEthereumChain",
+ params: [{ chainId: "0xaa36a7" }], // Sepolia chain ID
+ });
+ setError(null);
+ } catch (error) {
+ console.error("Network switch failed:", error);
+ alert("Failed to switch network. Please switch to Sepolia manually.");
+ } finally {
+ setNetworkLoading(false);
+ }
+ };
- // const pdfHeight = (imgProperties.height * pdfWidth) / imgProperties.width;
+ const formatAddress = (address) => {
+ return `${address.substring(0, 10)}...${address.substring(
+ address.length - 10
+ )}`;
+ };
- // pdf.addImage(data, "PNG", 0, 0, pdfWidth, pdfHeight);
- // pdf.save("invoice.pdf");
+ const formatDate = (issueDate) => {
+ const date = new Date(issueDate);
+ return date.toLocaleString();
};
+
return (
-
-
Received Invoice Request
-
Pay to your client request
-
- {loading ? (
- Loading invoices...
- ) : error ? (
- {error}
- ) : receivedInvoices.length === 0 ? (
- No invoices found
- ) : (
- <>
-
-
-
-
- {columns.map((column) => (
-
- {column.label}
-
- ))}
-
-
-
- {receivedInvoices
- .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
- .map((invoice, index) => (
-
- {columns.map((column) => {
-
- const value = invoice?.user[column.id] || "";
-
- if (column.id === "to") {
- return (
-
- {invoice.user?.address
- ? `${invoice.user.address.substring(
- 0,
- 10
- )}...${invoice.user.address.substring(
- invoice.user.address.length - 10
- )}`
- : "N/A"}
-
- );
- }
- if (column.id === "amountDue") {
- return (
-
- {/* {ethers.formatUnits(invoice.amountDue)} ETH */}
- {invoice.amountDue} ETH
-
- );
- }
- if (column.id === "isPaid") {
- return (
-
-
- {invoice.isPaid ? "Paid" : "Not Paid"}
-
-
- );
- }
- if (column.id === "detail") {
- return (
-
+
+
+
+
Received Invoices
+
+ Manage and pay your incoming invoices
+
+
+ {error && (
+
+ {networkLoading ? (
+ <>
+
+ Switching...
+ >
+ ) : (
+ "Switch to Sepolia"
+ )}
+
+ )}
+
+
+
+ {loading ? (
+
+
+
+
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+ ) : error ? (
+
+ ) : receivedInvoices.length === 0 ? (
+
+
+
+
+ No Invoices Found
+
+
+ You don't have any received invoices yet.
+
+
+
+ ) : (
+ <>
+
+
+
+
+ {columns.map((column) => (
+
+ {column.label}
+
+ ))}
+
+
+
+ {receivedInvoices
+ .slice(
+ page * rowsPerPage,
+ page * rowsPerPage + rowsPerPage
+ )
+ .map((invoice) => (
+
+ {/* Client Column */}
+
+
+
-
+
+
+ {invoice.user?.fname} {invoice.user?.lname}
+
+
+ {invoice.user?.email}
+
+
+
+
+
+ {/* Sender Column */}
+
+
+
+ {formatAddress(invoice.user?.address)}
+
+
+
+
+ {/* Amount Column */}
+
+
+ {invoice.paymentToken?.logo ? (
+
+ ) : (
+
+ )}
+
+ {invoice.amountDue}{" "}
+ {invoice.paymentToken?.symbol}
+
+
+
+
+ {/* Status Column */}
+
+ {invoice.isCancelled ? (
+ }
+ label="Cancelled"
+ color="error"
+ size="small"
+ variant="outlined"
+ />
+ ) : invoice.isPaid ? (
+ }
+ label="Paid"
+ color="success"
+ size="small"
+ variant="outlined"
+ />
+ ) : (
+ }
+ />
+ )}
+
+ {/* Date Column */}
+
+
+
+ {formatDate(invoice.issueDate)}
+
+
+
+
+
+
+
-
-
-
- );
- }
- if (column.id === "pay" && !invoice.isPaid) {
- return (
-
+
+
+
+
+ {!invoice.isPaid && !invoice.isCancelled && (
- payInvoice(invoice.id, invoice.amountDue)
+ payInvoice(
+ invoice.id,
+ invoice.amountDue,
+ invoice.paymentToken?.address ??
+ ethers.ZeroAddress
+ )
}
+ disabled={paymentLoading[invoice.id]}
+ className={`px-3 py-1 rounded-md text-sm font-medium flex items-center ${
+ paymentLoading[invoice.id]
+ ? "bg-gray-300 text-gray-600"
+ : "bg-green-600 hover:bg-green-700 text-white"
+ }`}
>
- Pay Now
+ {paymentLoading[invoice.id] ? (
+ <>
+
+ Processing...
+ >
+ ) : (
+ "Pay Now"
+ )}
-
- );
- }
- if (column.id === "pay" && invoice.isPaid) {
- return (
-
+ )}
+ {invoice.isCancelled && (
- payInvoice(invoice.id, invoice.amountDue)
- }
- disabled
+ disabled={true}
+ className="px-3 py-1 rounded-md text-sm font-medium flex items-center bg-gray-300 text-gray-600"
>
- Already Paid
+ Cancelled
-
- );
- }
- return (
-
- {value}
-
- );
- })}
-
- ))}
-
-
-
-
+
+
+ ))}
+
+
+
+
- >
- )}
-
-
+ "& .MuiSelect-icon": {
+ color: "#64748b",
+ },
+ }}
+ />
+ >
+ )}
+
+
+
+ {/* Invoice Detail Drawer */}
{drawerState.selectedInvoice && (
-
-
-
-
-
-
- Issued by {drawerState.selectedInvoice.issueDate}
-
-
- Payment Due by {drawerState.selectedInvoice.dueDate}
+
+
+
+
+
+
+ Cha
+ in
+ voice
+
+
+ Powered by Chainvoice
+
-
-
- Invoice # {drawerState.selectedInvoice.id.toString()}
-
+
+
INVOICE
+
+ #{drawerState.selectedInvoice.id.toString().padStart(6, "0")}
+
+
+ {drawerState.selectedInvoice.isCancelled ? (
+ }
+ />
+ ) : drawerState.selectedInvoice.isPaid ? (
+ }
+ />
+ ) : (
+ }
+ />
+ )}
+
+
+ {drawerState.selectedInvoice.isCancelled && (
+
+
+
+
+ Invoice Cancelled by{" "}
+ {drawerState.selectedInvoice.user?.fname || "The sender"}{" "}
+ {drawerState.selectedInvoice.user?.lname || ""}{" "}
+
+
+ You no longer need to make payment for this invoice.
+
+
+
-
-
From
-
+ {!drawerState.selectedInvoice.isPaid && (
+
+
+ Note: This invoice was cancelled before payment was
+ completed
+
+
+ )}
+
+ )}
+
+
+
+ From
+
+
+ {drawerState.selectedInvoice.user.fname}{" "}
+ {drawerState.selectedInvoice.user.lname}
+
+
{drawerState.selectedInvoice.user.address}
-
{`${drawerState.selectedInvoice.user.fname} ${drawerState.selectedInvoice.user.lname}`}
-
+
+ {drawerState.selectedInvoice.user.city},{" "}
+ {drawerState.selectedInvoice.user.country},{" "}
+ {drawerState.selectedInvoice.user.postalcode}
+
+
{drawerState.selectedInvoice.user.email}
-
{`${drawerState.selectedInvoice.user.city}, ${drawerState.selectedInvoice.user.country} (${drawerState.selectedInvoice.user.postalcode})`}
-
-
Billed to
-
+
+
+ Bill To
+
+
+ {drawerState.selectedInvoice.client.fname}{" "}
+ {drawerState.selectedInvoice.client.lname}
+
+
{drawerState.selectedInvoice.client.address}
-
{`${drawerState.selectedInvoice.client.fname} ${drawerState.selectedInvoice.client.lname}`}
-
+
+ {drawerState.selectedInvoice.client.city},{" "}
+ {drawerState.selectedInvoice.client.country},{" "}
+ {drawerState.selectedInvoice.client.postalcode}
+
+
{drawerState.selectedInvoice.client.email}
-
{`${drawerState.selectedInvoice.client.city}, ${drawerState.selectedInvoice.client.country} (${drawerState.selectedInvoice.client.postalcode})`}
-
-
-
- Description
- QTY
- Unit Price
- Discount
- Tax
- Amount
+
+
+
+ Payment Currency
+
+
+ {drawerState.selectedInvoice.paymentToken?.logo ? (
+
+ ) : (
+
+
+
+ )}
+
+
+ {drawerState.selectedInvoice.paymentToken?.name || "Ether "}
+ {"("}
+ {drawerState.selectedInvoice.paymentToken?.symbol || "ETH"}
+ {")"}
+
+
+ {drawerState.selectedInvoice.paymentToken?.address
+ ? `${drawerState.selectedInvoice.paymentToken.address.substring(
+ 0,
+ 10
+ )}......${drawerState.selectedInvoice.paymentToken.address.substring(
+ 33
+ )}`
+ : "Native Currency"}
+
+
+
+ {drawerState.selectedInvoice.paymentToken?.address && (
+
+
+ Decimals:{" "}
+ {drawerState.selectedInvoice.paymentToken.decimals || 18}
+
+
Chain: Sepolia Testnet
+
+ )}
+
+
+
+
+ Issued:{" "}
+ {new Date(
+ drawerState.selectedInvoice.issueDate
+ ).toLocaleDateString()}
+
+
+ Due:{" "}
+ {new Date(
+ drawerState.selectedInvoice.dueDate
+ ).toLocaleDateString()}
+
+
+
+
+
+
+
+ Description
+ Qty
+ Price
+ Discount
+ Tax
+ Amount
- {drawerState.selectedInvoice?.items?.map((item, index) => (
-
-
-
- {item.description}
- {item.qty.toString()}
-
- {/* {ethers.formatUnits(item.unitPrice)} */}
- {item.unitPrice}
+
+ {drawerState.selectedInvoice.items?.map((item, index) => (
+
+ {item.description}
+ {item.qty}
+
+ {item.unitPrice}{" "}
+ {drawerState.selectedInvoice.paymentToken?.symbol}
+
+
+ {item.discount || "0"}
- {item.discount.toString()}
- {item.tax.toString()}
-
- {item.amount} ETH
- {/* {ethers.formatUnits(item.amount)} ETH */}
+
+ {item.tax || "0%"}
+
+
+ {item.amount}{" "}
+ {drawerState.selectedInvoice.paymentToken?.symbol}
-
- ))}
+ ))}
+
-
-
- {/* Fee for invoice pay : {ethers.formatUnits(fee)} ETH */}
+
- Fee for invoice pay : {parseFloat(ethers.formatUnits(fee))}{" "}
- ETH
-
-
- {" "}
- Amount: {drawerState.selectedInvoice.amountDue}{" "}
- {/* {ethers.formatUnits(drawerState.selectedInvoice.amountDue)}{" "} */}
- ETH
-
-
- Total Amount:{" "}
- {parseFloat(drawerState.selectedInvoice.amountDue) +
- parseFloat(ethers.formatUnits(fee))}{" "}
- ETH
-
+
+
+ Subtotal:
+
+ {drawerState.selectedInvoice.amountDue}{" "}
+ {drawerState.selectedInvoice.paymentToken?.symbol}
+
+
+
+ Network Fee:
+
+ {ethers.formatUnits(fee)} ETH
+
-
-
Powered by
-
+
+ Total Amount:
+
+ {drawerState.selectedInvoice.paymentToken?.symbol === "ETH"
+ ? `${(
+ parseFloat(drawerState.selectedInvoice.amountDue) +
+ parseFloat(ethers.formatUnits(fee))
+ ).toFixed(6)} ETH`
+ : `${drawerState.selectedInvoice.amountDue} ${
+ drawerState.selectedInvoice.paymentToken?.symbol
+ } + ${ethers.formatUnits(fee)} ETH`}
+
+
+
+
+ Close
+
+
+
+ Download Invoice
+
+
)}
diff --git a/frontend/src/page/SentInvoice.jsx b/frontend/src/page/SentInvoice.jsx
index f24eb7f4..95f5a188 100644
--- a/frontend/src/page/SentInvoice.jsx
+++ b/frontend/src/page/SentInvoice.jsx
@@ -6,15 +6,14 @@ import TableContainer from "@mui/material/TableContainer";
import TableHead from "@mui/material/TableHead";
import TablePagination from "@mui/material/TablePagination";
import TableRow from "@mui/material/TableRow";
+import { ChainvoiceABI } from "@/contractsABI/ChainvoiceABI";
import { BrowserProvider, Contract, ethers } from "ethers";
-import React, { useEffect, useState, useRef } from "react";
+import React, { useEffect, useState } from "react";
import { useAccount, useWalletClient } from "wagmi";
-import { ChainvoiceABI } from "../contractsABI/ChainvoiceABI";
import DescriptionIcon from "@mui/icons-material/Description";
-
import SwipeableDrawer from "@mui/material/SwipeableDrawer";
+import { useRef } from "react";
import html2canvas from "html2canvas";
-
import { LitNodeClient } from "@lit-protocol/lit-node-client";
import { decryptToString } from "@lit-protocol/encryption/src/lib/encryption.js";
import { LIT_ABILITY, LIT_NETWORK } from "@lit-protocol/constants";
@@ -23,22 +22,54 @@ import {
generateAuthSig,
LitAccessControlConditionResource,
} from "@lit-protocol/auth-helpers";
+import { ERC20_ABI } from "@/contractsABI/ERC20_ABI";
+import {
+ CircularProgress,
+ Skeleton,
+ Chip,
+ Avatar,
+ Tooltip,
+ IconButton,
+ Typography,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogContentText,
+ DialogActions,
+ Button,
+ Alert,
+} from "@mui/material";
+import PaidIcon from "@mui/icons-material/CheckCircle";
+import UnpaidIcon from "@mui/icons-material/Pending";
+import DownloadIcon from "@mui/icons-material/Download";
+import CancelIcon from "@mui/icons-material/Cancel";
+import CurrencyExchangeIcon from "@mui/icons-material/CurrencyExchange";
+import { TOKEN_PRESETS } from "@/utils/erc20_token";
const columns = [
- { id: "fname", label: "First Name", minWidth: 100 },
- { id: "lname", label: "Last Name", minWidth: 100 },
- { id: "to", label: "Receiver's Address", minWidth: 200 },
- { id: "email", label: "Email", minWidth: 170 },
- { id: "country", label: "Country", minWidth: 100 },
- { id: "city", label: "City", minWidth: 100 },
- { id: "amountDue", label: "Total Amount", minWidth: 100, align: "right" },
- { id: "isPaid", label: "Status", minWidth: 100 },
- { id: "detail", label: "Detail Invoice", minWidth: 100 },
+ { id: "fname", label: "Client", minWidth: 120 },
+ { id: "to", label: "Receiver", minWidth: 150 },
+ { id: "amountDue", label: "Amount", minWidth: 100, align: "right" },
+ { id: "status", label: "Status", minWidth: 120 },
+ { id: "date", label: "Date", minWidth: 100 },
+ { id: "actions", label: "Actions", minWidth: 150 },
];
function SentInvoice() {
const [page, setPage] = useState(0);
const [rowsPerPage, setRowsPerPage] = useState(10);
+ const { data: walletClient } = useWalletClient();
+ const { address } = useAccount();
+ const [loading, setLoading] = useState(true);
+ const [sentInvoices, setSentInvoices] = useState([]);
+ const [fee, setFee] = useState(0);
+ const [error, setError] = useState(null);
+ const [litReady, setLitReady] = useState(false);
+ const litClientRef = useRef(null);
+ const [paymentLoading, setPaymentLoading] = useState({});
+ const [networkLoading, setNetworkLoading] = useState(false);
+ const [cancelConfirmOpen, setCancelConfirmOpen] = useState(false);
+ const [invoiceToCancel, setInvoiceToCancel] = useState(null);
const handleChangePage = (event, newPage) => {
setPage(newPage);
};
@@ -46,17 +77,6 @@ function SentInvoice() {
setRowsPerPage(+event.target.value);
setPage(0);
};
-
- const { data: walletClient } = useWalletClient();
- const [sentInvoices, setSentInvoices] = useState([]);
- const [invoiceItems, setInvoiceItems] = useState([]);
- const [loading, setLoading] = useState(false);
- const [fee, setFee] = useState(0);
- const [error, setError] = useState(null);
- const { address } = useAccount();
- const [litReady, setLitReady] = useState(false);
- const litClientRef = useRef(null);
-
useEffect(() => {
const initLit = async () => {
try {
@@ -69,10 +89,9 @@ function SentInvoice() {
await client.connect();
litClientRef.current = client;
setLitReady(true);
- console.log(litClientRef.current);
}
} catch (error) {
- console.error("Error while lit client initialization:", error);
+ console.error("Error initializing Lit client:", error);
} finally {
setLoading(false);
}
@@ -81,16 +100,23 @@ function SentInvoice() {
}, []);
useEffect(() => {
- if (!walletClient || !litReady) return;
+ if (!walletClient || !address || !litReady) return;
const fetchSentInvoices = async () => {
try {
setLoading(true);
-
- // 1. Setup signer
+ setError(null);
const provider = new BrowserProvider(walletClient);
const signer = await provider.getSigner();
+ const network = await provider.getNetwork();
+ if (network.chainId != 11155111) {
+ setError(
+ `You're connected to ${network.name}. Please switch to Sepolia network to view your invoices.`
+ );
+ setLoading(false);
+ return;
+ }
// 2. Connect to Lit Node
const litNodeClient = litClientRef.current;
@@ -107,12 +133,11 @@ function SentInvoice() {
);
const res = await contract.getSentInvoices(address);
- console.log(res);
+ console.log("Raw invoices data:", res);
if (!res || !Array.isArray(res) || res.length === 0) {
console.warn("No invoices found.");
setSentInvoices([]);
- setInvoiceItems([]);
setLoading(false);
return;
}
@@ -120,89 +145,108 @@ function SentInvoice() {
const decryptedInvoices = [];
for (const invoice of res) {
- const id = invoice[0];
- const from = invoice[1].toLowerCase();
- const to = invoice[2].toLowerCase();
- const isPaid = invoice[4];
- const encryptedStringBase64 = invoice[5]; // encryptedData
- const dataToEncryptHash = invoice[6];
-
- if (!encryptedStringBase64 || !dataToEncryptHash) continue;
- const currentUserAddress = address.toLowerCase();
- if (currentUserAddress !== from && currentUserAddress !== to) {
- console.warn(
- `User ${currentUserAddress} not authorized to decrypt invoice ${id}`
- );
- continue;
- }
- const ciphertext = atob(encryptedStringBase64);
- const accessControlConditions = [
- {
- contractAddress: "",
- standardContractType: "",
- chain: "ethereum",
- method: "",
- parameters: [":userAddress"],
- returnValueTest: {
- comparator: "=",
- value: invoice[1].toLowerCase(), // from
- },
- },
- { operator: "or" },
- {
- contractAddress: "",
- standardContractType: "",
- chain: "ethereum",
- method: "",
- parameters: [":userAddress"],
- returnValueTest: {
- comparator: "=",
- value: invoice[2].toLowerCase(), // to
+ try {
+ const id = invoice[0];
+ const from = invoice[1].toLowerCase();
+ const to = invoice[2].toLowerCase();
+ const isPaid = invoice[5];
+ const isCancelled = invoice[6];
+ const encryptedStringBase64 = invoice[7];
+ const dataToEncryptHash = invoice[8];
+
+ if (!encryptedStringBase64 || !dataToEncryptHash) continue;
+
+ const currentUserAddress = address.toLowerCase();
+ if (currentUserAddress !== from && currentUserAddress !== to) {
+ console.warn(`Unauthorized access attempt for invoice ${id}`);
+ continue;
+ }
+ const ciphertext = atob(encryptedStringBase64);
+ const accessControlConditions = [
+ {
+ contractAddress: "",
+ standardContractType: "",
+ chain: "ethereum",
+ method: "",
+ parameters: [":userAddress"],
+ returnValueTest: {
+ comparator: "=",
+ value: from,
+ },
},
- },
- ];
-
- const sessionSigs = await litNodeClient.getSessionSigs({
- chain: "ethereum",
- resourceAbilityRequests: [
+ { operator: "or" },
{
- resource: new LitAccessControlConditionResource("*"),
- ability: LIT_ABILITY.AccessControlConditionDecryption,
+ contractAddress: "",
+ standardContractType: "",
+ chain: "ethereum",
+ method: "",
+ parameters: [":userAddress"],
+ returnValueTest: {
+ comparator: "=",
+ value: to,
+ },
},
- ],
- authNeededCallback: async ({
- uri,
- expiration,
- resourceAbilityRequests,
- }) => {
- const nonce = await litNodeClient.getLatestBlockhash();
- const toSign = await createSiweMessageWithRecaps({
+ ];
+
+ const sessionSigs = await litNodeClient.getSessionSigs({
+ chain: "ethereum",
+ resourceAbilityRequests: [
+ {
+ resource: new LitAccessControlConditionResource("*"),
+ ability: LIT_ABILITY.AccessControlConditionDecryption,
+ },
+ ],
+ authNeededCallback: async ({
uri,
expiration,
- resources: resourceAbilityRequests,
- walletAddress: address,
- nonce,
- litNodeClient,
- });
- return await generateAuthSig({ signer, toSign });
- },
- });
+ resourceAbilityRequests,
+ }) => {
+ const nonce = await litNodeClient.getLatestBlockhash();
+ const toSign = await createSiweMessageWithRecaps({
+ uri,
+ expiration,
+ resources: resourceAbilityRequests,
+ walletAddress: address,
+ nonce,
+ litNodeClient,
+ });
+ return await generateAuthSig({ signer, toSign });
+ },
+ });
- const decryptedString = await decryptToString(
- {
- accessControlConditions,
- chain: "ethereum",
- ciphertext,
- dataToEncryptHash,
- sessionSigs,
- },
- litNodeClient
- );
+ const decryptedString = await decryptToString(
+ {
+ accessControlConditions,
+ chain: "ethereum",
+ ciphertext,
+ dataToEncryptHash,
+ sessionSigs,
+ },
+ litNodeClient
+ );
- const parsed = JSON.parse(decryptedString);
- parsed["id"] = id;
- parsed["isPaid"] = isPaid;
- decryptedInvoices.push(parsed);
+ const parsed = JSON.parse(decryptedString);
+ parsed["id"] = id;
+ parsed["isPaid"] = isPaid;
+ parsed["isCancelled"] = isCancelled;
+ if (parsed.paymentToken?.address) {
+ const tokenInfo = TOKEN_PRESETS.find(
+ (t) =>
+ t.address.toLowerCase() ===
+ parsed.paymentToken.address.toLowerCase()
+ );
+ if (tokenInfo) {
+ parsed.paymentToken = {
+ ...parsed.paymentToken,
+ logo: tokenInfo.logo,
+ decimals: tokenInfo.decimals,
+ };
+ }
+ }
+ decryptedInvoices.push(parsed);
+ } catch (err) {
+ console.error(`Error processing invoice ${invoice[0]}:`, err);
+ }
}
setSentInvoices(decryptedInvoices);
@@ -210,14 +254,14 @@ function SentInvoice() {
setFee(fee);
} catch (error) {
console.error("Decryption error:", error);
- alert("Failed to decrypt invoice.");
} finally {
+ console.log(sentInvoices);
setLoading(false);
}
};
fetchSentInvoices();
- }, [walletClient, litReady]);
+ }, [walletClient, litReady, address]);
const [drawerState, setDrawerState] = useState({
open: false,
@@ -225,7 +269,6 @@ function SentInvoice() {
});
const toggleDrawer = (invoice) => (event) => {
- console.log(invoice);
if (
event &&
event.type === "keydown" &&
@@ -239,313 +282,666 @@ function SentInvoice() {
});
};
- const contentRef = useRef();
const handlePrint = async () => {
- const element = contentRef.current;
- if (!element) {
- return;
- }
+ const element = document.getElementById("invoice-print");
+ if (!element) return;
- const canvas = await html2canvas(element, {
- scale: 2,
- });
+ const canvas = await html2canvas(element, { scale: 2 });
const data = canvas.toDataURL("image/png");
- // download feature (implement later on)
- // const pdf = new jsPDF({
- // orientation: "portrait",
- // unit: "px",
- // format: "a4",
- // });
-
- // const imgProperties = pdf.getImageProperties(data);
- // const pdfWidth = pdf.internal.pageSize.getWidth();
+ const link = document.createElement("a");
+ link.download = `invoice-${drawerState.selectedInvoice.id}.png`;
+ link.href = data;
+ link.click();
+ };
+ const handleCancelInvoice = async (invoiceId) => {
+ try {
+ setPaymentLoading((prev) => ({ ...prev, [invoiceId]: true }));
+ const provider = new BrowserProvider(walletClient);
+ const signer = await provider.getSigner();
+ const contract = new Contract(
+ import.meta.env.VITE_CONTRACT_ADDRESS,
+ ChainvoiceABI,
+ signer
+ );
+
+ const tx = await contract.cancelInvoice(invoiceId);
+ await tx.wait();
+ setSentInvoices((prev) =>
+ prev.map((inv) =>
+ inv.id === invoiceId ? { ...inv, isCancelled: true } : inv
+ )
+ );
+
+ toast.success("Invoice cancelled successfully");
+ } catch (error) {
+ console.error("Cancellation failed:", error);
+ toast.error("Failed to cancel invoice");
+ } finally {
+ setPaymentLoading((prev) => ({ ...prev, [invoiceId]: false }));
+ }
+ };
+ const switchNetwork = async () => {
+ try {
+ setNetworkLoading(true);
+ await window.ethereum.request({
+ method: "wallet_switchEthereumChain",
+ params: [{ chainId: "0xaa36a7" }], // Sepolia chain ID
+ });
+ setError(null);
+ } catch (error) {
+ console.error("Network switch failed:", error);
+ alert("Failed to switch network. Please switch to Sepolia manually.");
+ } finally {
+ setNetworkLoading(false);
+ }
+ };
- // const pdfHeight = (imgProperties.height * pdfWidth) / imgProperties.width;
+ const formatAddress = (address) => {
+ return `${address.substring(0, 10)}...${address.substring(
+ address.length - 10
+ )}`;
+ };
- // pdf.addImage(data, "PNG", 0, 0, pdfWidth, pdfHeight);
- // pdf.save("invoice.pdf");
+ const formatDate = (issueDate) => {
+ const date = new Date(issueDate);
+ return date.toLocaleString();
};
return (
-
-
Your Sent Invoice Request
-
- {loading ? (
- Loading invoices...
- ) : error ? (
- {error}
- ) : sentInvoices.length === 0 ? (
- No invoices found
- ) : (
- <>
-
-
-
-
- {columns.map((column) => (
-
- {column.label}
-
- ))}
-
-
-
- {sentInvoices
- .slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
- .map((invoice, index) => (
-
- {columns.map((column) => {
- const value = invoice?.client[column.id];
- if (column.id === "to") {
- return (
-
- {invoice.client.address
- ? `${invoice.client.address.substring(
- 0,
- 10
- )}...${invoice.client.address.substring(
- invoice.client.address.length - 10
- )}`
- : "N/A"}
-
- );
- }
- if (column.id === "amountDue") {
- return (
-
- {invoice.amountDue} ETH
-
- );
- }
- if (column.id === "isPaid") {
- return (
-
-
- {invoice.isPaid ? "Paid" : "Not Paid"}
-
-
- );
- }
- if (column.id === "detail") {
- return (
-
+
+
+
+
Sent Invoices
+
+ {error && (
+
+ {networkLoading ? (
+ <>
+
+ Switching...
+ >
+ ) : (
+ "Switch to Sepolia"
+ )}
+
+ )}
+
+
+
+ {loading ? (
+
+
+
+
+
+ {[...Array(5)].map((_, i) => (
+
+ ))}
+
+ ) : error ? (
+
+ ) : sentInvoices.length === 0 ? (
+
+
+
+
+ No Invoices Found
+
+
+ You haven't sent any invoices yet.
+
+
+
+ ) : (
+ <>
+
+
+
+
+ {columns.map((column) => (
+
+ {column.label}
+
+ ))}
+
+
+
+ {sentInvoices
+ .slice(
+ page * rowsPerPage,
+ page * rowsPerPage + rowsPerPage
+ )
+ .map((invoice) => (
+
+ {/* Client Column */}
+
+
+
-
+
+
+ {invoice.client?.fname}{" "}
+ {invoice.client?.lname}
+
+
+ {invoice.client?.email}
+
+
+
+
+
+ {/* Sender Column */}
+
+
+
+ {formatAddress(invoice.client?.address)}
+
+
+
+
+ {/* Amount Column */}
+
+
+ {invoice.paymentToken?.logo ? (
+
+ ) : (
+
+ )}
+
+ {invoice.amountDue}{" "}
+ {invoice.paymentToken?.symbol}
+
+
+
+
+ {/* Status Column */}
+
+ {invoice.isCancelled ? (
+ }
+ label="Cancelled"
+ color="error"
+ size="small"
+ variant="outlined"
+ />
+ ) : invoice.isPaid ? (
+ }
+ label="Paid"
+ color="success"
+ size="small"
+ variant="outlined"
+ />
+ ) : (
+ }
+ label="Pending"
+ color="warning"
+ size="small"
+ variant="outlined"
+ />
+ )}
+
+
+ {/* Date Column */}
+
+
+ {formatDate(invoice.issueDate)}
+
+
+
+
+
+ {!invoice.isPaid && !invoice.isCancelled && (
+
+ {
+ setInvoiceToCancel(invoice);
+ setCancelConfirmOpen(true);
+ }}
+ sx={{
+ backgroundColor: "#fee2e2",
+ "&:hover": { backgroundColor: "#fecaca" },
+ }}
+ >
+
+
+
+ )}
+
+
-
-
-
- );
- }
- return (
-
- {value}
-
- );
- })}
-
- ))}
-
-
-
-
-
+
+
+
+
+
+ ))}
+
+
+
+
- >
- )}
-
-
+ "& .MuiSelect-icon": {
+ color: "#64748b",
+ },
+ }}
+ />
+ >
+ )}
+
+
+ {/* Invoice Detail Drawer */}
{drawerState.selectedInvoice && (
-
-
-
-
-
-
- Issued by {drawerState.selectedInvoice.issueDate}
-
-
- Payment Due by {drawerState.selectedInvoice.dueDate}
+
+
+
+ Powered by
+
+
+
+ Cha
+ in
+ voice
-
-
- Invoice # {drawerState.selectedInvoice.id.toString()}
-
+
+
INVOICE
+
+ #{drawerState.selectedInvoice.id.toString().padStart(6, "0")}
+
+
+ {drawerState.selectedInvoice.isCancelled ? (
+ }
+ />
+ ) : drawerState.selectedInvoice.isPaid ? (
+ }
+ />
+ ) : (
+ }
+ />
+ )}
+
+
+
+ {drawerState.selectedInvoice.isCancelled && (
+
+
+
+
+ You Cancelled This Invoice
+
+
+ {drawerState.selectedInvoice.client?.fname ||
+ "The recipient"}{" "}
+ {drawerState.selectedInvoice.client?.lname || ""}{" "}
+ has been notified and cannot pay this invoice.
+
+
+
-
-
From
-
+ {drawerState.selectedInvoice.isPaid && (
+
+
+ Note: Payment was already completed before cancellation
+
+
+ )}
+
+ )}
+
+
+
+ From
+
+
+ {drawerState.selectedInvoice.user.fname}{" "}
+ {drawerState.selectedInvoice.user.lname}
+
+
{drawerState.selectedInvoice.user.address}
-
{`${drawerState.selectedInvoice.user.fname} ${drawerState.selectedInvoice.user.lname}`}
-
+
+ {drawerState.selectedInvoice.user.city},{" "}
+ {drawerState.selectedInvoice.user.country},{" "}
+ {drawerState.selectedInvoice.user.postalcode}
+
+
{drawerState.selectedInvoice.user.email}
-
{`${drawerState.selectedInvoice.user.city}, ${drawerState.selectedInvoice.user.country} (${drawerState.selectedInvoice.user.postalcode})`}
-
-
Billed to
-
+
+
+ Bill To
+
+
+ {drawerState.selectedInvoice.client.fname}{" "}
+ {drawerState.selectedInvoice.client.lname}
+
+
{drawerState.selectedInvoice.client.address}
-
{`${drawerState.selectedInvoice.client.fname} ${drawerState.selectedInvoice.client.lname}`}
-
+
+ {drawerState.selectedInvoice.client.city},{" "}
+ {drawerState.selectedInvoice.client.country},{" "}
+ {drawerState.selectedInvoice.client.postalcode}
+
+
{drawerState.selectedInvoice.client.email}
-
{`${drawerState.selectedInvoice.client.city}, ${drawerState.selectedInvoice.client.country} (${drawerState.selectedInvoice.client.postalcode})`}
-
-
-
- Description
- QTY
- Unit Price
- Discount
- Tax
- Amount
+
+
+
+ Payment Currency
+
+
+ {drawerState.selectedInvoice.paymentToken?.logo ? (
+
+ ) : (
+
+
+
+ )}
+
+
+ {drawerState.selectedInvoice.paymentToken?.name || "Ether "}
+ {"("}
+ {drawerState.selectedInvoice.paymentToken?.symbol || "ETH"}
+ {")"}
+
+
+ {drawerState.selectedInvoice.paymentToken?.address
+ ? `${drawerState.selectedInvoice.paymentToken.address.substring(
+ 0,
+ 10
+ )}......${drawerState.selectedInvoice.paymentToken.address.substring(
+ 33
+ )}`
+ : "Native Currency"}
+
+
+
+ {drawerState.selectedInvoice.paymentToken?.address && (
+
+
Chain: Sepolia Testnet
+
+ )}
+
+
+
+
+ Issued:{" "}
+ {new Date(
+ drawerState.selectedInvoice.issueDate
+ ).toLocaleDateString()}
+
+
+ Due:{" "}
+ {new Date(
+ drawerState.selectedInvoice.dueDate
+ ).toLocaleDateString()}
+
+
+
+
+
+
+
+
+ Description
+ Qty
+ Price
+ Discount
+ Tax
+ Amount
- {drawerState.selectedInvoice?.items?.map((item, index) => (
-
-
- {item.description}
- {item.qty.toString()}
-
- {item.unitPrice}
+
+ {drawerState.selectedInvoice.items?.map((item, index) => (
+
+ {item.description}
+ {item.qty}
+
+ {item.unitPrice}{" "}
+ {drawerState.selectedInvoice.paymentToken?.symbol}
+
+
+ {item.discount || "0"}
- {item.discount.toString()}
- {item.tax.toString()}
-
- {item.amount}
+
+ {item.tax || "0%"}
+
+
+ {item.amount}{" "}
+ {drawerState.selectedInvoice.paymentToken?.symbol}
-
- ))}
+ ))}
+
-
-
- Fee for invoice pay : {parseFloat(ethers.formatUnits(fee))}{" "}
- ETH
-
-
- {" "}
- Amount:{" "}
- {drawerState.selectedInvoice.amountDue} ETH
-
-
- Total Amount:{" "}
- {parseFloat(drawerState.selectedInvoice.amountDue) +
- parseFloat(ethers.formatUnits(fee))}{" "}
- ETH
-
+
+
+
+
+ Subtotal:
+
+ {drawerState.selectedInvoice.amountDue}{" "}
+ {drawerState.selectedInvoice.paymentToken?.symbol}
+
+
+
+ Network Fee:
+
+ {ethers.formatUnits(fee)} ETH
+
-
-
Powered by
-
+
+ Total Amount:
+
+ {drawerState.selectedInvoice.paymentToken?.symbol === "ETH"
+ ? `${(
+ parseFloat(drawerState.selectedInvoice.amountDue) +
+ parseFloat(ethers.formatUnits(fee))
+ ).toFixed(6)} ETH`
+ : `${drawerState.selectedInvoice.amountDue} ${
+ drawerState.selectedInvoice.paymentToken?.symbol
+ } + ${ethers.formatUnits(fee)} ETH`}
+
+
+
+
+ Close
+
+
+
+ Download Invoice
+
+
)}
+
setCancelConfirmOpen(false)}
+ aria-labelledby="alert-dialog-title"
+ aria-describedby="alert-dialog-description"
+ >
+
+ Confirm Invoice Cancellation
+
+
+
+
+ You're about to cancel this invoice sent to{" "}
+
+ {invoiceToCancel?.client.fname} {invoiceToCancel?.client.lname}
+
+ .
+
+ {invoiceToCancel?.isPaid ? (
+
+ Payment was already received - cancelling will not reverse the
+ transaction
+
+ ) : (
+
+ This action cannot be undone
+
+ )}
+
+
+
+ setCancelConfirmOpen(false)}>
+ {" "}
+ No,Keep Invoice Active
+
+ {
+ handleCancelInvoice(invoiceToCancel.id);
+ setCancelConfirmOpen(false);
+ }}
+ color="error"
+ autoFocus
+ >
+ Yes, Cancel It
+
+
+
);
}
diff --git a/frontend/src/page/Treasure.jsx b/frontend/src/page/Treasure.jsx
index e2cbaf7b..78549639 100644
--- a/frontend/src/page/Treasure.jsx
+++ b/frontend/src/page/Treasure.jsx
@@ -10,8 +10,11 @@ import {
Banknote,
Key,
Wallet,
- DollarSignIcon,
+ DollarSign,
+ Settings,
+ ChevronRight,
} from "lucide-react";
+import { motion } from "framer-motion";
const Treasure = () => {
const [treasureAmount, setTreasureAmount] = useState(0);
@@ -21,9 +24,11 @@ const Treasure = () => {
fetch: false,
setAddress: false,
withdraw: false,
+ feeUpdate: false,
});
const [treasuryAddress, setTreasuryAddress] = useState("");
const [newTreasuryAddress, setNewTreasuryAddress] = useState("");
+ const [newFee, setNewFee] = useState("");
useEffect(() => {
const fetchTreasureAmount = async () => {
@@ -107,77 +112,129 @@ const Treasure = () => {
}
};
+ const handleUpdateFee = async () => {
+ if (!newFee || isNaN(newFee)) {
+ alert("Please enter a valid fee amount");
+ return;
+ }
+ try {
+ if (!walletClient) return;
+ setLoading((prev) => ({ ...prev, feeUpdate: true }));
+ const provider = new BrowserProvider(walletClient);
+ const signer = await provider.getSigner();
+ const contract = new Contract(
+ import.meta.env.VITE_CONTRACT_ADDRESS,
+ ChainvoiceABI,
+ signer
+ );
+ const tx = await contract.setFeeAmount(
+ ethers.parseUnits(newFee, "ether")
+ );
+ await tx.wait();
+ const updatedFee = await contract.fee();
+ setFee(ethers.formatUnits(updatedFee));
+ setNewFee("");
+ alert("Fee updated successfully!");
+ } catch (error) {
+ console.error("Error updating fee:", error);
+ alert(error.message || "Failed to update fee");
+ } finally {
+ setLoading((prev) => ({ ...prev, feeUpdate: false }));
+ }
+ };
+
return (
-
-
+
+ {/* Treasury Overview Card */}
+
-
-
+
+
-
+
-
+
Treasury Vault
-
-
-
-
- Current Balance:
-
-
+
+
+
+
+
+ Current Balance
+
+
{loading.fetch ? (
) : (
- `${treasureAmount} cBTC`
+ `${treasureAmount} ETH`
)}
-
-
-
- Fee Per Transaction:
-
-
+
+
+
+
+ Transaction Fee
+
+
{loading.fetch ? (
) : (
- `${fee} cBTC`
+ `${fee} ETH`
)}
-
-
-
- Admin Access Only
-
+
+
+
+ Admin Access Only
-
+
- {/* Content */}
-
-
- Treasury Controls
-
+ {/* Control Panel */}
+
+
+
+
+ Treasury Controls
+
+
- Secure management of platform funds and treasury settings
+ Manage platform funds and configuration settings
-
-
-
-
Current Treasury
+ {/* Treasury Address Section */}
+
+
+
+
+ Treasury Address
+
-
+
{loading.fetch ? (
@@ -188,61 +245,93 @@ const Treasure = () => {
)}
+
+
setNewTreasuryAddress(e.target.value)}
+ className="bg-gray-800 border-gray-700 text-white font-mono"
+ />
+
+ {loading.setAddress ? (
+
+ ) : (
+
+ Update Address
+
+ )}
+
+
+ Requires contract owner privileges
+
+
+ {/* Fee Adjustment Section */}
-
-
- Update Treasury Address
+
+
+ Transaction Fee
-
+
setNewTreasuryAddress(e.target.value)}
- className="flex-1 bg-gray-800 border-gray-700 text-white font-mono text-sm"
+ type="number"
+ placeholder={`Current: ${fee} ETH`}
+ value={newFee}
+ onChange={(e) => setNewFee(e.target.value)}
+ className="bg-gray-800 border-gray-700 text-white font-mono"
/>
- {loading.setAddress ? (
-
+ {loading.feeUpdate ? (
+
) : (
- "Update"
+
+ Update Fee
+
)}
+
+ Applies to all future transactions
+
-
- Requires contract owner privileges
-
- {/* Withdraw */}
+ {/* Withdrawal Section */}
-
-
Funds Withdrawal
+
+ Withdraw Funds
{loading.withdraw ? (
- ) : null}
- Transfer {treasureAmount} cBTC
+ ) : (
+
+ Withdraw {treasureAmount} ETH{" "}
+
+
+ )}
- Will be sent to the current treasury address
+ Funds will be sent to the current treasury address
-
-
+
+
);
};
diff --git a/frontend/src/utils/erc20_token.js b/frontend/src/utils/erc20_token.js
new file mode 100644
index 00000000..c347db7c
--- /dev/null
+++ b/frontend/src/utils/erc20_token.js
@@ -0,0 +1,709 @@
+// {
+// name: "Chainvoice",
+// symbol: "CIN",
+// address: "0xB5E9C6e57C9d312937A059089B547d0036c155C7",
+// decimals: 18,
+// },
+
+export const TOKEN_PRESETS = [
+ {
+ name: "Ethereum Mainnet",
+ symbol: "ETH",
+ address: "0x0000000000000000000000000000000000000000",
+ decimals: 18,
+ logo: "/tokenImages/eth.png",
+ },
+ {
+ name: "Tether USD",
+ symbol: "USDT",
+ address: "0xdac17f958d2ee523a2206206994597c13d831ec7",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x636c00eefcf239bf56cc07dfc8ec2a0d26ecc765805f8d9e62c866b0164ccb62.png",
+ },
+ {
+ name: "BNB",
+ symbol: "BNB",
+ address: "0xb8c77482e45f1f44de1745f52c74426c631bdd52",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x533cc6c47bad45528f615b1f66023b1f7c429d4304e2d1ddb84918077e8ead8b.png",
+ },
+ {
+ name: "USD Coin",
+ symbol: "USDC",
+ address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xda4a7aa3c2c6966c74c4a8446b6348c3e397491e14b2874719192fa5a4c71cab.png",
+ },
+ {
+ name: "Liquid staked Ether 2.0",
+ symbol: "stETH",
+ address: "0xae7ab96520de3a18e5e111b5eaab095312d7fe84",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x4e05bd4f46441ab8812874ddb4a8e6ace80ab398e09c07086a93e60ff4b7f4bb.png",
+ },
+ {
+ name: "Wrapped BTC",
+ symbol: "WBTC",
+ address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xbd2860a79e7bc89d86922c8a80cdeb9a32a0082045fb0da24c31e3871916e0d3.png",
+ },
+ {
+ name: "Wrapped liquid staked Ether 2.0",
+ symbol: "wstETH",
+ address: "0x7f39c581f595b53c5cb19bd0b3f8da6c935e2ca0",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x3454daef9545c526928d9a01030752ff1995f65f4ec044a0b127b16f913e4065.png",
+ },
+ {
+ name: "ChainLink Token",
+ symbol: "LINK",
+ address: "0x514910771af9ca656af840dff83e8264ecf986ca",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x0d5bccf38448bc46b1a49419cfa43d1d569dbce172c214bacde2d2e22b005dc0.png",
+ },
+ {
+ name: "Bitfinex LEO Token",
+ symbol: "LEO",
+ address: "0x2af5d2ad76741191d15dfe7bf6ac92d4bd912ca3",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x0f5926985ff75e8c8caaa95501ea575923c317bb847421d4d00dbf5964a1feea.png",
+ },
+ {
+ name: "USDS Stablecoin",
+ symbol: "USDS",
+ address: "0xdc035d45d973e3ec169d2276ddab16f1e407384f",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xb547d1b9a9e8f112d440784ffaed3dbdc433a70820a359614664ec981752ae4e.png",
+ },
+ {
+ name: "SHIBA INU",
+ symbol: "SHIB",
+ address: "0x95ad61b0a150d79219dcf64e1e6cc01f0b64c4ce",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x934042eb1c76a54280d40bebcc15138690c0b719eaaa4bd07529ce5d8dede7e6.png",
+ },
+ {
+ name: "EtherFi wrapped ETH",
+ symbol: "weETH",
+ address: "0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x8a7377a984da522f6099b1a7d8314a6fb527d07cfd637ababfbab4b94bfe693a.png",
+ },
+ {
+ name: "Wrapped TON Coin",
+ symbol: "TONCOIN",
+ address: "0x582d872a1b094fc48f5de31d3b73f2d9be47def1",
+ decimal: "9",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x5b41e3f6b7eaa6d3ae160a0d3525678c720b1948ca6734dd4d998f2d6310ff1b.png",
+ },
+ {
+ name: "Wrapped Ether",
+ symbol: "WETH",
+ address: "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x7f71adae0bcc9186e966ba6fe0be5dfd303f6ccba90512991f91babf1f333625.png",
+ },
+ {
+ name: "WBT",
+ symbol: "WBT",
+ address: "0x925206b8a707096ed26ae47c84747fe0bb734f59",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xdeea9fdad65441cbb8ed321961502ca3f60ce0e6939d6f3761a6c1d4989a61ec.png",
+ },
+ {
+ name: "Coinbase Wrapped BTC",
+ symbol: "cbBTC",
+ address: "0xcbb7c0000ab88b473b1f5afd9ef808440eed33bf",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x12601a638d95d1bed9ee4d1973daad3d89925f85ea42a09951daceb9494645a6.png",
+ },
+ {
+ name: "USDe",
+ symbol: "USDe",
+ address: "0x4c9edd5852cd905f086c759e8383e09bff1e68b3",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x26c646b32ea6353a192f6c760b97beb706772d0e28099f504520f70a31e3a466.png",
+ },
+ {
+ name: "BitgetToken",
+ symbol: "BGB",
+ address: "0x19de6b897ed14a376dda0fe53a5420d2ac828a28",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xef3652281dd019ae4dd24ec58d13d40c6d0115eb6ed972108808e361bd9db3e1.png",
+ },
+ {
+ name: "Uniswap",
+ symbol: "UNI",
+ address: "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xc53061256eb8145b0a928ac83ee76b055f7c563ad686521367bd16dd20ec3bed.png",
+ },
+ {
+ name: "Aave Token",
+ symbol: "AAVE",
+ address: "0x7fc66500c84a76ad7e9c93437bfc5ac33e2ddae9",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xeab69f3d8298f4b4b826af873d42063dbb05350742033769837aa0be6fbb9b64.png",
+ },
+ {
+ name: "Pepe",
+ symbol: "PEPE",
+ address: "0x6982508145454ce325ddbe47a25d4ec3d2311933",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x673e4e814b3ed3008059e214b80fba443dae310acc14e66f707360067b4424a9.png",
+ },
+ {
+ name: "Dai Stablecoin",
+ symbol: "DAI",
+ address: "0x6b175474e89094c44da98b954eedeac495271d0f",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xea5be93d23c21846ac28d0096ce859aceaac4471a4492818d06c0c5a5e540644.png",
+ },
+ {
+ name: "Staked USDe",
+ symbol: "sUSDe",
+ address: "0x9d39a5de30e57443bff2a8307a4256c8797a3497",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x3abf76c30b11bc875bc5460a1f9b52e9d8a203f93c1983eb395afc5a8ba7027f.png",
+ },
+ {
+ name: "CRO",
+ symbol: "CRO",
+ address: "0xa0b73e1ff0b80914ab6fe0444e65848c4c34450b",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x25095b588aae7d80740f215b6e69966926d3a8994f141e1593a5c3294f042297.png",
+ },
+ {
+ name: "OKB",
+ symbol: "OKB",
+ address: "0x75231f58b43240c9718dd58b4967c5114342a86c",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x80ca2e5c6480ba7147551c1327ac2bfd1214607e90348784d37d84d724b8c5e5.png",
+ },
+ {
+ name: "BlackRock USD Institutional Digital Liquidity Fund",
+ symbol: "BUIDL",
+ address: "0x7712c34205737192402172409a8f7ccef8aa2aec",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xe7568c777cc874c0f00c7528f1c03bc2d9c51ede922116ce57e0941c8b0100b8.png",
+ },
+ {
+ name: "NEAR",
+ symbol: "NEAR",
+ address: "0x85f17cf997934a597031b2e18a9ab6ebd4b9f6a4",
+ decimal: "24",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xc75188cae4539f032ee7e1174ba4c7c9154e1dfbfb855c861f96efaab6ec7258.png",
+ },
+ {
+ name: "Ondo",
+ symbol: "ONDO",
+ address: "0xfaba6f8e4a5e8ab82f62fe7c39859fa577269be3",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x1fb261c892aa9b241652d883258be36cc4b87a6f34674e7ea7dc334523b80121.png",
+ },
+ {
+ name: "Savings USDS",
+ symbol: "sUSDS",
+ address: "0xa3931d71877c0e7a3148cb7eb4463524fec27fbd",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xd3a649f8632bc78ce570ce63e898c6b1b0e13082bdfd528f82d46d27b8c09114.png",
+ },
+ {
+ name: "World Liberty Financial USD",
+ symbol: "USD1",
+ address: "0x8d0d000ee44948fc98c9b98a4fa4921476f08b0d",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xf53fefeddd8477c2879e714bfae64d77fd05a5099c91d3a88502f4b404b5a710.png",
+ },
+ {
+ name: "Mantle",
+ symbol: "MNT",
+ address: "0x3c3a81e81dc49a522a592e7622a7e711c06bf354",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x22f5817a14887c89254d451c8f2fbcd605d6c9b922fa1897bd0b83f9b335a56b.png",
+ },
+ {
+ name: "Fasttoken",
+ symbol: "FTN",
+ address: "0xaedf386b755465871ff874e3e37af5976e247064",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x53ff0289d0573bc4b047b0fd00e4d7a0b724533af5400f544009f4c503308b5e.png",
+ },
+ {
+ name: "GateChainToken",
+ symbol: "GT",
+ address: "0xe66747a101bff2dba3697199dcce5b743b454759",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x28dee88c251fffa5e1c340e8ddd0689c0d1d0b5d225d47ef709a30aed05879c9.png",
+ },
+ {
+ name: "Polygon Ecosystem Token",
+ symbol: "POL",
+ address: "0x455e53cbb86018ac2b8092fdcd39d8444affc3f6",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xf5dfa373e5ab92008519c13cddc2505a8679c49cf490ba06ba8365936a06701b.png",
+ },
+ {
+ name: "Fetch",
+ symbol: "FET",
+ address: "0xaea46a60368a7bd060eec7df8cba43b7ef41ad85",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x86707a37ebedf7ec6fc725027f04d0c38cd720d0dc38795877093b97613afbd8.png",
+ },
+ {
+ name: "ENA",
+ symbol: "ENA",
+ address: "0x57e114b691db790c35207b2e685d4a43181e6061",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x90dde53b21e0318435509c0e32190cf31da0971ba874f695b02107f992c9fdc5.png",
+ },
+ {
+ name: "SKY Governance Token",
+ symbol: "SKY",
+ address: "0x56072c95faa701256059aa122697b133aded9279",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x1f6ec6ef067dc18c953e2e43c86923a53b9c9a966166502b3f3c4bb463eed240.png",
+ },
+ {
+ name: "Render Token",
+ symbol: "RNDR",
+ address: "0x6de037ef9ad2725eb40118bb1702ebb27e4aeb24",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x6e69899378586303df5fcb70a6488e60828c68706fd1b83a8954853fe40222f2.png",
+ },
+ {
+ name: "Arbitrum",
+ symbol: "ARB",
+ address: "0xb50721bcf8d664c30412cfbc6cf7a15145234ad1",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xa11a48d3dac41fa9f07bee709903fbf6405f7ce48c6e6dc797e532efeda337d9.png",
+ },
+ {
+ name: "Lombard Staked Bitcoin",
+ symbol: "LBTC",
+ address: "0x8236a87084f8b84306f72007f36f2618a5634494",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x3e5e975c15cd33f005ecaa28f7e23cddb1af50d3f85090fdf28e80c77e089a29.png",
+ },
+ {
+ name: "Quant",
+ symbol: "QNT",
+ address: "0x4a220e6096b25eadb88358cb44068a3248254675",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xae8315d6bbe7c190cc699b64db3ecdbc0bce3db790732e7bb67718b58e3c0426.png",
+ },
+ {
+ name: "Bonk",
+ symbol: "Bonk",
+ address: "0x1151cb3d861920e07a38e03eead12c32178567f6",
+ decimal: "5",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x35fcbf9794295309281d4c0cc759e240a586111e49cd6cdf0cb82499c37dacc0.png",
+ },
+ {
+ name: "Worldcoin",
+ symbol: "WLD",
+ address: "0x163f8c2467924be0ae7b5347228cabf260318753",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xb444942fc451b3f471944b51d9be5ada722671cba9b9bce652f95ed1d01ef387.png",
+ },
+ {
+ name: "USDtb",
+ symbol: "USDtb",
+ address: "0xc139190f447e929f090edeb554d95abb8b18ac1c",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x80db354588d993be46ec08fdadf0fe3954140e29d185b5c75217ac9556f06420.png",
+ },
+ {
+ name: "First Digital USD",
+ symbol: "FDUSD",
+ address: "0xc5f0f7b66764f6ec8c8dff7ba683102295e16409",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x3dba518552999dd3a3b923b18ce39deba017998d7862bffaaabf10f3ebb74470.png",
+ },
+ {
+ name: "SPX6900",
+ symbol: "SPX",
+ address: "0xe0f63a424a4439cbe457d80e4f4b51ad25b2c56c",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x7c69f4632587621b26bd220686a2bd668f1fdf1937b28167d08b075e9d9da281.png",
+ },
+ {
+ name: "rsETH",
+ symbol: "rsETH",
+ address: "0xa1290d69c65a6fe4df752f95823fae25cb99e5a7",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x4d7847e6cfdf7a7b5d1b9d6c7bfc63e067d14ff2dfc6aac731302dec0f16f5a0.png",
+ },
+ {
+ name: "Rocket Pool ETH",
+ symbol: "rETH",
+ address: "0xae78736cd615f374d3085123a210448e74fc6393",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x9ae7abade50f86dc63e72cf2add44c92ab2d3950f42245cb76ffe7dc2e77cb6d.png",
+ },
+ {
+ name: "Nexo",
+ symbol: "NEXO",
+ address: "0xb62132e35a6c13ee1ee0f84dc5d40bad8d815206",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xaf742d00a2e8d58b2c9341fd73f42cfc2f2e5ae39005533c2aa8d1eeed0a489d.png",
+ },
+ {
+ name: "Injective Token",
+ symbol: "INJ",
+ address: "0xe28b3b32b6c345a34ff64674606124dd5aceca30",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x5aabea3f7de09d6b2a3e861cd97902ba0d341e8f5daa71c9e9ede234d56cf9a5.png",
+ },
+ {
+ name: "mETH",
+ symbol: "mETH",
+ address: "0xd5f7838f5c461feff7fe49ea5ebaf7728bb0adfa",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x65a213ed5544f36d32261e2f1b45a2aa8ce18e674c788687bafbe1d7b303acac.png",
+ },
+ {
+ name: "Staked ETH",
+ symbol: "osETH",
+ address: "0xf1c9acdc66974dfb6decb12aa385b9cd01190e38",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xfb9cdd2c50e3cf5ad5452714caaa98a8076975a9bd07d69068f6be235fe814aa.png",
+ },
+ {
+ name: "Solv BTC",
+ symbol: "SolvBTC",
+ address: "0x7a56e1c57c7475ccf742a1832b028f0456652f97",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xaab141f44b3f0d76f3315866e19ef7631d74f58c22916af091896824b59e3496.png",
+ },
+ {
+ name: "Tokenize Emblem",
+ symbol: "TKX",
+ address: "0x667102bd3413bfeaa3dffb48fa8288819e480a88",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xf7fed58cf6ffda940c013bbb37bfbf319c0c155f54a2a9073156735c0c55a3f0.png",
+ },
+ {
+ name: "Virtual Protocol",
+ symbol: "VIRTUAL",
+ address: "0x44ff8620b8ca30902395a7bd3f2407e1a091bf73",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xab89b951d480f6a2548f6fd2abfe8f13ecc4189fd811cd44c7ff41fa8b5cb1c6.png",
+ },
+ {
+ name: "Syrup USDC",
+ symbol: "syrupUSDC",
+ address: "0x80ac24aa929eaf5013f6436cda2a7ba190f5cc0b",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xb7f804c79a04a9b48473c29cdfc51e9780a52623c5672abb8a48d4056d17beb6.png",
+ },
+ {
+ name: "Paxos Gold",
+ symbol: "PAXG",
+ address: "0x45804880de22913dafe09f4980848ece6ecbaf78",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xb95043411156e0cf0a718f9938a17c09a077d3bb7c2b23e87086388a50e0ad69.png",
+ },
+ {
+ name: "PayPal USD",
+ symbol: "PYUSD",
+ address: "0x6c3ea9036406852006290770bedfcaba0e23a0e8",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x61d41978836bfe9cd6810f33bc4833cdde140fbfe1ddd5d5a0cf23d80d9e3c61.png",
+ },
+ {
+ name: "FLOKI",
+ symbol: "FLOKI",
+ address: "0xcf0c122c6b73ff809c693db761e7baebe62b6a2e",
+ decimal: "9",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xef24c2a03a5e2659fe073768e7b266215411dee6bb50bfa6ae88e0b0b3ed5915.png",
+ },
+ {
+ name: "Graph Token",
+ symbol: "GRT",
+ address: "0xc944e90c64b2c07662a292be6244bdf05cda44a7",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xbcf1ff7bcf39ea3fdc2944b48e335f8a016ca1390c2f9ab3375d592c2a7a284e.png",
+ },
+ {
+ name: "clBTC",
+ symbol: "clBTC",
+ address: "0xe7ae30c03395d66f30a26c49c91edae151747911",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xf5890b52fc0e86a586337ccc8e49e4f5f512ab5c00778f5642b3de7cdcf66d4b.png",
+ },
+ {
+ name: "Tether Gold",
+ symbol: "XAUt",
+ address: "0x68749665ff8d2d112fa859aa293f07a622782f38",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xeb1e86b05e387d0ba5081ba78b6d6e42043b36555e443a1293f0bc476afa124c.png",
+ },
+ {
+ name: "Immutable X",
+ symbol: "IMX",
+ address: "0xf57e7e7c23978c3caec3c3548e3d615c346e79ff",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x582820f7558c1a76a1edf7e58df994e265f323bf4020d62d344093e9c176154b.png",
+ },
+ {
+ name: "PancakeSwap Token",
+ symbol: "Cake",
+ address: "0x152649ea73beab28c5b49b26eb48f7ead6d4c898",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x11d48fd825024aa8cfa4d43cc6bd330b0e6a21d5e265a0fbd3d5bb255eb9e69d.png",
+ },
+ {
+ name: "Liquid Staked ETH",
+ symbol: "LsETH",
+ address: "0x8c1bed5b9a0928467c9b1341da1d7bd5e10b6549",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x0d8f81393bb459317dac5773b83bb2d790b8a4567b58daf941ea06ff69a56fa9.png",
+ },
+ {
+ name: "Curve DAO Token",
+ symbol: "CRV",
+ address: "0xd533a949740bb3306d119cc777fa900ba034cd52",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xc6a36535595fc2a85d6c076ec06df02d3cc28d2399b409c7a7ae58e04367d976.png",
+ },
+ {
+ name: "Ondo Short-Term U.S. Government Bond Fund",
+ symbol: "OUSG",
+ address: "0x1b19c19393e2d034d8ff31ff34c81252fcbbee92",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x368c45405136c537b8cd38c87e5495d88399b388e15a8c05065b548b8de5c7a0.png",
+ },
+ {
+ name: "Superstate Short Duration US Government Securities Fund",
+ symbol: "USTB",
+ address: "0x43415eb6ff9db7e26a15b704e7a3edce97d31c4e",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x3a42a31b349dbcc4545784c9083717db6eb96117bd5e046e9c90f8a5121d3fb5.png",
+ },
+ {
+ name: "USDX",
+ symbol: "USDX",
+ address: "0xf3527ef8de265eaa3716fb312c12847bfba66cef",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xaebcf05a1df94e754c2f21331f1b10056204bd810e2a015c2ec6e78ba2ba25e0.png",
+ },
+ {
+ name: "Lido DAO Token",
+ symbol: "LDO",
+ address: "0x5a98fcbea516cf06857215779fd812ca3bef1b32",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xb003c5c36c3bc6ab6a04cf816bcadf7c69763f51e95169652e8dbd51c4bf09ef.png",
+ },
+ {
+ name: "Gala",
+ symbol: "GALA",
+ address: "0xd1d2eb1b1e90b638588728b4130137d262c87cae",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x4031e94d1b2221342425981938d79c0824065b9e0ca604da7594c2ffeb66fe52.png",
+ },
+ {
+ name: "Ethereum Name Service",
+ symbol: "ENS",
+ address: "0xc18360217d8f7ab5e7c516566761ea12ce7f9d72",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x8b1881f819ba5e5e8b986832b569d7c2e0ce1453572ce65d8d41709bec018d94.png",
+ },
+ {
+ name: "Ondo U.S. Dollar Yield",
+ symbol: "USDY",
+ address: "0x96f6ef951840721adbf46ac996b59e0235cb985c",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xfd4029f0337c74afcab7b96cf7491132f4653636b3d7944acae9cc27ee23e776.png",
+ },
+ {
+ name: "SAND",
+ symbol: "SAND",
+ address: "0x3845badade8e6dff049820680d1f14bd3903a5d0",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x3d3aa642e221f1362236429e6c61d46559bb680fe5e30891e8495471a1c9cb6d.png",
+ },
+ {
+ name: "BitTorrent",
+ symbol: "BTT",
+ address: "0xc669928185dbce49d2230cc9b0979be6dc797957",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xc4ba29a5dc7368f01ce67ed8ebc9019f0f67bde7cbfabe8ff6157da67aeb99e0.png",
+ },
+ {
+ name: "JasmyCoin",
+ symbol: "JASMY",
+ address: "0x7420b4b9a0110cdc71fb720908340c03f9bc03ec",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xf02f7e6ab17a0563cb628067a587959fa6875b4c489a004ba078030583e19ad7.png",
+ },
+ {
+ name: "Pendle",
+ symbol: "PENDLE",
+ address: "0x808507121b80c02388fad14726482e061b8da827",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x93bfdf82043b3dcfd0ecbde84ec9ec332b832168f479c17ceda39ee9429e0338.png",
+ },
+ {
+ name: "Usual USD",
+ symbol: "USD0",
+ address: "0x73a15fed60bf67631dc6cd7bc5b6e8da8190acf5",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x0e1df7dd5a2d894870a16cd5ccaeb51a240c1f473761915219565a6a993857e5.png",
+ },
+ {
+ name: "cmETH",
+ symbol: "cmETH",
+ address: "0xe6829d9a7ee3040e1276fa75293bde931859e8fa",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x2db801d03445f6a78a4eaa4c5f1ac075c66e89fb42f1312d454a7e759df774d5.png",
+ },
+ {
+ name: "SolvBTC Babylon",
+ symbol: "SolvBTC.BBN",
+ address: "0xd9d920aa40f578ab794426f5c90f6c731d159def",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x07bd4c851a9dd9bf2731542a39e6986017f2dc41350a6ff4447d84e36655fe1a.png",
+ },
+ {
+ name: "tBTC v2",
+ symbol: "tBTC",
+ address: "0x18084fba666a33d37592fa2633fd49a74dd93a88",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x374c55831315eece6acdf73dd0ed4342ca11ba7a43146a2814f75b3cd47f02b4.png",
+ },
+ {
+ name: "cgETH Hashkey Cloud",
+ symbol: "cgETH.hashkey",
+ address: "0xc60a9145d9e9f1152218e7da6df634b7a74ae444",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x518d0de9c0610837259b1bafb438bec7e0d08d6d50472fd1f16fe150c9c686b9.png",
+ },
+ {
+ name: "Syrup Token",
+ symbol: "SYRUP",
+ address: "0x643c4e15d7d62ad0abec4a9bd4b001aa3ef52d66",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x53a946528e8af04d1cf5aeb25bff5b60cb3d619a771a47ae8573de1c52fcff54.png",
+ },
+ {
+ name: "Decentraland MANA",
+ symbol: "MANA",
+ address: "0x0f5d2fb29fb7d3cfee444a200298f468908cc942",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x3de2fd9036d3330aa44d05bed8e7d42c78763c78b7e9c67ec9e57763cfe007ab.png",
+ },
+ {
+ name: "",
+ symbol: "",
+ address: "0xcfd748b9de538c9f5b1805e8db9e1d4671f7f2ec",
+ decimal: "0",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x11e6eff08e149134d03f121f37913a360168286298c72c99aa2c7a6e78182205.png",
+ },
+ {
+ name: "TrueUSD",
+ symbol: "TUSD",
+ address: "0x0000000000085d4780b73119b644ae5ecd22b376",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xfdee06ccdc1b94b47884cb37f623663824e23ad500f601cc50c7419b266b68d9.png",
+ },
+ {
+ name: "ApeCoin",
+ symbol: "APE",
+ address: "0x4d224452801aced8b2f0aebe155379bb5d594381",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x97863e7e4f8a71de7dffecb21913a33f7af57a7170fbfeff05870f2acb645da2.png",
+ },
+ {
+ name: "Chain",
+ symbol: "XCN",
+ address: "0xa2cd3d43c775978a96bdbf12d733d5a1ed94fb18",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x321ed57969e51fa8315f370d76414baa8fe2fb9b079ce3ca5bfaccead729fca5.png",
+ },
+ {
+ name: "Morpho Token",
+ symbol: "MORPHO",
+ address: "0x9994e35db50125e0df82e4c2dde62496ce330999",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xe90be6230ef4ccee86e8b87c0ca8d24f8cf14bead8f1621ce3456135ffd3cc85.png",
+ },
+ {
+ name: "Decentralized USD",
+ symbol: "USDD",
+ address: "0x0c10bf8fcb7bf5412187a595ab97a3609160b5c6",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xac24f1bff2cf09d84ce133faf5e5428994ad07efc23b65d8779c09dd8c0f7cff.png",
+ },
+ {
+ name: "Dexe",
+ symbol: "DEXE",
+ address: "0xde4ee8057785a7e8e800db58f9784845a5c2cbd6",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x95ccb485493b37dbf2e6a86496688d00ef15614710b2841904dd1a9371fbfd66.png",
+ },
+ {
+ name: "Mog Coin",
+ symbol: "Mog",
+ address: "0xaaee1a9723aadb7afa2810263653a34ba2c21c7a",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x2901a28b39db8fc38d87ca82a3a54efcfa312e737501dd4ca68dda7cf8fbd275.png",
+ },
+ {
+ name: "APENFT",
+ symbol: "NFT",
+ address: "0x198d14f2ad9ce69e76ea330b374de4957c3f850a",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0xf48a34674e86da1610277474d53e92a21d74b5385215c84dc874c6098cd41f80.png",
+ },
+ {
+ name: "Reserve Rights",
+ symbol: "RSR",
+ address: "0x320623b8e4ff03373931769a31fc52a4e78b5d70",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x79fb26c2f551cc14b32575f693298d3d30e2a87c242344cca29e527fa39a7786.png",
+ },
+ {
+ name: "StarkNet Token",
+ symbol: "STRK",
+ address: "0xca14007eff0db1f8135f4c25b34de49ab0d42766",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x9a122beff8685e1b1ccd7a2b55b1f923b74eb7c37a77e58fca918937fe72c4ae.png",
+ },
+ {
+ name: "ETHx",
+ symbol: "ETHx",
+ address: "0xa35b1b31ce002fbf2058d22f30f95d405200a15b",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x7ecb25bb3cb2e648b6d601ab906e5bc27a43709ff0818d471043828f7738b98f.png",
+ },
+ {
+ name: "US Yield Coin",
+ symbol: "USYC",
+ address: "0x136471a34f6ef19fe571effc1ca711fdb8e49f2b",
+ decimal: "6",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x3fc57e0034e59dfc92e4a39bb7deb3238d876295a1a565b1985fb9b69ec261d2.png",
+ },
+ {
+ name: "Compound",
+ symbol: "COMP",
+ address: "0xc00e94cb662c3520282e6f5717214004a7f26888",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x6bfc3cb11dcccb2a934dbad676e90523308cf1a9e07402f6850d6c1ee84d967b.png",
+ },
+ {
+ name: "Movement",
+ symbol: "MOVE",
+ address: "0x3073f7aaa4db83f95e9fff17424f71d4751a3073",
+ decimal: "8",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x54afe78e77ea2aeac01a26c6e24da8a0d8829c4b11385df5e1b71db741c40bad.png",
+ },
+ {
+ name: "ether.fi ETH",
+ symbol: "eETH",
+ address: "0x35fa164735182de50811e8e2e824cfb9b6118ac2",
+ decimal: "18",
+ logo: "https://market-data-images.s3.us-east-1.amazonaws.com/tokenImages/0x02eb37d0c375737c34d9057a6c89d563d7cb96ea30f155bcecea5040bf92a640.png",
+ },
+];