Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Liquidity Prototype #24

Merged
merged 5 commits into from
Sep 11, 2024
Merged
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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const vaultABI = [
export const liquidityABI = [
{
inputs: [
{ internalType: "address", name: "_pool", type: "address" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import React, { useEffect, useState } from "react";
import Image from "next/image";
import { useReadContract } from "wagmi";
import { vaultABI } from "~~/app/components/abis/vault";
import { liquidityABI } from "~~/app/components/abis/liquidity";
import { useTranslation } from "~~/app/context/LanguageContext";

const VaultInfo: React.FC = () => {
const LiquidityInfo: React.FC = () => {
const { t } = useTranslation();
const [, setTotalReserves] = useState<number | null>(null);
const { data: totalReserves } = useReadContract({
address: "0xD6DaB267b7C23EdB2ed5605d9f3f37420e88e291",
abi: vaultABI,
abi: liquidityABI,
functionName: "totalSupply",
});

Expand All @@ -33,7 +33,7 @@ const VaultInfo: React.FC = () => {
error: lpTokenError,
} = useReadContract({
address: "0xD6DaB267b7C23EdB2ed5605d9f3f37420e88e291",
abi: vaultABI,
abi: liquidityABI,
functionName: "getTotalAmounts",
});

Expand Down Expand Up @@ -101,4 +101,4 @@ const VaultInfo: React.FC = () => {
);
};

export default VaultInfo;
export default LiquidityInfo;
270 changes: 270 additions & 0 deletions packages/nextjs/app/liquidity/components/LiquidityWidget/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
"use client";

import React, { useEffect, useState } from "react";
import { spenderAddress, usdcContract, xocContract } from "@/app/constants/contracts";
import { parseEther, parseUnits } from "viem";
import { useAccount, useReadContract, useWriteContract } from "wagmi";
import { ERC20ABI } from "~~/app/components/abis/erc20";
import { liquidityABI } from "~~/app/components/abis/liquidity";
import { useTranslation } from "~~/app/context/LanguageContext";

// Use parseUnits for USDC

const LiquidityWidget: React.FC = () => {
const { address: accountAddress } = useAccount(); // Get the address, not the entire account object
const { t } = useTranslation();
const [action, setAction] = useState<"Deposit" | "Withdraw">("Deposit");
const [tokenA, setTokenA] = useState(""); // USDC amount
const [tokenB, setTokenB] = useState(""); // XOC amount
const [xocAllowanceState, xocSetAllowanceState] = useState<string>("0");
const [usdcAllowanceState, usdcSetAllowanceState] = useState<string>("0");
// State for share withdrawal input
const [shareAmount, setShareAmount] = useState(""); // Share amount

// State to track if approval is needed
const [requiresApproval, setRequiresApproval] = useState(false);

const { writeContract: deposit } = useWriteContract();

const { writeContract: approveERC20 } = useWriteContract();

const { writeContract: withdraw } = useWriteContract();

// Hook to read the XOC contract allowance
const {
data: xocAllowance,
isError,
isLoading,
} = useReadContract({
address: xocContract,
abi: ERC20ABI,
functionName: "allowance",
args: [accountAddress, spenderAddress], // Only pass the address
});

useEffect(() => {
if (isError) {
console.error("Error fetching allowance");
xocSetAllowanceState("0");
} else if (!isLoading && xocAllowance) {
const allowanceInEther = (Number(xocAllowance) / 1e18).toFixed(7);
xocSetAllowanceState(allowanceInEther);
}
}, [xocAllowance, isError, isLoading]);

// Hook to read the USDC contract allowance
const {
data: usdcAllowance,
isError: usdcIsError,
isLoading: usdcIsLoading,
} = useReadContract({
address: usdcContract,
abi: ERC20ABI,
functionName: "allowance",
args: [accountAddress, spenderAddress],
});

useEffect(() => {
if (usdcIsError) {
console.error("Error fetching allowance");
usdcSetAllowanceState("0");
} else if (!usdcIsLoading && usdcAllowance) {
const allowanceInEther = (Number(usdcAllowance) / 1e6).toFixed(7); // USDC has 6 decimals
usdcSetAllowanceState(allowanceInEther);
}
}, [usdcAllowance, usdcIsError, usdcIsLoading]);

// Trigger approval check whenever tokenA or tokenB changes
useEffect(() => {
// Function to check if approval is required
const checkIfApprovalNeeded = () => {
const usdcAmount = parseFloat(tokenA) || 0;
const xocAmount = parseFloat(tokenB) || 0;

// Compare the input values against the allowance states
const needsApproval = xocAmount > parseFloat(xocAllowanceState) || usdcAmount > parseFloat(usdcAllowanceState);
setRequiresApproval(needsApproval);
};

checkIfApprovalNeeded();
}, [tokenA, tokenB, xocAllowanceState, usdcAllowanceState]);

const handleActionChange = (newAction: "Deposit" | "Withdraw") => {
setAction(newAction);
};

// Function to handle the deposit
const handleDeposit = async () => {
if (!accountAddress) {
console.error("Account address not found");
return;
}

const usdcAmount = parseFloat(tokenA) || 0;
const xocAmount = parseFloat(tokenB) || 0;
const xocAmountInWei = parseEther(xocAmount.toString()); // XOC uses 18 decimals
const usdcAmountInWei = parseUnits(usdcAmount.toString(), 6); // USDC uses 6 decimals

try {
const tx = await deposit({
abi: liquidityABI,
address: "0xD6DaB267b7C23EdB2ed5605d9f3f37420e88e291", // Liquidity contract address
functionName: "deposit",
args: [usdcAmountInWei, xocAmountInWei, accountAddress],
});

console.log("Transaction submitted:", tx);
// Optionally wait for the transaction to be mined
// const receipt = await tx.wait();
// console.log("Transaction confirmed:", receipt);
} catch (err) {
console.error("Error executing contract function:", err);
}
};

// Function to handle approval
const handleApproval = async () => {
const usdcAmount = parseFloat(tokenA) || 0;
const xocAmount = parseFloat(tokenB) || 0;

try {
// Approve XOC
if (usdcAmount > parseFloat(usdcAllowanceState)) {
await approveERC20({
abi: ERC20ABI,
address: usdcContract,
functionName: "approve",
args: [spenderAddress, usdcAmount * 1e6], // Multiply by 1e6 to convert to USDC decimals
});
}

// Approve USDC
if (xocAmount > parseFloat(xocAllowanceState)) {
await approveERC20({
abi: ERC20ABI,
address: xocContract,
functionName: "approve",
args: [spenderAddress, xocAmount * 1e18], // Multiply by 1e18 to convert to XOC decimals
});
}
} catch (err) {
console.error("Error approving tokens:", err);
}
};

console.log("Xoc Allowance", xocAllowanceState);
console.log("USDC Allowance", usdcAllowanceState);

// Function to handle the withdrawal
const handleWithdrawal = async () => {
if (!accountAddress) {
console.error("Account address not found");
return;
}

const shareAmountInWei = parseEther(shareAmount.toString()); // XOC uses 18 decimals

try {
const tx = await withdraw({
abi: liquidityABI,
address: "0xD6DaB267b7C23EdB2ed5605d9f3f37420e88e291", // Liquidity contract address
functionName: "withdraw",
args: [shareAmountInWei, accountAddress],
});

console.log("Transaction submitted:", tx);
// Optionally wait for the transaction to be mined
// const receipt = await tx.wait();
// console.log("Transaction confirmed:", receipt);
} catch (err) {
console.error("Error executing contract function:", err);
}
};

return (
<div className="w-full bg-white p-6 rounded-lg shadow-md mt-6">
<div className="mb-4">
<h2 className="text-xl font-semibold text-gray-800 mb-2">{t("XoktleLiquidityTitle")}</h2>
<hr className="border-t-2 border-gray-300 rounded-t-full" />
</div>

<div className="mb-6 flex justify-center">
<div className="flex">
<button
onClick={() => handleActionChange("Deposit")}
className={`px-6 py-2 rounded-l-full ${
action === "Deposit" ? "bg-base-300 text-xl text-white" : "bg-gray-200 text-gray-800"
}`}
>
{t("XoktleDepositSwitcher")}
</button>
<button
onClick={() => handleActionChange("Withdraw")}
className={`px-6 py-2 rounded-r-full ${
action === "Withdraw" ? "bg-base-300 text-xl text-white" : "bg-gray-200 text-gray-800"
}`}
>
{t("XoktleWithdrawSwitcher")}
</button>
</div>
</div>

{/* Conditionally render input fields based on the selected action */}
{action === "Deposit" ? (
<div className="space-y-4 mb-6">
<div>
<label className="block text-gray-700">{t("XoktleUSDCIndicate")}</label>
<input
type="number"
value={tokenA}
onChange={e => setTokenA(e.target.value)}
className="w-full p-2 border rounded-lg dark:bg-neutral dark:text-neutral-content"
placeholder={t("XoktleUSDCAmount")}
/>
</div>

<div>
<label className="block text-gray-700">{t("XoktleXOCIndicate")}</label>
<input
type="number"
value={tokenB}
onChange={e => setTokenB(e.target.value)}
className="w-full p-2 border rounded-lg dark:bg-neutral dark:text-neutral-content"
placeholder={t("XoktleXOCAmount")}
/>
</div>
</div>
) : (
<div className="space-y-4 mb-6">
<div>
<label className="block text-gray-700">{t("XoktleShareIndicate")}</label>
<input
type="number"
value={shareAmount} // This will represent the share amount to withdraw
onChange={e => setShareAmount(e.target.value)}
className="w-full p-2 border rounded-lg dark:bg-neutral dark:text-neutral-content"
placeholder={t("XoktleShareAmount")}
/>
</div>
</div>
)}

<button
className="w-full py-3 bg-base-300 text-2xl text-white font-semibold rounded-lg"
onClick={() => {
if (requiresApproval) {
handleApproval(); // Call handleApproval when approval is needed
} else if (action === "Deposit") {
handleDeposit(); // Call handleDeposit if deposit is selected
} else if (action === "Withdraw") {
handleWithdrawal(); // Call handleWithdrawal if withdraw is selected
}
}}
>
{requiresApproval ? t("Approve") : action}
</button>
</div>
);
};

export default LiquidityWidget;
68 changes: 68 additions & 0 deletions packages/nextjs/app/liquidity/components/OverviewWidget/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
"use client";

import React, { useEffect, useState } from "react";
import { useAccount, useReadContract } from "wagmi";
import { liquidityABI } from "~~/app/components/abis/liquidity";
import { useTranslation } from "~~/app/context/LanguageContext";

const OverviewWidget: React.FC = () => {
const { t } = useTranslation();
const [balance, setBalance] = useState<number | null>(null);
const { address: accountAddress } = useAccount(); // Get accountAddress using useAccount hook

// Fetch the balanceOf using the accountAddress
const {
data: balanceData,
isLoading: balanceLoading,
error: balanceError,
} = useReadContract({
address: "0xD6DaB267b7C23EdB2ed5605d9f3f37420e88e291", // Liquidity contract address
abi: liquidityABI,
functionName: "balanceOf",
args: [accountAddress], // Pass accountAddress as argument to balanceOf
});

useEffect(() => {
if (balanceData) {
// Convert the BigInt to a number and format it
const balanceValue = Number(balanceData) / 10 ** 18; // Assuming the token has 18 decimals
setBalance(balanceValue);
}
}, [balanceData]);

// Function to format the balance as a standard number with commas
const formatNumber = (amount: number) => {
return new Intl.NumberFormat("en-US", {
minimumFractionDigits: 6,
maximumFractionDigits: 6,
}).format(amount);
};

const formattedBalance = balance !== null ? formatNumber(balance) : null;

return (
<div className="w-full bg-white p-6 rounded-lg shadow-md mt-6">
{/* Title */}
<div className="mb-4">
<h2 className="text-xl font-semibold text-gray-800 mb-2">{t("XoktleOverView")}</h2>
<hr className="border-t-2 border-gray-300 rounded-t-full" />
</div>

{/* Loading and Error States */}
{balanceLoading && <p className="text-gray-500">Loading balance...</p>}
{balanceError && <p className="text-red-500">Error loading balance.</p>}

{/* Display Balance */}
{!balanceLoading && !balanceError && balance !== null && (
<div className="space-y-4">
<div className="flex justify-between">
<span className="text-gray-700">{t("XoktleAccountShares")}:</span>
<span className="text-gray-900 font-semibold">{formattedBalance} shares</span>
</div>
</div>
)}
</div>
);
};

export default OverviewWidget;
Loading
Loading