diff --git a/src/App.vue b/src/App.vue index 2d1e23d..02ab545 100644 --- a/src/App.vue +++ b/src/App.vue @@ -26,6 +26,9 @@ const web3Onboard = init({ rpcUrl: import.meta.env.VITE_ROOTSTOCK_API_URL, }, ], + connect: { + autoConnectLastWallet: true, + }, }); const { connectedWallet } = useOnboard(); diff --git a/src/blockchain/addresses.ts b/src/blockchain/addresses.ts index 070472a..f957138 100644 --- a/src/blockchain/addresses.ts +++ b/src/blockchain/addresses.ts @@ -1,23 +1,22 @@ import { useEtherStore } from "@/store/ether"; import { NetworkEnum, TokenEnum } from "@/model/NetworkEnum"; +import { JsonRpcProvider } from "ethers"; const Tokens: { [key in NetworkEnum]: { [key in TokenEnum]: string } } = { [NetworkEnum.sepolia]: { BRZ: "0x3eBE67A2C7bdB2081CBd34ba3281E90377462289", - BRX: "0x3eBE67A2C7bdB2081CBd34ba3281E90377462289", - }, - [NetworkEnum.polygon]: { - BRZ: "0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29", - BRX: "0xC86042E9F2977C62Da8c9dDF7F9c40fde4796A29", + // BRX: "0x3eBE67A2C7bdB2081CBd34ba3281E90377462289", }, [NetworkEnum.rootstock]: { BRZ: "0xfE841c74250e57640390f46d914C88d22C51e82e", - BRX: "0xfE841c74250e57640390f46d914C88d22C51e82e", - } + // BRX: "0xfE841c74250e57640390f46d914C88d22C51e82e", + }, }; export const getTokenByAddress = (address: string) => { - for (const [, network] of Object.entries(NetworkEnum)) { + for (const network of Object.values(NetworkEnum).filter( + (v) => !isNaN(Number(v)) + )) { for (const token of Object.keys(Tokens[network as NetworkEnum])) { if (address === Tokens[network as NetworkEnum][token as TokenEnum]) { return token as TokenEnum; @@ -37,26 +36,29 @@ const getP2PixAddress = (network?: NetworkEnum): string => { const etherStore = useEtherStore(); const possibleP2PixAddresses: { [key in NetworkEnum]: string } = { [NetworkEnum.sepolia]: "0xb7cD135F5eFD9760981e02E2a898790b688939fe", - [NetworkEnum.polygon]: "0x4A2886EAEc931e04297ed336Cc55c4eb7C75BA00", [NetworkEnum.rootstock]: "0x98ba35eb14b38D6Aa709338283af3e922476dE34", }; return possibleP2PixAddresses[network ? network : etherStore.networkName]; }; -const getProviderUrl = (): string => { +const getProviderUrl = (network?: NetworkEnum): string => { const etherStore = useEtherStore(); const possibleProvidersUrls: { [key in NetworkEnum]: string } = { [NetworkEnum.sepolia]: import.meta.env.VITE_SEPOLIA_API_URL, - [NetworkEnum.polygon]: import.meta.env.VITE_MUMBAI_API_URL, [NetworkEnum.rootstock]: import.meta.env.VITE_RSK_API_URL, }; - return possibleProvidersUrls[etherStore.networkName]; + return possibleProvidersUrls[network || etherStore.networkName]; +}; + +const getProviderByNetwork = (network: NetworkEnum): JsonRpcProvider => { + console.log("network", network); + return new JsonRpcProvider(getProviderUrl(network), network); }; const isPossibleNetwork = (networkChain: NetworkEnum): boolean => { - return (Number(networkChain) in NetworkEnum); + return Number(networkChain) in NetworkEnum; }; export { @@ -64,4 +66,5 @@ export { getProviderUrl, isPossibleNetwork, getP2PixAddress, + getProviderByNetwork, }; diff --git a/src/blockchain/events.ts b/src/blockchain/events.ts index 59db0ec..b9d98cf 100644 --- a/src/blockchain/events.ts +++ b/src/blockchain/events.ts @@ -1,59 +1,64 @@ import { useEtherStore } from "@/store/ether"; -import { Contract, formatEther, Interface, JsonRpcProvider } from "ethers"; +import { Contract, formatEther, Interface } from "ethers"; import p2pix from "@/utils/smart_contract_files/P2PIX.json"; import { getContract } from "./provider"; import type { ValidDeposit } from "@/model/ValidDeposit"; -import { getP2PixAddress, getTokenAddress } from "./addresses"; +import { + getP2PixAddress, + getProviderByNetwork, + getTokenAddress, +} from "./addresses"; import { NetworkEnum } from "@/model/NetworkEnum"; import type { UnreleasedLock } from "@/model/UnreleasedLock"; import type { Pix } from "@/model/Pix"; const getNetworksLiquidity = async (): Promise => { const etherStore = useEtherStore(); - const sepoliaProvider = new JsonRpcProvider( - import.meta.env.VITE_SEPOLIA_API_URL, - 11155111 - ); // sepolia provider - const rootstockProvider = new JsonRpcProvider( - import.meta.env.VITE_RSK_API_URL, - 31 - ); // rootstock provider - - const p2pContractSepolia = new Contract( - getP2PixAddress(NetworkEnum.sepolia), - p2pix.abi, - sepoliaProvider - ); - const p2pContractRootstock = new Contract( - getP2PixAddress(NetworkEnum.rootstock), - p2pix.abi, - rootstockProvider - ); - etherStore.setLoadingNetworkLiquidity(true); - const depositListSepolia = await getValidDeposits( - getTokenAddress(etherStore.selectedToken, NetworkEnum.sepolia), - p2pContractSepolia - ); - // const depositListMumbai = await getValidDeposits( - // getTokenAddress(etherStore.selectedToken, NetworkEnum.polygon), - // p2pContractMumbai - // ); - const depositListRootstock = await getValidDeposits( - getTokenAddress(etherStore.selectedToken, NetworkEnum.rootstock), - p2pContractRootstock - ); + const depositLists: ValidDeposit[][] = []; - etherStore.setDepositsValidListSepolia(depositListSepolia); - // etherStore.setDepositsValidListMumbai(depositListMumbai); - etherStore.setDepositsValidListRootstock(depositListRootstock); + for (const network of Object.values(NetworkEnum).filter( + (v) => !isNaN(Number(v)) + )) { + console.log("getNetworksLiquidity", network); + const p2pContract = new Contract( + getP2PixAddress(network as NetworkEnum), + p2pix.abi, + getProviderByNetwork(network as NetworkEnum) + ); + + depositLists.push( + await getValidDeposits( + getTokenAddress(etherStore.selectedToken, network as NetworkEnum), + network as NetworkEnum, + p2pContract + ) + ); + } + + etherStore.setDepositsValidList(depositLists.flat()); etherStore.setLoadingNetworkLiquidity(false); }; +const getPixKey = async (seller: string, token: string): Promise => { + const p2pContract = await getContract(); + const pixKeyHex = await p2pContract.getPixTarget(seller, token); + // Remove '0x' prefix and convert hex to UTF-8 string + const bytes = new Uint8Array( + pixKeyHex + .slice(2) + .match(/.{1,2}/g) + .map((byte: string) => parseInt(byte, 16)) + ); + // Remove null bytes from the end of the string + return new TextDecoder().decode(bytes).replace(/\0/g, ""); +}; + const getValidDeposits = async ( token: string, + network: NetworkEnum, contract?: Contract ): Promise => { let p2pContract: Contract; @@ -65,8 +70,11 @@ const getValidDeposits = async ( } const filterDeposits = p2pContract.filters.DepositAdded(null); - const eventsDeposits = await p2pContract.queryFilter(filterDeposits); - + const eventsDeposits = await p2pContract.queryFilter( + filterDeposits + // 0, + // "latest" + ); if (!contract) p2pContract = await getContract(); // get metamask provider contract const depositList: { [key: string]: ValidDeposit } = {}; @@ -78,28 +86,22 @@ const getValidDeposits = async ( }); // Get liquidity only for the selected token if (decoded?.args.token != token) continue; - const mappedBalance = await p2pContract.getBalance( decoded.args.seller, token ); - const mappedPixTarget = await p2pContract.getPixTarget( - decoded.args.seller, - token - ); - let validDeposit: ValidDeposit | null = null; - if (mappedBalance._hex) { + if (mappedBalance) { validDeposit = { token: token, blockNumber: deposit.blockNumber, - remaining: Number(formatEther(mappedBalance._hex)), + remaining: Number(formatEther(mappedBalance)), seller: decoded.args.seller, - pixKey: mappedPixTarget, + network, + pixKey: "", }; } - if (validDeposit) depositList[decoded.args.seller + token] = validDeposit; } @@ -127,4 +129,9 @@ const getUnreleasedLockById = async ( }; }; -export { getValidDeposits, getNetworksLiquidity, getUnreleasedLockById }; +export { + getValidDeposits, + getNetworksLiquidity, + getUnreleasedLockById, + getPixKey, +}; diff --git a/src/blockchain/wallet.ts b/src/blockchain/wallet.ts index 1ce91fa..bfbf5f7 100644 --- a/src/blockchain/wallet.ts +++ b/src/blockchain/wallet.ts @@ -25,9 +25,9 @@ export const updateWalletStatus = async (): Promise => { const etherStore = useEtherStore(); const provider = await getProvider(); - const signer = await provider.getSigner(); + const signer = await provider?.getSigner(); - const { chainId } = await provider.getNetwork(); + const { chainId } = await provider?.getNetwork(); if (!isPossibleNetwork(Number(chainId))) { window.alert("Invalid chain!:" + chainId); return; @@ -40,7 +40,7 @@ export const updateWalletStatus = async (): Promise => { signer ); - const walletAddress = await provider.send("eth_requestAccounts", []); + const walletAddress = await provider?.send("eth_requestAccounts", []); const balance = await mockTokenContract.balanceOf(walletAddress[0]); etherStore.setBalance(formatEther(balance)); @@ -52,9 +52,9 @@ export const listValidDepositTransactionsByWalletAddress = async ( ): Promise => { const etherStore = useEtherStore(); const walletDeposits = await getValidDeposits( - getTokenAddress(etherStore.selectedToken) + getTokenAddress(etherStore.selectedToken), + etherStore.networkName ); - if (walletDeposits) { return walletDeposits .filter((deposit) => deposit.seller == walletAddress) @@ -69,7 +69,6 @@ export const listValidDepositTransactionsByWalletAddress = async ( const getLockStatus = async (id: [BigInt]): Promise => { const p2pContract = await getContract(); const res = await p2pContract.getLocksStatus([id]); - return res[1][0]; }; @@ -113,33 +112,41 @@ export const listAllTransactionByWalletAddress = async ( ): Promise => { const p2pContract = await getContract(true); + // Get deposits const filterDeposits = p2pContract.filters.DepositAdded([walletAddress]); const eventsDeposits = await p2pContract.queryFilter( filterDeposits, 0, "latest" ); + console.log("Fetched all wallet deposits"); + // Get locks const filterAddedLocks = p2pContract.filters.LockAdded([walletAddress]); const eventsAddedLocks = await p2pContract.queryFilter( filterAddedLocks, 0, "latest" ); + console.log("Fetched all wallet locks"); + // Get released locks const filterReleasedLocks = p2pContract.filters.LockReleased([walletAddress]); const eventsReleasedLocks = await p2pContract.queryFilter( filterReleasedLocks, 0, "latest" ); + console.log("Fetched all wallet released locks"); + // Get withdrawn deposits const filterWithdrawnDeposits = p2pContract.filters.DepositWithdrawn([ walletAddress, ]); const eventsWithdrawnDeposits = await p2pContract.queryFilter( filterWithdrawnDeposits ); + console.log("Fetched all wallet withdrawn deposits"); const lockStatusFiltered = await filterLockStatus( [ @@ -210,10 +217,13 @@ const listLockTransactionBySellerAddress = async ( sellerAddress: string ): Promise => { const p2pContract = await getContract(true); - + console.log("Will get locks as seller", sellerAddress); const filterAddedLocks = p2pContract.filters.LockAdded(); - const eventsReleasedLocks = await p2pContract.queryFilter(filterAddedLocks); - + const eventsReleasedLocks = await p2pContract.queryFilter( + filterAddedLocks + // 0, + // "latest" + ); return eventsReleasedLocks .map((lock) => { const IPix2Pix = new Interface(p2pix.abi); @@ -264,7 +274,7 @@ export const checkUnreleasedLock = async ( export const getActiveLockAmount = async ( walletAddress: string ): Promise => { - const p2pContract = await getContract(); + const p2pContract = await getContract(true); const lockSeller = await listLockTransactionBySellerAddress(walletAddress); const lockStatus = await p2pContract.getLocksStatus( diff --git a/src/components/SearchComponent.vue b/src/components/SearchComponent.vue index 7339a44..a28d6e3 100644 --- a/src/components/SearchComponent.vue +++ b/src/components/SearchComponent.vue @@ -22,8 +22,7 @@ const { walletAddress, networkName, selectedToken, - depositsValidListSepolia, - depositsValidListMumbai, + depositsValidList, loadingNetworkLiquidity, } = storeToRefs(etherStore); @@ -33,14 +32,13 @@ const tokenDropdownRef = ref(null); // Reactive state const tokenValue = ref(0); const enableConfirmButton = ref(false); -const enableWalletButton = ref(false); const hasLiquidity = ref(true); const validDecimals = ref(true); -const selectedSepoliaDeposit = ref(); -const selectedRootstockDeposit = ref(); +const selectedDeposits = ref(); import ChevronDown from "@/assets/chevronDown.svg"; import { useOnboard } from "@web3-onboard/vue"; +import { getPixKey } from "@/blockchain/events"; // Emits const emit = defineEmits(["tokenBuy"]); @@ -51,12 +49,13 @@ const connectAccount = async (): Promise => { await connectWallet(); }; -const emitConfirmButton = (): void => { - const selectedDeposit = - networkName.value == NetworkEnum.sepolia - ? selectedSepoliaDeposit.value - : selectedRootstockDeposit.value; - emit("tokenBuy", selectedDeposit, tokenValue.value); +const emitConfirmButton = async (): Promise => { + const deposit = selectedDeposits.value?.find( + (d) => d.network === networkName.value + ); + if (!deposit) return; + deposit.pixKey = await getPixKey(deposit.seller, deposit.token); + emit("tokenBuy", deposit, tokenValue.value); }; // Debounce methods @@ -91,45 +90,23 @@ const handleSelectedToken = (token: TokenEnum): void => { // Verify if there is a valid deposit to buy const verifyLiquidity = (): void => { enableConfirmButton.value = false; - selectedSepoliaDeposit.value = undefined; - selectedRootstockDeposit.value = undefined; - selectedRootstockDeposit.value = undefined; - - if (tokenValue.value <= 0) { - enableWalletButton.value = false; - return; - } - - selectedSepoliaDeposit.value = verifyNetworkLiquidity( + const selDeposits = verifyNetworkLiquidity( tokenValue.value, walletAddress.value, - depositsValidListSepolia.value - ); - selectedRootstockDeposit.value = verifyNetworkLiquidity( - tokenValue.value, - walletAddress.value, - depositsValidListMumbai.value + depositsValidList.value ); + selectedDeposits.value = selDeposits; + hasLiquidity.value = !!selDeposits.find( + (d) => d.network === networkName.value + ); enableOrDisableConfirmButton(); - if (selectedSepoliaDeposit.value || selectedRootstockDeposit.value) { - hasLiquidity.value = true; - enableWalletButton.value = true; - } else { - hasLiquidity.value = false; - enableWalletButton.value = true; - } }; const enableOrDisableConfirmButton = (): void => { - if (selectedSepoliaDeposit.value && networkName.value == NetworkEnum.sepolia) - enableConfirmButton.value = true; - else if ( - selectedRootstockDeposit.value && - networkName.value != NetworkEnum.rootstock - ) - enableConfirmButton.value = true; - else enableConfirmButton.value = false; + enableConfirmButton.value = + !!selectedDeposits.value && + !!selectedDeposits.value.find((d) => d.network === networkName.value); }; watch(networkName, (): void => { @@ -204,6 +181,7 @@ watch(walletAddress, (): void => { >
-
+

~ R$ {{ tokenValue.toFixed(2) }}

Polygon image Ethereum image
@@ -274,14 +255,14 @@ watch(walletAddress, (): void => { v-else-if="!hasLiquidity && !loadingNetworkLiquidity" > Atualmente não há liquidez nas redes para sua demandaAtualmente não há liquidez nas rede selecionada para sua + demanda
{ width="24" /> {{ Networks[etherStore.networkName].chainName }} @@ -330,7 +330,7 @@ onClickOutside(infoMenuRef, () => { > Account image {{ formatWalletAddress() }} diff --git a/src/model/ValidDeposit.ts b/src/model/ValidDeposit.ts index 9a862c4..c15eea0 100644 --- a/src/model/ValidDeposit.ts +++ b/src/model/ValidDeposit.ts @@ -1,8 +1,11 @@ +import { NetworkEnum } from "./NetworkEnum"; + export type ValidDeposit = { token: string; blockNumber: number; remaining: number; seller: string; pixKey: string; + network: NetworkEnum; open?: boolean; }; diff --git a/src/store/ether.ts b/src/store/ether.ts index b0694a6..c4bba46 100644 --- a/src/store/ether.ts +++ b/src/store/ether.ts @@ -10,12 +10,7 @@ export const useEtherStore = defineStore("ether", { selectedToken: TokenEnum.BRZ, loadingLock: false, sellerView: false, - // Depósitos válidos para compra SEPOLIA - depositsValidListSepolia: [] as ValidDeposit[], - // Depósitos válidos para compra MUMBAI - depositsValidListMumbai: [] as ValidDeposit[], - // Depósitos válidos para compra ROOTSTOCK - depositsValidListRootstock: [] as ValidDeposit[], + depositsValidList: [] as ValidDeposit[], loadingWalletTransactions: false, loadingNetworkLiquidity: false, }), @@ -38,14 +33,8 @@ export const useEtherStore = defineStore("ether", { setSellerView(sellerView: boolean) { this.sellerView = sellerView; }, - setDepositsValidListSepolia(depositsValidList: ValidDeposit[]) { - this.depositsValidListSepolia = depositsValidList; - }, - setDepositsValidListMumbai(depositsValidList: ValidDeposit[]) { - this.depositsValidListMumbai = depositsValidList; - }, - setDepositsValidListRootstock(depositsValidList: ValidDeposit[]) { - this.depositsValidListRootstock = depositsValidList; + setDepositsValidList(depositsValidList: ValidDeposit[]) { + this.depositsValidList = depositsValidList; }, setLoadingWalletTransactions(isLoadingWalletTransactions: boolean) { this.loadingWalletTransactions = isLoadingWalletTransactions; @@ -54,11 +43,10 @@ export const useEtherStore = defineStore("ether", { this.loadingNetworkLiquidity = isLoadingNetworkLiquidity; }, }, - // Alterar para integrar com mumbai getters: { getValidDepositByWalletAddress: (state) => { return (walletAddress: string) => - state.depositsValidListSepolia + state.depositsValidList .filter((deposit) => deposit.seller == walletAddress) .sort((a, b) => { return b.blockNumber - a.blockNumber; diff --git a/src/utils/networkLiquidity.ts b/src/utils/networkLiquidity.ts index 15622b6..0321646 100644 --- a/src/utils/networkLiquidity.ts +++ b/src/utils/networkLiquidity.ts @@ -4,20 +4,41 @@ const verifyNetworkLiquidity = ( tokenValue: number, walletAddress: string, validDepositList: ValidDeposit[] -): ValidDeposit | undefined => { - const element = validDepositList.find((element) => { - const remaining = element.remaining; - if ( - tokenValue!! <= remaining && - tokenValue!! != 0 && - element.seller !== walletAddress - ) { - return true; - } - return false; - }); +): ValidDeposit[] => { + const filteredDepositList = validDepositList + .filter((element) => { + const remaining = element.remaining; + if ( + tokenValue!! <= remaining && + tokenValue!! != 0 && + element.seller !== walletAddress + ) { + return true; + } + return false; + }) + .sort((a, b) => { + return b.remaining - a.remaining; + }); - return element; + const uniqueNetworkDeposits = filteredDepositList.reduce( + (acc: ValidDeposit[], current) => { + const existingNetwork = acc.find( + (deposit) => deposit.network === current.network + ); + if (!existingNetwork) { + acc.push(current); + } + return acc; + }, + [] + ); + + console.log( + "uniqueNetworkDeposits", + JSON.stringify(uniqueNetworkDeposits, null, 2) + ); + return uniqueNetworkDeposits; }; export { verifyNetworkLiquidity }; diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index b40afed..f08da47 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -79,6 +79,7 @@ const releaseTransaction = async (e2eId: string) => { }; const checkForUnreleasedLocks = async (): Promise => { + console.log("Checking for unreleased locks"); const walletLocks = await checkUnreleasedLock(walletAddress.value); if (walletLocks) { lockID.value = walletLocks.lockID; @@ -107,7 +108,6 @@ if (paramLockID) { }); watch(networkName, async () => { - console.log(walletAddress.value); if (walletAddress.value) await checkForUnreleasedLocks(); }); } diff --git a/src/views/ManageBidsView.vue b/src/views/ManageBidsView.vue index c5f9f76..94b4f2c 100644 --- a/src/views/ManageBidsView.vue +++ b/src/views/ManageBidsView.vue @@ -49,15 +49,19 @@ const callWithdraw = async (amount: string) => { const getWalletTransactions = async () => { etherStore.setLoadingWalletTransactions(true); if (walletAddress.value) { + console.log("Will fetch all required data..."); const walletDeposits = await listValidDepositTransactionsByWalletAddress( walletAddress.value ); + console.log("Fetched deposits"); const allUserTransactions = await listAllTransactionByWalletAddress( walletAddress.value ); + console.log("Fetched all transactions"); activeLockAmount.value = await getActiveLockAmount(walletAddress.value); + console.log("Fetched active lock amount"); if (walletDeposits) { depositList.value = walletDeposits;