diff --git a/composables/zksync/deposit/useMntAndWeth.ts b/composables/zksync/deposit/useMntAndWeth.ts new file mode 100644 index 00000000..281b82bf --- /dev/null +++ b/composables/zksync/deposit/useMntAndWeth.ts @@ -0,0 +1,57 @@ +import { storeToRefs } from "pinia"; + +import type { Token } from "@/types"; + +import { useNetworkStore } from "@/store/network"; + +export default (selectedToken: Ref) => { + const { zkSyncNetworks } = useNetworks(); + const { selectedNetwork } = storeToRefs(useNetworkStore()); + const isMNTSelected = computed(() => { + return selectedToken.value?.symbol === "MNT" && selectedNetwork.value.key === "mantle"; + }); + + const isWETHSelected = computed(() => { + let weths: string[] = []; + zkSyncNetworks.forEach((item) => { + if (item.wethContract) { + weths = weths.concat(item.wethContract.map((e) => e.toLowerCase())); + } + }); + console.log( + "isWETHSelected", + selectedToken.value?.address && weths.some((item) => item === selectedToken.value?.address.toLowerCase()) + ); + return selectedToken.value?.address && weths.some((item) => item === selectedToken.value?.address.toLowerCase()); + }); + + const isMNTOrWETH = computed(() => { + console.log("isMNTOrWETH", isMNTSelected.value || isWETHSelected.value); + return isMNTSelected.value || isWETHSelected.value; + }); + + const warpTipsTitle = computed(() => { + return isMNTSelected.value ? "Wrap your MNT" : isWETHSelected.value ? "Unwrap your WETH" : ""; + }); + + const warpTipsDesc = computed(() => { + return isMNTSelected.value + ? "The zkLink Nova Portal doesn't currently support depositing MNT. You could convert it to WMNT before using the bridge." + : isWETHSelected.value + ? "The zkLink Nova Portal doesn't currently encourage depositing WETH. You could convert it to ETH before using the bridge." + : ""; + }); + + const wrapBtnText = computed(() => { + return isMNTSelected.value ? "Wrap" : isWETHSelected.value ? "Unwrap" : ""; + }); + + return { + isMNTSelected, + isWETHSelected, + isMNTOrWETH, + warpTipsTitle, + warpTipsDesc, + wrapBtnText, + }; +}; diff --git a/composables/zksync/deposit/useTransaction.ts b/composables/zksync/deposit/useTransaction.ts index e118a0cd..e7d028dd 100644 --- a/composables/zksync/deposit/useTransaction.ts +++ b/composables/zksync/deposit/useTransaction.ts @@ -6,9 +6,11 @@ import type { BigNumberish } from "ethers"; import { useZkSyncWalletStore } from "@/store/zksync/wallet"; import { formatError } from "@/utils/formatters"; +import { ETH_ADDRESS } from "@/zksync-web3-nova/src/utils"; export default (getL1Signer: () => Promise) => { const status = ref<"not-started" | "processing" | "waiting-for-signature" | "done">("not-started"); + const wrapStatus = ref<"not-started" | "processing" | "waiting-for-signature" | "sending" | "done">("not-started"); const error = ref(); const ethTransactionHash = ref(); const eraWalletStore = useZkSyncWalletStore(); @@ -63,10 +65,43 @@ export default (getL1Signer: () => Promise) => { } }; + const wrapTransaction = async (transaction: { to: string; tokenAddress: string; amount: BigNumberish }) => { + try { + error.value = undefined; + + wrapStatus.value = "processing"; + const wallet = await getL1Signer(); + if (!wallet) throw new Error("Wallet is not available"); + + await eraWalletStore.walletAddressValidate(); + await validateAddress(transaction.to); + + wrapStatus.value = "waiting-for-signature"; + const cb = () => { + wrapStatus.value = "sending"; + }; + + if (wallet._providerL2().isMantleChain() && transaction.tokenAddress == ETH_ADDRESS) { + await wallet.depositMNT(transaction.amount, cb); + } + const weths = await wallet._providerL2().getWETHContractAddress(); + if (weths.map((item) => item.toLowerCase()).includes(transaction.tokenAddress.toLowerCase())) { + console.log("test isWETH"); + await wallet.unwrapWETH(transaction.tokenAddress, transaction.amount, cb); + } + wrapStatus.value = "done"; + } catch (err) { + error.value = formatError(err as Error); + wrapStatus.value = "not-started"; + } + }; + return { + wrapStatus, status, error, ethTransactionHash, commitTransaction, + wrapTransaction, }; }; diff --git a/views/transactions/Deposit.vue b/views/transactions/Deposit.vue index cae887e4..a6260c74 100644 --- a/views/transactions/Deposit.vue +++ b/views/transactions/Deposit.vue @@ -241,14 +241,28 @@ Allowance approval error: {{ setAllowanceError.message }} - - + + + + + + + + + + + + + + Processing... + Waiting for confirmation + + + {{ isMNTSelected ? "Wrapping..." : "Unwrapping..." }} + + {{ wrapBtnText }} + + (() => { return res; }); -const isMNTSelected = computed(() => { - return selectedToken.value?.symbol === "MNT" && selectedNetwork.value.key === "mantle"; -}); - -const isWETHSelected = computed(() => { - let weths: string[] = []; - zkSyncNetworks.forEach((item) => { - if (item.wethContract) { - weths = weths.concat(item.wethContract.map(e => e.toLowerCase())); - } - }); - return selectedToken.value?.address && weths.some(item => item === selectedToken.value?.address.toLowerCase()); -}); +const { isMNTSelected, isWETHSelected, isMNTOrWETH, warpTipsTitle, warpTipsDesc, wrapBtnText } = + useMntAndWeth(selectedToken); const tokenCustomBridge = computed(() => { if (!selectedToken.value) { @@ -780,13 +800,24 @@ const buttonContinue = () => { } }; +const buttonWrap = () => { + if (continueButtonDisabled.value) { + return; + } + if (step.value === "form") { + makeTransactionWrap(); + } +}; + /* Transaction signing and submitting */ const transfersHistoryStore = useZkSyncTransfersHistoryStore(); const { previousTransactionAddress } = storeToRefs(usePreferencesStore()); const { + wrapStatus, status: transactionStatus, error: transactionError, commitTransaction, + wrapTransaction, } = useTransaction(eraWalletStore.getL1Signer); const { recentlyBridged, ecosystemBannerVisible } = useEcosystemBanner(); const { saveTransaction, waitForCompletion } = useZkSyncTransactionStatusStore(); @@ -862,11 +893,33 @@ const makeTransaction = async () => { } }; +const makeTransactionWrap = async () => { + if (continueButtonDisabled.value) return; + + await wrapTransaction({ + to: transaction.value!.to.address, + tokenAddress: transaction.value!.token.address, + amount: transaction.value!.token.amount, + }); + + if (wrapStatus.value === "done") { + if (isMNTSelected) { + selectedTokenAddress.value = WMNT_CONTRACT; + } else if (isWETHSelected) { + selectedTokenAddress.value = ETH_ADDRESS; + } + wrapStatus.value = "not-started"; + fetchBalances(true).catch(() => undefined); + console.log("wrap done"); + } +}; + const resetForm = () => { address.value = ""; amount.value = ""; step.value = "form"; transactionStatus.value = "not-started"; + wrapStatus.value = "not-started"; transactionInfo.value = undefined; resetSetAllowance(); requestAllowance(); diff --git a/zksync-web3-nova/src/adapters.ts b/zksync-web3-nova/src/adapters.ts index 56eaba3e..7801ef8d 100644 --- a/zksync-web3-nova/src/adapters.ts +++ b/zksync-web3-nova/src/adapters.ts @@ -163,20 +163,26 @@ export function AdapterL1>(Base: TBase) { ); } - async depositMNT(amount: BigNumberish) { + async depositMNT(amount: BigNumberish, cb?: () => void) { const wmntContract = new ethers.Contract(WMNT_CONTRACT, WrappedMNTAbi, this._signerL1()); const { hash } = await wmntContract.deposit({ value: amount }); + if (cb) { + cb(); + } const res = await this._providerL1().waitForTransaction(hash); console.log("approve mnt res: ", res); } - async unwrapWETH(token: Address, amount: BigNumberish) { + async unwrapWETH(token: Address, amount: BigNumberish, cb?: () => void) { const weths = await this._providerL2().getWETHContractAddress(); if (!weths.map((item) => item.toLowerCase().includes(token.toLowerCase()))) { return; } const wethContract = new ethers.Contract(token, WethAbi, this._signerL1()); const { hash } = await wethContract.withdraw(amount); + if (cb) { + cb(); + } await this._providerL1().waitForTransaction(hash); } @@ -193,19 +199,6 @@ export function AdapterL1>(Base: TBase) { overrides?: ethers.PayableOverrides; approveOverrides?: ethers.Overrides; }): Promise { - // handle mnt deposit - let isMntDeposit = false; - if (this._providerL2().isMantleChain() && transaction.token == ETH_ADDRESS) { - isMntDeposit = true; - await this.depositMNT(transaction.amount); - transaction.token = WMNT_CONTRACT; - transaction.approveERC20 = true; - } - const weths = await this._providerL2().getWETHContractAddress(); - if (weths.map((item) => item.toLowerCase()).includes(transaction.token.toLowerCase())) { - await this.unwrapWETH(transaction.token, transaction.amount); - transaction.token = ETH_ADDRESS; - } const depositTx = await this.getDepositTx(transaction); if (transaction.token == ETH_ADDRESS) { @@ -234,9 +227,6 @@ export function AdapterL1>(Base: TBase) { const gasLimit = scaleGasLimit(baseGasLimit); depositTx.gasLimit = gasLimit; } - if (depositTx.gasLimit && isMntDeposit) { - depositTx.gasLimit = depositTx.gasLimit.mul(2); // or the tx would fail - } if (this._providerL2().isZkSyncChain()) { const fee = await zkSyncProvider.attachEstimateFee()({