Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
99 changes: 99 additions & 0 deletions app/components/Ethereum-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@

import { createContext, useContext, useEffect, useState } from "react";
import { ethers } from "ethers";
import { BRIDGE_CONTRACTS } from "../config";

interface DepositResult {
success: boolean;
commitmentHash?: string;
transactionHash?: string;
error?: string;
}

interface EthereumContextType {
provider: ethers.BrowserProvider | null;
signer: ethers.JsonRpcSigner | null;
isConnected: boolean;
connectWallet: () => Promise<void>;
address: string | null;
depositAsset: (assetType: number, tokenAddress: string, amount: string) => Promise<DepositResult>;
}

const EthereumContext = createContext<EthereumContextType>({
Expand All @@ -17,6 +26,7 @@ const EthereumContext = createContext<EthereumContextType>({
isConnected: false,
connectWallet: async () => {},
address: null,
depositAsset: async () => ({ success: false, error: "Not implemented" }),
});

export const EthereumProvider = ({ children }: { children: React.ReactNode }) => {
Expand Down Expand Up @@ -45,6 +55,94 @@ export const EthereumProvider = ({ children }: { children: React.ReactNode }) =>
}
};

const depositAsset = async (
assetType: number,
tokenAddress: string,
amount: string
): Promise<DepositResult> => {
try {
if (!signer || !provider || !address) {
return { success: false, error: "Wallet not connected" };
}

const network = await provider.getNetwork();
const bridgeAddress = BRIDGE_CONTRACTS[Number(network.chainId)];

if (!bridgeAddress) {
return { success: false, error: `Bridge not supported on network ${network.chainId}` };
}

// Bridge contract ABI (simplified - includes only depositAsset function)
const bridgeABI = [
"function depositAsset(uint256 assetType, address tokenAddress, uint256 amount, address user) external payable returns (bytes32)"
];

const bridgeContract = new ethers.Contract(bridgeAddress, bridgeABI, signer);
const amountInWei = ethers.parseEther(amount);

let tx;

if (assetType === 0) {
// ETH deposit
tx = await bridgeContract.depositAsset(
assetType,
tokenAddress,
amountInWei,
address,
{ value: amountInWei }
);
} else {
// ERC20 deposit - first check and handle approval
const erc20ABI = [
"function allowance(address owner, address spender) view returns (uint256)",
"function approve(address spender, uint256 amount) returns (bool)"
];

const tokenContract = new ethers.Contract(tokenAddress, erc20ABI, signer);
const allowance = await tokenContract.allowance(address, bridgeAddress);

if (allowance < amountInWei) {
const approvalTx = await tokenContract.approve(bridgeAddress, amountInWei);
await approvalTx.wait();
}

tx = await bridgeContract.depositAsset(assetType, tokenAddress, amountInWei, address);
}
Comment on lines +81 to +110
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

ERC20 amount uses parseEther — breaks for non-18 decimals (USDC/USDT).

For ERC20s, amounts must be in token units (decimals vary). Using parseEther will overshoot by 12 orders for 6-decimal tokens and cause allowance/transfer reverts.

Fix by resolving decimals and using parseUnits; also rename the variable to avoid “wei” implying ETH only:

-      const amountInWei = ethers.parseEther(amount);
+      // Resolve amount units
+      let amountInUnits: bigint;

ETH path:

-      if (assetType === 0) {
+      if (assetType === 0) {
         // ETH deposit
-        tx = await bridgeContract.depositAsset(
+        const amountInWei = ethers.parseEther(amount);
+        tx = await bridgeContract.depositAsset(
           assetType,
           tokenAddress,
-          amountInWei,
+          amountInWei,
           address,
           { value: amountInWei }
         );
       } else {
         // ERC20 deposit - first check and handle approval
         const erc20ABI = [
           "function allowance(address owner, address spender) view returns (uint256)",
-          "function approve(address spender, uint256 amount) returns (bool)"
+          "function approve(address spender, uint256 amount) returns (bool)",
+          "function decimals() view returns (uint8)"
         ];
         
         const tokenContract = new ethers.Contract(tokenAddress, erc20ABI, signer);
+        const decimals: number = await tokenContract.decimals();
+        amountInUnits = ethers.parseUnits(amount, decimals);
-        const allowance = await tokenContract.allowance(address, bridgeAddress);
+        const allowance = await tokenContract.allowance(address, bridgeAddress);
         
-        if (allowance < amountInWei) {
-          const approvalTx = await tokenContract.approve(bridgeAddress, amountInWei);
+        if (allowance < amountInUnits) {
+          const approvalTx = await tokenContract.approve(bridgeAddress, amountInUnits);
           await approvalTx.wait();
         }
         
-        tx = await bridgeContract.depositAsset(assetType, tokenAddress, amountInWei, address);
+        tx = await bridgeContract.depositAsset(assetType, tokenAddress, amountInUnits, address);
       }
📝 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 amountInWei = ethers.parseEther(amount);
let tx;
if (assetType === 0) {
// ETH deposit
tx = await bridgeContract.depositAsset(
assetType,
tokenAddress,
amountInWei,
address,
{ value: amountInWei }
);
} else {
// ERC20 deposit - first check and handle approval
const erc20ABI = [
"function allowance(address owner, address spender) view returns (uint256)",
"function approve(address spender, uint256 amount) returns (bool)"
];
const tokenContract = new ethers.Contract(tokenAddress, erc20ABI, signer);
const allowance = await tokenContract.allowance(address, bridgeAddress);
if (allowance < amountInWei) {
const approvalTx = await tokenContract.approve(bridgeAddress, amountInWei);
await approvalTx.wait();
}
tx = await bridgeContract.depositAsset(assetType, tokenAddress, amountInWei, address);
}
// Resolve amount units
let amountInUnits: bigint;
let tx;
if (assetType === 0) {
// ETH deposit
const amountInWei = ethers.parseEther(amount);
tx = await bridgeContract.depositAsset(
assetType,
tokenAddress,
amountInWei,
address,
{ value: amountInWei }
);
} else {
// ERC20 deposit - first check and handle approval
const erc20ABI = [
"function allowance(address owner, address spender) view returns (uint256)",
"function approve(address spender, uint256 amount) returns (bool)",
"function decimals() view returns (uint8)"
];
const tokenContract = new ethers.Contract(tokenAddress, erc20ABI, signer);
const decimals: number = await tokenContract.decimals();
amountInUnits = ethers.parseUnits(amount, decimals);
const allowance = await tokenContract.allowance(address, bridgeAddress);
if (allowance < amountInUnits) {
const approvalTx = await tokenContract.approve(bridgeAddress, amountInUnits);
await approvalTx.wait();
}
tx = await bridgeContract.depositAsset(
assetType,
tokenAddress,
amountInUnits,
address
);
}


const receipt = await tx.wait();

// Extract commitment hash from logs
let commitmentHash: string | undefined;
for (const log of receipt.logs) {
try {
const parsedLog = bridgeContract.interface.parseLog({
topics: log.topics,
data: log.data,
});

if (parsedLog && parsedLog.args && parsedLog.args.length > 0) {
commitmentHash = parsedLog.args[0];
break;
}
} catch (error) {
console.warn("Could not parse log:", error);
}
}

return {
success: true,
commitmentHash,
transactionHash: receipt.hash,
};
} catch (error) {
console.error("Deposit error:", error);
return {
success: false,
error: error instanceof Error ? error.message : "Unknown error occurred",
};
}
};

useEffect(() => {
const checkConnection = async () => {
if (typeof window.ethereum !== "undefined") {
Expand Down Expand Up @@ -87,6 +185,7 @@ export const EthereumProvider = ({ children }: { children: React.ReactNode }) =>
isConnected,
connectWallet,
address,
depositAsset,
}}
>
{children}
Expand Down
28 changes: 21 additions & 7 deletions app/components/claim-burn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ const XZBInterface: React.FC<XZBInterfaceProps> = ({
const {
isConnected: isEthereumConnected,
// connect: connectEthereum,
// depositAsset,
depositAsset,
// claimTokens,
address: ethereumAddress,
provider
Expand Down Expand Up @@ -214,13 +214,27 @@ const XZBInterface: React.FC<XZBInterfaceProps> = ({
});

// Call depositAsset function
// const txHash = await depositAsset(
// assetType,
// tokenAddress,
// amount
// );
const result = await depositAsset(
assetType,
tokenAddress,
amount
);

if (!result.success) {
throw new Error(result.error || "Deposit failed");
}

console.log("Transaction successful:", {
transactionHash: result.transactionHash,
commitmentHash: result.commitmentHash
});

// Store commitment hash for L2 use
if (result.commitmentHash) {
localStorage.setItem('latestCommitmentHash', result.commitmentHash);
localStorage.setItem('latestDepositTx', result.transactionHash || '');
}

// console.log("Transaction hash:", txHash);
setHasBurned(true);
onBurn(amount, assetId);
} catch (error) {
Expand Down
117 changes: 80 additions & 37 deletions app/components/deposit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
// useSendTransaction
} from "@starknet-react/core";
import { useTheme } from "../ThemeContext";
import { useEthereum } from "./Ethereum-provider";
// import { uint256 } from "starknet";

interface DepositProps {
Expand All @@ -18,10 +19,14 @@ interface DepositProps {
const Deposit: React.FC<DepositProps> = ({ token, onClose }) => {
const [amount, setAmount] = useState("");
const [error, setError] = useState("");
const [isProcessing, setIsProcessing] = useState(false);
const { isDarkMode } = useTheme();

// Starknet React Hooks
const { account } = useAccount();

// Ethereum hooks
const { isConnected: isEthereumConnected, depositAsset, address: ethereumAddress } = useEthereum();
// // Get contract instance
// const { contract } = useContract({
// address: `0x${contractAddress}`,
Expand All @@ -45,41 +50,77 @@ const Deposit: React.FC<DepositProps> = ({ token, onClose }) => {
// ],
// });

// const handleDeposit = async () => {
// if (!contract || !account) {
// setError("Please connect your wallet");
// return;
// }
const handleDeposit = async () => {
try {
setError("");
setIsProcessing(true);

// try {
// setError("");
if (!isEthereumConnected || !ethereumAddress) {
setError("Please connect your Ethereum wallet");
return;
}

if (!amount || parseFloat(amount) <= 0) {
setError("Please enter a valid amount");
return;
}

// Determine asset type and token address
const assetType = token === "ETH" ? 0 : 1;
let tokenAddress;

if (assetType === 0) {
tokenAddress = "0x0000000000000000000000000000000000000000";
} else {
// For ERC20 tokens, you'd need to map token symbols to addresses
// This is a placeholder - in production, you'd have a token registry
const tokenAddresses: Record<string, string> = {
USDC: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
USDT: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
};
tokenAddress = tokenAddresses[token];

if (!tokenAddress) {
setError(`Token ${token} not supported`);
return;
}
}
Comment on lines +68 to +87
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Token address mapping is mainnet-only; deposits on Sepolia will fail for ERC20.

You’re passing mainnet USDC/USDT addresses while the bridge is configured for Sepolia. On Sepolia those contracts either don’t exist or don’t match the expected ERC20s, causing allowance checks and deposits to revert.

Recommended approach:

  • Maintain a chain-aware token registry (addresses, decimals) keyed by chainId (e.g., 11155111 for Sepolia).
  • Pull the connected chainId (either via the Ethereum context or by modifying depositAsset to resolve decimals/addresses internally).
  • Surface “Unsupported token on this network” early if missing.

Minimal inline change (temporary) to avoid accidental mainnet addresses:

-        const tokenAddresses: Record<string, string> = {
-          USDC: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
-          USDT: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
-        };
-        tokenAddress = tokenAddresses[token];
+        // TODO: Replace with a proper per-chain registry.
+        const tokenAddressesByChain: Record<number, Record<string, string>> = {
+          11155111: {
+            // Fill with your Sepolia token deployments
+            // USDC: "<SEPOLIA_USDC_ADDRESS>",
+            // USDT: "<SEPOLIA_USDT_ADDRESS>",
+          },
+        };
+        const chainId = 11155111; // TEMP: read from Ethereum provider/network
+        tokenAddress = tokenAddressesByChain[chainId]?.[token];

If you’d like, I can extract a typed TokenRegistry into app/config and wire it here and in claim-burn for consistency.

📝 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
// Determine asset type and token address
const assetType = token === "ETH" ? 0 : 1;
let tokenAddress;
if (assetType === 0) {
tokenAddress = "0x0000000000000000000000000000000000000000";
} else {
// For ERC20 tokens, you'd need to map token symbols to addresses
// This is a placeholder - in production, you'd have a token registry
const tokenAddresses: Record<string, string> = {
USDC: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
USDT: "0xdAC17F958D2ee523a2206206994597C13D831ec7",
};
tokenAddress = tokenAddresses[token];
if (!tokenAddress) {
setError(`Token ${token} not supported`);
return;
}
}
// Determine asset type and token address
const assetType = token === "ETH" ? 0 : 1;
let tokenAddress;
if (assetType === 0) {
tokenAddress = "0x0000000000000000000000000000000000000000";
} else {
// For ERC20 tokens, you'd need to map token symbols to addresses
// This is a placeholder - in production, you'd have a token registry
// TODO: Replace with a proper per-chain registry.
const tokenAddressesByChain: Record<number, Record<string, string>> = {
11155111: {
// Fill with your Sepolia token deployments
// USDC: "<SEPOLIA_USDC_ADDRESS>",
// USDT: "<SEPOLIA_USDT_ADDRESS>",
},
};
const chainId = 11155111; // TEMP: read from Ethereum provider/network
tokenAddress = tokenAddressesByChain[chainId]?.[token];
if (!tokenAddress) {
setError(`Token ${token} not supported`);
return;
}
}
🤖 Prompt for AI Agents
In app/components/deposit.tsx around lines 68 to 87, the code uses hard-coded
mainnet ERC20 addresses (USDC/USDT) which will fail on Sepolia; change the logic
to be chain-aware by resolving token addresses and decimals from a TokenRegistry
keyed by chainId: obtain the current chainId from the web3/ethers provider or
component props, look up tokenAddresses = tokenRegistry[chainId] and then
tokenAddress = tokenAddresses?.[token]; if the lookup fails, call
setError("Unsupported token on this network") and return; ensure the registry
contains decimals and addresses for each supported chain (e.g., mainnet and
Sepolia) and update any allowance/decimals handling to use the registry values.


console.log("Initiating deposit:", {
assetType,
tokenAddress,
amount,
userAddress: ethereumAddress
});

const result = await depositAsset(assetType, tokenAddress, amount);

// // Convert amount to uint256
// const amountInWei = uint256.bnToUint256(
// BigInt(parseFloat(amount) * 10 ** 18)
// );

// // Update calldata with the correct values
// const tx = send([
// {
// contractAddress: contractAddress,
// entrypoint: "deposit",
// calldata: [
// account.address,
// amountInWei.low,
// amountInWei.high,
// ],
// },
// ]);
// console.log('transaction result', tx);
// // Clear input and close modal
// setAmount("");
// onClose();

// } catch (err) {
// setError(err instanceof Error ? err.message : "Failed to deposit");
// }
// };
if (!result.success) {
throw new Error(result.error || "Deposit failed");
}

console.log("Deposit successful:", {
transactionHash: result.transactionHash,
commitmentHash: result.commitmentHash
});

// Store commitment hash for L2 use
if (result.commitmentHash) {
localStorage.setItem('latestCommitmentHash', result.commitmentHash);
localStorage.setItem('latestDepositTx', result.transactionHash || '');
}

// Clear input and close modal on success
setAmount("");
onClose();

} catch (err) {
console.error("Deposit error:", err);
setError(err instanceof Error ? err.message : "Failed to deposit");
} finally {
setIsProcessing(false);
}
};

// Format balance for display
const formattedBalance = balance
Expand Down Expand Up @@ -153,13 +194,15 @@ const Deposit: React.FC<DepositProps> = ({ token, onClose }) => {
)}

<button
// onClick={handleDeposit}
disabled={!account || !amount || parseFloat(amount) <= 0}
onClick={handleDeposit}
disabled={!isEthereumConnected || !amount || parseFloat(amount) <= 0 || isProcessing}
className="mt-6 w-full py-2 px-4 bg-[#1F1333] text-white
disabled:bg-[#3B2A65] rounded-lg transition-colors"
>
{!account
? "Connect Wallet"
{!isEthereumConnected
? "Connect Ethereum Wallet"
: isProcessing
? "Processing..."
: "Deposit"
}
</button>
Expand Down
13 changes: 10 additions & 3 deletions app/config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { http, createConfig } from 'wagmi'
import { base, mainnet} from 'wagmi/chains'
import { base, mainnet, sepolia} from 'wagmi/chains'
import { injected, metaMask, safe, walletConnect } from 'wagmi/connectors'

const projectId = '<WALLETCONNECT_PROJECT_ID>'

export const config = createConfig({
chains: [mainnet, base],
chains: [mainnet, base, sepolia],
connectors: [
injected(),
walletConnect({ projectId }),
Expand All @@ -15,5 +15,12 @@ export const config = createConfig({
transports: {
[mainnet.id]: http(),
[base.id]: http(),
[sepolia.id]: http(),
},
})
})

// Bridge contract addresses
export const BRIDGE_CONTRACTS: Record<number, string> = {
[sepolia.id]: '0x8F25bFe32269632dfd8D223D51FF145414d8107b',
// Add other networks as needed
}