clear-wallet/src/views/SignTx.vue

433 lines
12 KiB
Vue

<template>
<ion-page>
<ion-header>
<ion-toolbar>
<ion-title>Send Transaction</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-item
><ion-label>Network Name: {{ selectedNetwork?.name }}</ion-label></ion-item
>
<ion-item>
<ion-avatar
v-if="(mainNets as any)[selectedNetwork?.chainId]?.icon"
style="margin-right: 1rem; width: 1.8rem; height: 1.8rem"
>
<img
:alt="selectedNetwork?.name"
:src="getUrl('assets/chain-icons/' + (mainNets as any)[selectedNetwork?.chainId]?.icon)"
/>
</ion-avatar>
<ion-label>Network ID: {{ selectedNetwork?.chainId }}</ion-label>
</ion-item>
<ion-item>
<ion-label>Transaction to Sign &amp; Send</ion-label>
</ion-item>
<ion-item>
Last Balance: {{ userBalance }}
<span
style="font-size: 0.9rem; opacity: 0.7; margin-left: 1rem"
v-if="dollarPrice > 0"
>${{ (dollarPrice * userBalance).toFixed(3) }}</span
>
</ion-item>
<ion-item> Contract: {{ contract }} </ion-item>
<ion-item>
Tx Total Cost: {{ totalCost }}
<span
style="font-size: 0.9rem; opacity: 0.7; margin-left: 1rem"
v-if="dollarPrice > 0"
>${{ (dollarPrice * totalCost).toFixed(3) }}</span
>
</ion-item>
<ion-item>
Gas Fee: {{ gasFee }}
<span
style="font-size: 0.9rem; opacity: 0.7; margin-left: 1rem"
v-if="dollarPrice > 0"
>${{ (dollarPrice * gasFee).toFixed(3) }}</span
>
</ion-item>
<ion-item> Tx value: {{ txValue }} </ion-item>
<ion-item>
Gas Limit: {{ gasLimit }}
<ion-button style="margin-left: 1rem" @click="gasLimitModal = true"
>Set manually</ion-button
>
</ion-item>
<ion-item>
Gas Price: {{ gasPrice }}
<ion-button style="margin-left: 1rem" @click="gasPriceModal = true"
>Set manually</ion-button
>
</ion-item>
<ion-item>
<ion-label>Raw TX:</ion-label>
<ion-textarea
style="overflow-y: scroll"
:rows="10"
:cols="20"
:value="signTxData"
readonly
></ion-textarea>
</ion-item>
<ion-item>
<ion-button @click="onCancel">Cancel</ion-button>
<ion-button :disabled="insuficientBalance" @click="onSign">{{
insuficientBalance ? "Insuficient Balance" : "Send"
}}</ion-button>
</ion-item>
<ion-alert
:is-open="alertOpen"
header="Error"
:message="alertMsg"
:buttons="['OK']"
@didDismiss="alertOpen = false"
></ion-alert>
<ion-list>
<ion-item>Auto-reject Timer: {{ timerReject }}</ion-item>
</ion-list>
<ion-list v-if="gasPriceReFetch">
<ion-item>New Fee price Timer: {{ timerFee }}</ion-item>
</ion-list>
<ion-loading
:is-open="loading"
cssClass="my-custom-class"
message="Please wait..."
:duration="4000"
@didDismiss="loading = false"
>
</ion-loading>
<ion-modal :is-open="gasLimitModal">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="gasLimitModal = false">Close</ion-button>
</ion-buttons>
<ion-title>Set Gas Limit</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-list>
<ion-item>
<ion-label>Limit in units</ion-label>
</ion-item>
<ion-item>
<ion-input v-model="inGasLimit" type="number"></ion-input>
</ion-item>
<ion-item>
<ion-button @click="setGasLimit">Set Price</ion-button>
</ion-item>
</ion-list>
</ion-content>
</ion-modal>
<ion-modal :is-open="gasPriceModal">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="gasPriceModal = false">Close</ion-button>
</ion-buttons>
<ion-title>Set Gas Price</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-list>
<ion-item>
<ion-label>Price in gwei</ion-label>
</ion-item>
<ion-item>
<ion-input v-model="inGasPrice" type="number"></ion-input>
</ion-item>
<ion-item>
<ion-button @click="setGasPrice">Set Price</ion-button>
</ion-item>
</ion-list>
</ion-content>
</ion-modal>
</ion-content>
</ion-page>
</template>
<script lang="ts">
import { defineComponent, ref, Ref } from "vue";
import {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonItem,
IonLabel,
IonButton,
IonAlert,
IonTextarea,
onIonViewWillEnter,
IonList,
IonLoading,
IonModal,
IonButtons,
IonInput,
modalController,
} from "@ionic/vue";
import { ethers } from "ethers";
import { approve, walletPing, walletSendData } from "@/extension/userRequest";
import { useRoute } from "vue-router";
import {
getSelectedNetwork,
getUrl,
getPrices,
numToHexStr,
blockLockout,
unBlockLockout,
getSelectedAccount,
strToHex,
hexTostr,
} from "@/utils/platform";
import { getBalance, getGasPrice, estimateGas } from "@/utils/wallet";
import type { Network } from "@/extension/types";
import { mainNets, chainIdToPriceId } from "@/utils/networks";
import UnlockModal from "@/views/UnlockModal.vue";
import router from "@/router";
export default defineComponent({
components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonItem,
IonLabel,
IonButton,
IonAlert,
IonTextarea,
IonList,
IonLoading,
IonModal,
IonButtons,
IonInput,
},
setup: () => {
const route = useRoute();
const rid = (route?.params?.rid as string) ?? "";
let isError = false;
const decodedParam = hexTostr((route.params?.param as string) ?? "");
const params = JSON.parse(decodedParam);
const signTxData = ref("");
const alertOpen = ref(false);
const alertMsg = ref("");
const loading = ref(true);
const contract = params.to;
const gasPrice = ref(0);
const gasLimit = ref(0);
const totalCost = ref(0);
const gasFee = ref(0);
const userBalance = ref(0);
const txValue = ref(0);
const timerReject = ref(140);
const timerFee = ref(20);
const insuficientBalance = ref(false);
const gasPriceReFetch = ref(true);
const selectedNetwork = (ref(null) as unknown) as Ref<Network>;
const dollarPrice = ref(0);
const gasLimitModal = ref(false);
const gasPriceModal = ref(false);
const inGasPrice = ref(0);
const inGasLimit = ref(0);
let interval = 0;
const bars = ref(0);
if (!rid) {
isError = true;
}
if (!decodedParam) {
isError = true;
} else {
signTxData.value = JSON.stringify(params, null, 2);
}
const openModal = async () => {
const modal = await modalController.create({
component: UnlockModal,
componentProps: {
unlockType: "transaction",
},
});
modal.present();
const { role } = await modal.onWillDismiss();
if (role === "confirm") return true;
return false;
};
const onSign = async () => {
loading.value = true;
const selectedAccount = await getSelectedAccount();
loading.value = false;
if ((selectedAccount.pk ?? "").length !== 66) {
const modalResult = await openModal();
if (modalResult) {
unBlockLockout();
loading.value = true;
approve(rid);
} else {
onCancel();
}
} else {
unBlockLockout();
approve(rid);
}
loading.value = false;
};
const onCancel = () => {
window.close();
if (interval) {
try {
unBlockLockout();
clearInterval(interval);
} catch {
// ignore
}
}
};
const newGasData = () => {
gasFee.value = Number(
ethers.utils.formatUnits(String(gasLimit.value * gasPrice.value), "gwei")
);
txValue.value = Number(ethers.utils.formatEther(params?.value ?? "0x0"));
totalCost.value = gasFee.value + txValue.value;
};
onIonViewWillEnter(async () => {
console.log(params.value);
(window as any)?.resizeTo?.(600, 800);
const pEstimateGas = estimateGas({
to: params?.to ?? "",
from: params?.from ?? "",
data: params?.data ?? "",
value: params?.value ?? "0x0",
});
blockLockout();
const pGasPrice = getGasPrice();
const pBalance = getBalance();
const pGetPrices = getPrices();
selectedNetwork.value = await getSelectedNetwork();
userBalance.value = Number(
ethers.utils.formatEther((await pBalance).toString() ?? "0x0")
);
gasPrice.value = parseInt(
ethers.utils.formatUnits((await pGasPrice).toString() ?? "0x0", "gwei"),
10
);
try {
gasLimit.value = parseInt((await pEstimateGas).toString(), 10);
} catch (err) {
const errorToHex = strToHex(String(err));
router.push(`/contract-error/${rid}/${errorToHex}/${contract}`);
loading.value = false;
return;
}
inGasPrice.value = gasPrice.value;
inGasLimit.value = gasLimit.value;
// console.log( 'test', ethers.utils.formatUnits((await pGasPrice).toString(), "gwei"), ethers.utils.formatUnits(ethers.utils.parseUnits(gasPrice.value.toString(), "gwei"), "gwei") )
newGasData();
if (userBalance.value < totalCost.value) {
insuficientBalance.value = true;
}
const prices = await pGetPrices;
dollarPrice.value =
prices[chainIdToPriceId(selectedNetwork.value?.chainId ?? 0)]?.usd ?? 0;
loading.value = false;
interval = setInterval(async () => {
if (timerReject.value <= 0) {
onCancel();
return;
}
if (gasPriceReFetch.value) {
timerFee.value -= 1;
if (timerFee.value <= 0) {
timerFee.value = 20;
loading.value = true;
gasPrice.value = parseInt(
ethers.utils.formatUnits((await getGasPrice()).toString(), "gwei"),
10
);
newGasData();
loading.value = false;
}
}
timerReject.value -= 1;
bars.value++;
walletPing();
}, 1000) as any;
});
const setGasLimit = () => {
gasLimit.value = inGasLimit.value;
walletSendData(rid, {
gas: numToHexStr(gasLimit.value),
});
newGasData();
gasLimitModal.value = false;
};
const setGasPrice = () => {
gasPrice.value = inGasPrice.value;
gasPriceReFetch.value = false;
walletSendData(rid, {
gasPrice: ethers.utils.parseUnits(gasPrice.value.toString(), "gwei"),
});
newGasData();
gasPriceModal.value = false;
};
return {
signTxData,
onCancel,
alertOpen,
alertMsg,
onSign,
isError,
contract,
txValue,
gasPrice,
gasLimit,
totalCost,
gasFee,
timerReject,
timerFee,
insuficientBalance,
gasPriceReFetch,
userBalance,
bars,
loading,
selectedNetwork,
mainNets,
getUrl,
setGasLimit,
setGasPrice,
dollarPrice,
gasLimitModal,
gasPriceModal,
inGasPrice,
inGasLimit,
};
},
});
</script>