From bc5dc90a9d13b44cd34cd246bab12c4e0f8b9235 Mon Sep 17 00:00:00 2001
From: MusabShakeel576 <46605319+MusabShakeel576@users.noreply.github.com>
Date: Sun, 6 Apr 2025 12:38:35 +0000
Subject: [PATCH 1/2] Add Wrap FUSE token in Operator deposit
---
components/dashboard/TopupAccountModal.tsx | 84 +++++-
lib/abi/WrappedToken.ts | 311 +++++++++++++++++++++
lib/erc20.ts | 28 +-
store/operatorSlice/index.ts | 31 +-
4 files changed, 441 insertions(+), 13 deletions(-)
create mode 100644 lib/abi/WrappedToken.ts
diff --git a/components/dashboard/TopupAccountModal.tsx b/components/dashboard/TopupAccountModal.tsx
index 94a5cf92..45238ae5 100644
--- a/components/dashboard/TopupAccountModal.tsx
+++ b/components/dashboard/TopupAccountModal.tsx
@@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
import { useAppDispatch, useAppSelector } from "@/store/store";
-import { chargeBridge, fetchBridgeSupportedTokens, selectOperatorSlice, setIsTopupAccountModalOpen, withRefreshToken } from "@/store/operatorSlice";
+import { chargeBridge, fetchBridgeSupportedTokens, selectOperatorSlice, setIsTopupAccountModalOpen, withRefreshToken, wrapToken } from "@/store/operatorSlice";
import copy from "@/assets/copy-black.svg";
import qr from "@/assets/qr.svg";
import leftArrow from "@/assets/left-arrow.svg";
@@ -33,6 +33,10 @@ type TopupFormValues = {
amount: string;
}
+type WrapTokenFormValues = {
+ amount: string;
+}
+
type CopyAddressProps = {
setQrCode: (qrCode: Address) => void;
address: Address;
@@ -49,6 +53,10 @@ type BridgeTimeProps = {
setBridgeEndTime: (bridgeEndTime: BridgeEndTime) => void;
}
+type FuseNetworkProps = {
+ setQrCode: (qrCode: Address) => void;
+}
+
type OtherNetworkProps = {
setQrCode: (qrCode: Address) => void;
}
@@ -63,15 +71,15 @@ const chains: Record = {
function filterTokens(data: ChargeBridgeSupportedTokens, tokenSymbol = "FUSE") {
const filteredData: ChargeBridgeSupportedTokens = {};
-
+
Object.keys(data).forEach(chainId => {
- const filteredTokens = data[chainId].filter(token => token.symbol === tokenSymbol);
-
- if (filteredTokens.length > 0) {
- filteredData[chainId] = filteredTokens;
- }
+ const filteredTokens = data[chainId].filter(token => token.symbol === tokenSymbol);
+
+ if (filteredTokens.length > 0) {
+ filteredData[chainId] = filteredTokens;
+ }
});
-
+
return filteredData;
}
@@ -245,6 +253,62 @@ const BridgeTime = ({ bridgeEndTime, setBridgeEndTime }: BridgeTimeProps) => {
)
}
+const FuseNetwork = ({ setQrCode }: FuseNetworkProps) => {
+ const dispatch = useAppDispatch();
+ const operatorSlice = useAppSelector(selectOperatorSlice);
+
+ const formik = useFormik({
+ initialValues: {
+ amount: "",
+ },
+ validationSchema: Yup.object({
+ amount: Yup.string().required('Required'),
+ }),
+ onSubmit: values => dispatch(wrapToken(values)),
+ });
+
+ return (
+
+ )
+}
+
const OtherNetwork = ({ setQrCode }: OtherNetworkProps) => {
const dispatch = useAppDispatch();
const operatorSlice = useAppSelector(selectOperatorSlice);
@@ -401,10 +465,8 @@ const TopupAccountModal = (): JSX.Element => {
{selectedTab === Tab.FUSE && (
-
)}
{selectedTab === Tab.OTHER && (
diff --git a/lib/abi/WrappedToken.ts b/lib/abi/WrappedToken.ts
new file mode 100644
index 00000000..2101f4d2
--- /dev/null
+++ b/lib/abi/WrappedToken.ts
@@ -0,0 +1,311 @@
+import { narrow } from 'abitype'
+
+export const WrappedTokenABI = narrow([
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "src",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "guy",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "Approval",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "dst",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "Deposit",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "src",
+ "type": "address"
+ },
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "dst",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "Transfer",
+ "type": "event"
+ },
+ {
+ "anonymous": false,
+ "inputs": [
+ {
+ "indexed": true,
+ "internalType": "address",
+ "name": "src",
+ "type": "address"
+ },
+ {
+ "indexed": false,
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "Withdrawal",
+ "type": "event"
+ },
+ {
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "fallback"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "allowance",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "guy",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "approve",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "decimals",
+ "outputs": [
+ {
+ "internalType": "uint8",
+ "name": "",
+ "type": "uint8"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [],
+ "name": "deposit",
+ "outputs": [],
+ "payable": true,
+ "stateMutability": "payable",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "name",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "symbol",
+ "outputs": [
+ {
+ "internalType": "string",
+ "name": "",
+ "type": "string"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": true,
+ "inputs": [],
+ "name": "totalSupply",
+ "outputs": [
+ {
+ "internalType": "uint256",
+ "name": "",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "dst",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "transfer",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "address",
+ "name": "src",
+ "type": "address"
+ },
+ {
+ "internalType": "address",
+ "name": "dst",
+ "type": "address"
+ },
+ {
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "transferFrom",
+ "outputs": [
+ {
+ "internalType": "bool",
+ "name": "",
+ "type": "bool"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "constant": false,
+ "inputs": [
+ {
+ "internalType": "uint256",
+ "name": "wad",
+ "type": "uint256"
+ }
+ ],
+ "name": "withdraw",
+ "outputs": [],
+ "payable": false,
+ "stateMutability": "nonpayable",
+ "type": "function"
+ }
+])
diff --git a/lib/erc20.ts b/lib/erc20.ts
index 8f3a0dd2..06cfd2b2 100644
--- a/lib/erc20.ts
+++ b/lib/erc20.ts
@@ -1,9 +1,10 @@
import { ERC20ABI } from "@/lib/abi/ERC20";
-import { Address, createPublicClient, http, parseUnits } from "viem";
+import { Address, createPublicClient, http, parseEther, parseUnits } from "viem";
import { getWalletClient, waitForTransactionReceipt } from "wagmi/actions";
import { hex } from "./helpers";
import { config } from "./wagmi";
import { fuse } from "viem/chains";
+import { WrappedTokenABI } from "@/lib/abi/WrappedToken";
const publicClient = (rpcUrl: string) => {
return createPublicClient({
@@ -83,3 +84,28 @@ export const getERC20Decimals = async (
});
return decimals;
};
+
+export const depositToken = async (
+ contractAddress: Address,
+ amount: string,
+ chainId: number = fuse.id
+) => {
+ const walletClient = await getWalletClient(config, { chainId });
+ if (!walletClient) {
+ return;
+ }
+ const accounts = await walletClient.getAddresses();
+ const account = accounts[0];
+ const tx = await walletClient.writeContract({
+ account,
+ address: contractAddress,
+ abi: WrappedTokenABI,
+ functionName: "deposit",
+ args: [],
+ value: parseEther(amount),
+ });
+ await waitForTransactionReceipt(config, {
+ hash: tx,
+ });
+ return tx;
+}
diff --git a/store/operatorSlice/index.ts b/store/operatorSlice/index.ts
index 8a53e25b..acc53bc2 100644
--- a/store/operatorSlice/index.ts
+++ b/store/operatorSlice/index.ts
@@ -12,7 +12,7 @@ import { CONFIG, NEXT_PUBLIC_FUSE_API_BASE_URL, NEXT_PUBLIC_PAYMASTER_FUNDER_ADD
import { PaymasterAbi } from "@/lib/abi/Paymaster";
import { getSponsorIdBalance } from "@/lib/contractInteract";
import * as amplitude from "@amplitude/analytics-browser";
-import { getERC20Balance } from "@/lib/erc20";
+import { depositToken, getERC20Balance } from "@/lib/erc20";
import { ERC20ABI } from "@/lib/abi/ERC20";
import { Account, parseEther, parseUnits } from "viem";
@@ -106,6 +106,7 @@ export interface OperatorStateType {
bridgeSupportedTokensStatus: Status;
chargeBridgeStatus: Status;
chargeBridge: ChargeBridgeResponse;
+ wrapTokenStatus: Status;
}
const INIT_STATE: OperatorStateType = {
@@ -153,6 +154,7 @@ const INIT_STATE: OperatorStateType = {
bridgeSupportedTokensStatus: Status.IDLE,
chargeBridgeStatus: Status.IDLE,
chargeBridge: initChargeBridge,
+ wrapTokenStatus: Status.IDLE,
};
export const checkOperator = createAsyncThunk(
@@ -877,6 +879,24 @@ export const chargeBridge = createAsyncThunk(
}
);
+export const wrapToken = createAsyncThunk(
+ "OPERATOR/WRAP_TOKEN",
+ async ({
+ amount
+ }: {
+ amount: string,
+ }) => {
+ try {
+ const subscriptionInfo = subscriptionInformation()
+ const tx = await depositToken(subscriptionInfo.tokenAddress, amount)
+ return tx;
+ } catch (error) {
+ console.log(error);
+ throw error;
+ }
+ }
+);
+
const operatorSlice = createSlice({
name: "OPERATOR_STATE",
initialState: INIT_STATE,
@@ -1157,6 +1177,15 @@ const operatorSlice = createSlice({
.addCase(chargeBridge.rejected, (state) => {
state.chargeBridgeStatus = Status.ERROR;
})
+ .addCase(wrapToken.pending, (state) => {
+ state.wrapTokenStatus = Status.PENDING;
+ })
+ .addCase(wrapToken.fulfilled, (state) => {
+ state.wrapTokenStatus = Status.SUCCESS;
+ })
+ .addCase(wrapToken.rejected, (state) => {
+ state.wrapTokenStatus = Status.ERROR;
+ })
},
});
From 1ae194ff5a4257e30056b3d9c5a4b00fa6798b28 Mon Sep 17 00:00:00 2001
From: MusabShakeel576 <46605319+MusabShakeel576@users.noreply.github.com>
Date: Sun, 6 Apr 2025 12:46:01 +0000
Subject: [PATCH 2/2] Add wrap token success in Operator deposit
---
components/dashboard/TopupAccountModal.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/components/dashboard/TopupAccountModal.tsx b/components/dashboard/TopupAccountModal.tsx
index 45238ae5..0d98a417 100644
--- a/components/dashboard/TopupAccountModal.tsx
+++ b/components/dashboard/TopupAccountModal.tsx
@@ -301,7 +301,7 @@ const FuseNetwork = ({ setQrCode }: FuseNetworkProps) => {
(operatorSlice.wrapTokenStatus === Status.ERROR) ? 'bg-[#FD0F0F] border-[#FD0F0F] text-white hover:text-[#FD0F0F]' : 'bg-black border-black text-white hover:text-black'
)}
>
- Wrap
+ {operatorSlice.wrapTokenStatus === Status.SUCCESS ? 'Successfully wrapped' : 'Wrap'}
{operatorSlice.wrapTokenStatus === Status.PENDING && }