Skip to content

Commit

Permalink
feat:wrap MNT and unwrap WETH
Browse files Browse the repository at this point in the history
  • Loading branch information
zhiyuan li authored and zhiyuan li committed Apr 17, 2024
1 parent 20e6316 commit 4aaa20b
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 38 deletions.
57 changes: 57 additions & 0 deletions composables/zksync/deposit/useMntAndWeth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { storeToRefs } from "pinia";

import type { Token } from "@/types";

import { useNetworkStore } from "@/store/network";

export default (selectedToken: Ref<Token | undefined>) => {
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,
};
};
35 changes: 35 additions & 0 deletions composables/zksync/deposit/useTransaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<L1Signer | undefined>) => {
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<Error | undefined>();
const ethTransactionHash = ref<string | undefined>();
const eraWalletStore = useZkSyncWalletStore();
Expand Down Expand Up @@ -63,10 +65,43 @@ export default (getL1Signer: () => Promise<L1Signer | undefined>) => {
}
};

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,
};
};
93 changes: 73 additions & 20 deletions views/transactions/Deposit.vue
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,28 @@
<CommonErrorBlock v-else-if="setAllowanceError" class="mt-2" @try-again="setTokenAllowance">
Allowance approval error: {{ setAllowanceError.message }}
</CommonErrorBlock>
<CommonHeightTransition
v-if="step === 'form'">


<!--MNT and WETH Tips-->
<CommonHeightTransition v-if="step === 'form'" :opened="!!isMNTOrWETH">
<CommonCardWithLineButtons class="mt-4">
<DestinationItem as="div">
<template #label>
{{ warpTipsTitle }}
</template>
<template #underline> {{ warpTipsDesc }} </template>
<template #image>
<div class="aspect-square h-full w-full rounded-full bg-warning-400 p-3 text-black">
<LockClosedIcon aria-hidden="true" />
</div>
</template>
</DestinationItem>
</CommonCardWithLineButtons>
</CommonHeightTransition>
<!-- MNT and WETH Tips end-->

<CommonHeightTransition
v-if="step === 'form'"
:opened="(!enoughAllowance && !continueButtonDisabled) || !!setAllowanceReceipt"
:opened="((!enoughAllowance && !continueButtonDisabled) || !!setAllowanceReceipt) && !isMNTOrWETH.value"
>
<CommonCardWithLineButtons class="mt-4">
<DestinationItem
Expand Down Expand Up @@ -337,6 +351,24 @@
:opened="setAllowanceStatus === 'waiting-for-signature'"
/>
</template>
<CommonButton
v-else-if="!!isMNTOrWETH"
type="submit"
:disabled="continueButtonDisabled || wrapStatus !== 'not-started'"
variant="primary"
class="w-full"
@click="buttonWrap()"
>
<transition v-bind="TransitionPrimaryButtonText" mode="out-in">
<span v-if="wrapStatus === 'processing'">Processing...</span>
<span v-else-if="wrapStatus === 'waiting-for-signature'">Waiting for confirmation</span>
<span class="flex items-center" v-else-if="wrapStatus === 'sending'">
<CommonSpinner class="mr-2 h-6 w-6" />
{{ isMNTSelected ? "Wrapping..." : "Unwrapping..." }}
</span>
<span v-else> {{ wrapBtnText }}</span>
</transition>
</CommonButton>
<CommonButton
v-else
type="submit"
Expand Down Expand Up @@ -413,9 +445,9 @@ import EthereumTransactionFooter from "@/components/transaction/EthereumTransact
import useAllowance from "@/composables/transaction/useAllowance";
import useMergeToken from "@/composables/transaction/useMergeToken";
import useInterval from "@/composables/useInterval";
import useNetworks from "@/composables/useNetworks";
import useEcosystemBanner from "@/composables/zksync/deposit/useEcosystemBanner";
import useFee from "@/composables/zksync/deposit/useFee";
import useMntAndWeth from "@/composables/zksync/deposit/useMntAndWeth";
import useTransaction from "@/composables/zksync/deposit/useTransaction";

import type { TransactionDestination } from "@/store/destinations";
Expand All @@ -442,7 +474,7 @@ import { checksumAddress, decimalToBigNumber, formatRawTokenPrice, parseTokenAmo
import { silentRouterChange } from "@/utils/helpers";
import { TransitionAlertScaleInOutTransition, TransitionOpacity } from "@/utils/transitions";
import DepositSubmitted from "@/views/transactions/DepositSubmitted.vue";
import { ETH_ADDRESS } from "~/zksync-web3-nova/src/utils";
import { ETH_ADDRESS, WMNT_CONTRACT } from "@/zksync-web3-nova/src/utils";

// const okxIcon = "/img/okx-cryptopedia.svg";
const launchIcon = "/img/launch.svg";
Expand All @@ -461,7 +493,6 @@ const { destinations } = storeToRefs(useDestinationsStore());
const { l1BlockExplorerUrl, selectedNetwork } = storeToRefs(useNetworkStore());
const { l1Tokens, tokensRequestInProgress, tokensRequestError } = storeToRefs(tokensStore);
const { balance, balanceInProgress, balanceError } = storeToRefs(zkSyncEthereumBalance);
const { zkSyncNetworks } = useNetworks();

const toNetworkModalOpened = ref(false);
const fromNetworkModalOpened = ref(false);
Expand Down Expand Up @@ -522,19 +553,8 @@ const selectedToken = computed<Token | undefined>(() => {
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) {
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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();
Expand Down
26 changes: 8 additions & 18 deletions zksync-web3-nova/src/adapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,20 +163,26 @@ export function AdapterL1<TBase extends Constructor<TxSender>>(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);
}

Expand All @@ -193,19 +199,6 @@ export function AdapterL1<TBase extends Constructor<TxSender>>(Base: TBase) {
overrides?: ethers.PayableOverrides;
approveOverrides?: ethers.Overrides;
}): Promise<PriorityOpResponse> {
// 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) {
Expand Down Expand Up @@ -234,9 +227,6 @@ export function AdapterL1<TBase extends Constructor<TxSender>>(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()({
Expand Down

0 comments on commit 4aaa20b

Please sign in to comment.