diff --git a/src/blockchain/__tests__/addresses.spec.ts b/src/blockchain/__tests__/addresses.spec.ts index 82ebbc8..1f92614 100644 --- a/src/blockchain/__tests__/addresses.spec.ts +++ b/src/blockchain/__tests__/addresses.spec.ts @@ -6,7 +6,6 @@ import { isPossibleNetwork, } from "../addresses"; -import { setActivePinia, createPinia } from "pinia"; import { NetworkEnum, TokenEnum } from "@/model/NetworkEnum"; import { useUser } from "@/composables/useUser"; @@ -20,10 +19,6 @@ describe("addresses.ts types", () => { }); describe("addresses.ts functions", () => { - beforeEach(() => { - setActivePinia(createPinia()); - }); - it("getTokenAddress Ethereum", () => { const user = useUser(); user.setNetworkId(11155111); diff --git a/src/blockchain/addresses.ts b/src/blockchain/addresses.ts index 8a85409..1693d59 100644 --- a/src/blockchain/addresses.ts +++ b/src/blockchain/addresses.ts @@ -28,19 +28,23 @@ export const getTokenByAddress = (address: string) => { export const getTokenAddress = ( token: TokenEnum, network?: NetworkEnum -): string => { +): `0x${string}` => { const user = useUser(); - return Tokens[network ? network : user.networkName.value][token]; + return Tokens[network ? network : user.networkName.value][ + token + ] as `0x${string}`; }; -export const getP2PixAddress = (network?: NetworkEnum): string => { +export const getP2PixAddress = (network?: NetworkEnum): `0x${string}` => { const user = useUser(); const possibleP2PixAddresses: { [key in NetworkEnum]: string } = { [NetworkEnum.sepolia]: "0xb7cD135F5eFD9760981e02E2a898790b688939fe", [NetworkEnum.rootstock]: "0x98ba35eb14b38D6Aa709338283af3e922476dE34", }; - return possibleP2PixAddresses[network ? network : user.networkName.value]; + return possibleP2PixAddresses[ + network ? network : user.networkName.value + ] as `0x${string}`; }; export const getProviderUrl = (network?: NetworkEnum): string => { @@ -54,7 +58,6 @@ export const getProviderUrl = (network?: NetworkEnum): string => { }; export const getProviderByNetwork = (network: NetworkEnum) => { - console.log("network", network); const chain = network === NetworkEnum.sepolia ? sepolia : rootstock; return createPublicClient({ chain, diff --git a/src/blockchain/buyerMethods.ts b/src/blockchain/buyerMethods.ts index 2e445b0..c91ae49 100644 --- a/src/blockchain/buyerMethods.ts +++ b/src/blockchain/buyerMethods.ts @@ -8,51 +8,65 @@ export const addLock = async ( tokenAddress: string, amount: number ): Promise => { - const { address, abi, wallet, client } = await getContract(); + const { address, abi, wallet, client, account } = await getContract(); const parsedAmount = parseEther(amount.toString()); + if (!wallet) { + throw new Error("Wallet not connected"); + } + const { request } = await client.simulateContract({ - address, + address: address as `0x${string}`, abi, functionName: "lock", - args: [sellerAddress, tokenAddress, parsedAmount, [], []], + args: [sellerAddress, tokenAddress as `0x${string}`, parsedAmount, [], []], + account: account as `0x${string}`, }); - console.log(wallet); const hash = await wallet.writeContract(request); const receipt = await client.waitForTransactionReceipt({ hash }); - return receipt.status ? receipt.logs[0].topics[2] : ""; + return receipt.status === "success" ? receipt.logs[0].topics[2] ?? "" : ""; }; export const withdrawDeposit = async ( amount: string, token: TokenEnum ): Promise => { - const { address, abi, wallet, client } = await getContract(); + const { address, abi, wallet, client, account } = await getContract(); + + if (!wallet) { + throw new Error("Wallet not connected"); + } const tokenAddress = getTokenAddress(token); const { request } = await client.simulateContract({ - address, + address: address as `0x${string}`, abi, functionName: "withdraw", - args: [tokenAddress, parseEther(amount), []], + args: [tokenAddress as `0x${string}`, parseEther(amount), []], + account: account as `0x${string}`, }); const hash = await wallet.writeContract(request); const receipt = await client.waitForTransactionReceipt({ hash }); - return receipt.status; + return receipt.status === "success"; }; export const releaseLock = async (solicitation: any): Promise => { - const { address, abi, wallet, client } = await getContract(); + const { address, abi, wallet, client, account } = await getContract(); + + if (!wallet) { + throw new Error("Wallet not connected"); + } const { request } = await client.simulateContract({ - address, + address: address as `0x${string}`, abi, functionName: "release", args: [solicitation.lockId, solicitation.e2eId], + account: account as `0x${string}`, }); const hash = await wallet.writeContract(request); diff --git a/src/blockchain/events.ts b/src/blockchain/events.ts index ce739bc..a720acf 100644 --- a/src/blockchain/events.ts +++ b/src/blockchain/events.ts @@ -7,7 +7,6 @@ import type { ValidDeposit } from "@/model/ValidDeposit"; import { getP2PixAddress, getTokenAddress } from "./addresses"; import { getNetworkSubgraphURL, NetworkEnum } from "@/model/NetworkEnum"; import type { UnreleasedLock } from "@/model/UnreleasedLock"; -import type { Pix } from "@/model/Pix"; const getNetworksLiquidity = async (): Promise => { const user = useUser(); @@ -34,7 +33,7 @@ const getPixKey = async (seller: string, token: string): Promise => { const { address, abi, client } = await getContract(); const pixKeyHex = await client.readContract({ - address, + address: address as `0x${string}`, abi, functionName: "getPixTarget", args: [seller, token], @@ -42,13 +41,12 @@ const getPixKey = async (seller: string, token: string): Promise => { // Remove '0x' prefix and convert hex to UTF-8 string const hexString = - typeof pixKeyHex === "string" ? pixKeyHex : toHex(pixKeyHex); + typeof pixKeyHex === "string" ? pixKeyHex : toHex(pixKeyHex as bigint); if (!hexString) throw new Error("PixKey not found"); const bytes = new Uint8Array( - // @ts-ignore hexString .slice(2) - .match(/.{1,2}/g) + .match(/.{1,2}/g)! .map((byte: string) => parseInt(byte, 16)) ); // Remove null bytes from the end of the string @@ -60,13 +58,13 @@ const getValidDeposits = async ( network: NetworkEnum, contractInfo?: { client: any; address: string } ): Promise => { - let client: PublicClient, address, abi; + let client: PublicClient, abi; if (contractInfo) { - ({ client, address } = contractInfo); + ({ client } = contractInfo); abi = p2pix.abi; } else { - ({ address, abi, client } = await getContract(true)); + ({ abi, client } = await getContract(true)); } // TODO: Remove this once we have a subgraph for rootstock @@ -114,7 +112,7 @@ const getValidDeposits = async ( const sellersList = Object.keys(uniqueSellers); // Use multicall to batch all getBalance requests const balanceCalls = sellersList.map((seller) => ({ - address: getP2PixAddress(network), + address: getP2PixAddress(network) as `0x${string}`, abi, functionName: "getBalance", args: [seller, token], @@ -147,25 +145,31 @@ const getUnreleasedLockById = async ( lockID: string ): Promise => { const { address, abi, client } = await getContract(); - const pixData: Pix = { - pixKey: "", - }; const lock = await client.readContract({ - address, + address: address as `0x${string}`, abi, functionName: "mapLocks", args: [BigInt(lockID)], }); - const pixTarget = lock.pixTarget; - const amount = formatEther(lock.amount); - pixData.pixKey = pixTarget; - pixData.value = Number(amount); + // Type the lock result as an array (based on the smart contract structure) + const lockData = lock as [ + bigint, + string, + string, + bigint, + string, + string, + string + ]; + const amount = formatEther(lockData[3]); return { lockID: lockID, - pix: pixData, + amount: Number(amount), + tokenAddress: lockData[4] as `0x${string}`, + sellerAddress: lockData[6] as `0x${string}`, }; }; diff --git a/src/blockchain/provider.ts b/src/blockchain/provider.ts index ab813d4..270cf10 100644 --- a/src/blockchain/provider.ts +++ b/src/blockchain/provider.ts @@ -15,7 +15,7 @@ import { useUser } from "@/composables/useUser"; let publicClient: PublicClient | null = null; let walletClient: WalletClient | null = null; -const getPublicClient: PublicClient | null = (onlyRpcProvider = false) => { +const getPublicClient = (onlyRpcProvider = false): PublicClient | null => { if (onlyRpcProvider) { const user = useUser(); const rpcUrl = getProviderUrl(); @@ -28,7 +28,7 @@ const getPublicClient: PublicClient | null = (onlyRpcProvider = false) => { return publicClient; }; -const getWalletClient: WalletClient | null = () => { +const getWalletClient = (): WalletClient | null => { return walletClient; }; @@ -38,13 +38,16 @@ const getContract = async (onlyRpcProvider = false) => { const abi = p2pix.abi; const wallet = getWalletClient(); + if (!client) { + throw new Error("Public client not initialized"); + } + const [account] = wallet ? await wallet.getAddresses() : [""]; return { address, abi, client, wallet, account }; }; const connectProvider = async (p: any): Promise => { - console.log("Connecting to wallet provider..."); const user = useUser(); const chain = Number(user.networkName.value) === sepolia.id ? sepolia : rootstock; diff --git a/src/blockchain/sellerMethods.ts b/src/blockchain/sellerMethods.ts index dcd3aa8..7201449 100644 --- a/src/blockchain/sellerMethods.ts +++ b/src/blockchain/sellerMethods.ts @@ -1,6 +1,7 @@ import { getContract, getPublicClient, getWalletClient } from "./provider"; import { getTokenAddress, getP2PixAddress } from "./addresses"; import { parseEther, toHex } from "viem"; +import { sepolia, rootstock } from "viem/chains"; import mockToken from "../utils/smart_contract_files/MockToken.json"; import { useUser } from "@/composables/useUser"; @@ -24,20 +25,25 @@ const approveTokens = async (participant: Participant): Promise => { // Check if the token is already approved const allowance = await publicClient.readContract({ - address: tokenAddress, + address: tokenAddress as `0x${string}`, abi: mockToken.abi, functionName: "allowance", args: [account, getP2PixAddress()], }); - if (allowance < parseEther(participant.offer.toString())) { + if ((allowance as bigint) < parseEther(participant.offer.toString())) { // Approve tokens + const chain = user.networkId.value === sepolia.id ? sepolia : rootstock; const hash = await walletClient.writeContract({ - address: tokenAddress, + address: tokenAddress as `0x${string}`, abi: mockToken.abi, functionName: "approve", - args: [getP2PixAddress(), parseEther(participant.offer.toString())], + args: [ + getP2PixAddress() as `0x${string}`, + parseEther(participant.offer.toString()), + ], account, + chain, }); await publicClient.waitForTransactionReceipt({ hash }); @@ -59,19 +65,23 @@ const addDeposit = async (): Promise => { const sellerId = await createParticipant(user.seller.value); user.setSellerId(sellerId.id); - + if (!sellerId.id) { + throw new Error("Failed to create participant"); + } + const chain = user.networkId.value === sepolia.id ? sepolia : rootstock; const hash = await walletClient.writeContract({ - address, + address: address as `0x${string}`, abi, functionName: "deposit", args: [ - user.networkId + "-" + sellerId.id, + user.networkId.value + "-" + sellerId.id, toHex("", { size: 32 }), - getTokenAddress(user.selectedToken.value), + getTokenAddress(user.selectedToken.value) as `0x${string}`, parseEther(user.seller.value.offer.toString()), true, ], account, + chain, }); const receipt = await client.waitForTransactionReceipt({ hash }); diff --git a/src/blockchain/wallet.ts b/src/blockchain/wallet.ts index 338dd5d..1766113 100644 --- a/src/blockchain/wallet.ts +++ b/src/blockchain/wallet.ts @@ -1,17 +1,14 @@ -import { decodeEventLog, formatEther, type Log } from "viem"; +import { formatEther } from "viem"; import { useUser } from "@/composables/useUser"; import { getPublicClient, getWalletClient, getContract } from "./provider"; import { getTokenAddress } from "./addresses"; -import p2pix from "@/utils/smart_contract_files/P2PIX.json"; - import { getValidDeposits } from "./events"; import type { ValidDeposit } from "@/model/ValidDeposit"; import type { WalletTransaction } from "@/model/WalletTransaction"; import type { UnreleasedLock } from "@/model/UnreleasedLock"; -import type { Pix } from "@/model/Pix"; import { getNetworkSubgraphURL } from "@/model/NetworkEnum"; export const updateWalletStatus = async (): Promise => { @@ -55,54 +52,12 @@ export const listValidDepositTransactionsByWalletAddress = async ( const getLockStatus = async (id: bigint): Promise => { const { address, abi, client } = await getContract(); const result = await client.readContract({ - address, + address: address as `0x${string}`, abi, functionName: "getLocksStatus", args: [[id]], }); - return result[1][0]; -}; - -const filterLockStatus = async ( - transactions: Log[] -): Promise => { - const txs: WalletTransaction[] = []; - - for (const transaction of transactions) { - try { - const decoded = decodeEventLog({ - abi: p2pix.abi, - data: transaction.data, - topics: transaction.topics, - }); - - if (!decoded || !decoded.args) continue; - - // Type assertion to handle the args safely - const args = decoded.args as Record; - - const tx: WalletTransaction = { - token: args.token ? String(args.token) : "", - blockNumber: Number(transaction.blockNumber), - amount: args.amount ? Number(formatEther(args.amount)) : -1, - seller: args.seller ? String(args.seller) : "", - buyer: args.buyer ? String(args.buyer) : "", - event: decoded.eventName || "", - lockStatus: - decoded.eventName == "LockAdded" && args.lockID - ? await getLockStatus(args.lockID) - : -1, - transactionHash: transaction.transactionHash - ? transaction.transactionHash - : "", - transactionID: args.lockID ? args.lockID.toString() : "", - }; - txs.push(tx); - } catch (error) { - console.error("Error decoding log", error); - } - } - return txs; + return (result as any)[1][0] as number; }; export const listAllTransactionByWalletAddress = async ( @@ -118,6 +73,7 @@ export const listAllTransactionByWalletAddress = async ( query: ` { depositAddeds(where: {seller: "${walletAddress.toLowerCase()}"}) { + id seller token amount @@ -129,7 +85,6 @@ export const listAllTransactionByWalletAddress = async ( buyer lockID seller - token amount blockTimestamp blockNumber @@ -138,7 +93,6 @@ export const listAllTransactionByWalletAddress = async ( lockReleaseds(where: {buyer: "${walletAddress.toLowerCase()}"}) { buyer lockId - e2eId blockTimestamp blockNumber transactionHash @@ -155,7 +109,6 @@ export const listAllTransactionByWalletAddress = async ( `, }; - console.log("Fetching transactions from subgraph"); const response = await fetch(getNetworkSubgraphURL(network), { method: "POST", headers: { @@ -165,14 +118,12 @@ export const listAllTransactionByWalletAddress = async ( }); const data = await response.json(); - console.log("Subgraph data fetched:", data); - + console.log("Subgraph data:", data); // Convert all transactions to common WalletTransaction format const transactions: WalletTransaction[] = []; // Process deposit added events if (data.data?.depositAddeds) { - console.log("Processing deposit events"); for (const deposit of data.data.depositAddeds) { transactions.push({ token: deposit.token, @@ -189,7 +140,6 @@ export const listAllTransactionByWalletAddress = async ( // Process lock added events if (data.data?.lockAddeds) { - console.log("Processing lock events"); for (const lock of data.data.lockAddeds) { // Get lock status from the contract const lockStatus = await getLockStatus(BigInt(lock.lockID)); @@ -210,7 +160,6 @@ export const listAllTransactionByWalletAddress = async ( // Process lock released events if (data.data?.lockReleaseds) { - console.log("Processing release events"); for (const release of data.data.lockReleaseds) { transactions.push({ token: "", // Subgraph doesn't provide token in this event, we could enhance this later @@ -228,7 +177,6 @@ export const listAllTransactionByWalletAddress = async ( // Process deposit withdrawn events if (data.data?.depositWithdrawns) { - console.log("Processing withdrawal events"); for (const withdrawal of data.data.depositWithdrawns) { transactions.push({ token: withdrawal.token, @@ -325,7 +273,6 @@ const listLockTransactionByWalletAddress = async (walletAddress: string) => { buyer lockID seller - token amount blockTimestamp blockNumber @@ -386,7 +333,6 @@ const listLockTransactionByWalletAddress = async (walletAddress: string) => { const listLockTransactionBySellerAddress = async (sellerAddress: string) => { const user = useUser(); const network = user.networkName.value; - console.log("Will get locks as seller", sellerAddress); // Query subgraph for lock added transactions where seller matches const subgraphQuery = { @@ -459,10 +405,6 @@ export const checkUnreleasedLock = async ( walletAddress: string ): Promise => { const { address, abi, client } = await getContract(); - const pixData: Pix = { - pixKey: "", - }; - const addedLocks = await listLockTransactionByWalletAddress(walletAddress); if (!addedLocks.length) return undefined; @@ -470,34 +412,43 @@ export const checkUnreleasedLock = async ( const lockIds = addedLocks.map((lock: any) => lock.args.lockID); const lockStatus = await client.readContract({ - address, + address: address as `0x${string}`, abi, functionName: "getLocksStatus", args: [lockIds], }); - const unreleasedLockId = lockStatus[1].findIndex( + const lockStatusResult = lockStatus as [bigint[], number[]]; + const unreleasedLockId = lockStatusResult[1].findIndex( (status: number) => status == 1 ); if (unreleasedLockId !== -1) { - const lockID = lockStatus[0][unreleasedLockId]; + const lockID = lockStatusResult[0][unreleasedLockId]; const lock = await client.readContract({ - address, + address: address as `0x${string}`, abi, functionName: "mapLocks", args: [lockID], }); - const pixTarget = lock.pixTarget; - const amount = formatEther(lock.amount); - pixData.pixKey = pixTarget; - pixData.value = Number(amount); + const lockData = lock as [ + bigint, + string, + string, + bigint, + string, + string, + string + ]; + const amount = formatEther(lockData[0]); return { - lockID, - pix: pixData, + lockID: lockID.toString(), + amount: Number(amount), + sellerAddress: lockData[1] as `0x${string}`, + tokenAddress: lockData[4] as `0x${string}`, }; } }; @@ -513,15 +464,16 @@ export const getActiveLockAmount = async ( const lockIds = lockSeller.map((lock: any) => lock.args.lockID); const lockStatus = await client.readContract({ - address, + address: address as `0x${string}`, abi, functionName: "getLocksStatus", args: [lockIds], }); - const mapLocksRequests = lockStatus[0].map((id: bigint) => + const lockStatusResult = lockStatus as [bigint[], number[]]; + const mapLocksRequests = lockStatusResult[0].map((id: bigint) => client.readContract({ - address, + address: address as `0x${string}`, abi, functionName: "mapLocks", args: [id], @@ -533,9 +485,24 @@ export const getActiveLockAmount = async ( }); return mapLocksResults.reduce((total: number, lock: any, index: number) => { - if (lockStatus[1][index] === 1) { + if (lockStatusResult[1][index] === 1) { return total + Number(formatEther(lock.amount)); } return total; }, 0); }; + +export const getSellerParticipantId = async ( + sellerAddress: string, + tokenAddress: string +): Promise => { + const { address, abi, client } = await getContract(); + + const participantId = await client.readContract({ + address: address as `0x${string}`, + abi, + functionName: "getPixTarget", + args: [sellerAddress, tokenAddress], + }); + return participantId as string; +}; diff --git a/src/components/BuyConfirmedComponent/__tests__/BuyConfirmedComponent.spec.ts b/src/components/BuyConfirmedComponent/__tests__/BuyConfirmedComponent.spec.ts index f860cf5..a64e8a1 100644 --- a/src/components/BuyConfirmedComponent/__tests__/BuyConfirmedComponent.spec.ts +++ b/src/components/BuyConfirmedComponent/__tests__/BuyConfirmedComponent.spec.ts @@ -1,12 +1,7 @@ import { mount } from "@vue/test-utils"; import BuyConfirmedComponent from "../BuyConfirmedComponent.vue"; -import { createPinia, setActivePinia } from "pinia"; describe("BuyConfirmedComponent.vue", async () => { - beforeEach(() => { - setActivePinia(createPinia()); - }); - const wrapper = mount(BuyConfirmedComponent, { props: { tokenAmount: 1, diff --git a/src/components/CustomModal/CustomModal.vue b/src/components/CustomModal/CustomModal.vue index 5bb5495..bc99912 100644 --- a/src/components/CustomModal/CustomModal.vue +++ b/src/components/CustomModal/CustomModal.vue @@ -19,7 +19,7 @@ if (props.isRedirectModal) {