433 lines
12 KiB
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 & 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>
|