added: extract from Mnemonic support when adding account

This commit is contained in:
Andrei O 2022-12-09 19:02:16 +02:00
parent 83b88709d4
commit 0bb63c2941
No known key found for this signature in database
GPG Key ID: B961E5B68389457E
2 changed files with 262 additions and 147 deletions

View File

@ -76,6 +76,12 @@ export const getCode = async (addr: string) => {
return await provider.getCode(addr) return await provider.getCode(addr)
} }
export const getFromMemonic = (memonic: string, index: number) => {
const path = `m/44'/60'/0'/0/${index}`
const wallet = ethers.Wallet.fromMnemonic(memonic, path)
return wallet.privateKey
}
export const sendTransaction = async ({ data= '', gas='0x0', to='', from='', value='0x0', gasPrice='0x0'}: export const sendTransaction = async ({ data= '', gas='0x0', to='', from='', value='0x0', gasPrice='0x0'}:
{to: string, from: string, data: string, value: string, gas: string, gasPrice: string}, {to: string, from: string, data: string, value: string, gas: string, gasPrice: string},
gasEstimate: Promise<BigNumber> | null = null, pGasPrice : Promise<BigNumber> | null) => { gasEstimate: Promise<BigNumber> | null = null, pGasPrice : Promise<BigNumber> | null) => {

View File

@ -2,8 +2,8 @@
<ion-page> <ion-page>
<ion-header> <ion-header>
<ion-toolbar> <ion-toolbar>
<ion-title v-if="!isEdit" >Add Account</ion-title> <ion-title v-if="!isEdit">Add Account</ion-title>
<ion-title v-else >Edit Account</ion-title> <ion-title v-else>Edit Account</ion-title>
</ion-toolbar> </ion-toolbar>
</ion-header> </ion-header>
@ -13,199 +13,308 @@
<ion-input v-model="name"></ion-input> <ion-input v-model="name"></ion-input>
</ion-item> </ion-item>
<ion-item> <ion-item>
<ion-label>Get Random Name</ion-label> <ion-label>Get Random Name</ion-label>
<ion-button @click="getRandomName" >Generate</ion-button> <ion-button @click="getRandomName">Generate</ion-button>
</ion-item> </ion-item>
<ion-item v-if="!isEdit"> <ion-item v-if="!isEdit">
<ion-icon style="margin-right: 0.5rem;" @click="paste('pastePk')" :icon="clipboardOutline" button/> <ion-icon
style="margin-right: 0.5rem"
@click="paste('pastePk')"
:icon="clipboardOutline"
button
/>
<ion-label button>PK</ion-label> <ion-label button>PK</ion-label>
<ion-input id="pastePk" v-model="pk"></ion-input> <ion-input id="pastePk" v-model="pk"></ion-input>
</ion-item> </ion-item>
<ion-item v-if="!isEdit"> <template v-if="!isEdit">
<ion-label>Get Random PK</ion-label> <ion-item>
<ion-button @click="generateRandomPk" >Generate</ion-button> <ion-label>Get Random PK</ion-label>
</ion-item> <ion-button @click="generateRandomPk">Generate</ion-button>
</ion-item>
<ion-item>
<ion-button @click="mnemonicModal = true" expand="full"
>Extarct From A Mnemonic</ion-button
>
</ion-item>
</template>
<ion-item> <ion-item>
<ion-button @click="onCancel">Cancel</ion-button> <ion-button @click="onCancel">Cancel</ion-button>
<ion-button @click="onAddAccount">{{ isEdit ? 'Edit Account' : 'Add Account' }}</ion-button> <ion-button @click="onAddAccount">{{
isEdit ? "Edit Account" : "Add Account"
}}</ion-button>
</ion-item> </ion-item>
<ion-alert <ion-alert
:is-open="alertOpen" :is-open="alertOpen"
header="Error" header="Error"
:message="alertMsg" :message="alertMsg"
:buttons="['OK']" :buttons="['OK']"
@didDismiss="alertOpen=false" @didDismiss="alertOpen = false"
></ion-alert> ></ion-alert>
<ion-modal :is-open="mnemonicModal" @didDismiss="mnemonic = ''">
<ion-header>
<ion-toolbar>
<ion-buttons slot="start">
<ion-button @click="mnemonicModal = false">Close</ion-button>
</ion-buttons>
<ion-title>Extract PK from mnemonic</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-item>
<ion-label>Enter mnemonic</ion-label>
</ion-item>
<ion-item>
<ion-textarea
style="overflow-y: scroll"
:rows="10"
:cols="10"
v-model="mnemonic"
></ion-textarea>
</ion-item>
<ion-item>
<ion-label>Enter Index (default: 0)</ion-label>
<ion-input v-model="mnemonicIndex"></ion-input>
</ion-item>
<ion-item>
<ion-button @click="extractMnemonic">Extract</ion-button>
</ion-item>
</ion-content>
</ion-modal>
</ion-content> </ion-content>
</ion-page> </ion-page>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref } from "vue"; import { defineComponent, ref } from "vue";
import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonInput, IonButton, IonAlert, IonIcon, onIonViewWillEnter, modalController } from "@ionic/vue"; import {
import { ethers } from "ethers" IonContent,
import { saveSelectedAccount, getAccounts, saveAccount, getRandomPk, smallRandomString, paste, getSettings } from "@/utils/platform"; IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonItem,
IonLabel,
IonInput,
IonButton,
IonAlert,
IonIcon,
onIonViewWillEnter,
modalController,
IonModal,
IonButtons,
IonTextarea,
} from "@ionic/vue";
import { ethers } from "ethers";
import {
saveSelectedAccount,
getAccounts,
saveAccount,
getRandomPk,
smallRandomString,
paste,
getSettings,
} from "@/utils/platform";
import router from "@/router"; import router from "@/router";
import { useRoute } from 'vue-router' import { useRoute } from "vue-router";
import type { Account, Settings } from '@/extension/types' import type { Account, Settings } from "@/extension/types";
import UnlockModal from '@/views/UnlockModal.vue' import UnlockModal from "@/views/UnlockModal.vue";
import { encrypt, getCryptoParams } from '@/utils/webCrypto' import { encrypt, getCryptoParams } from "@/utils/webCrypto";
import { clipboardOutline } from "ionicons/icons"; import { clipboardOutline } from "ionicons/icons";
import { getFromMemonic } from "@/utils/wallet";
export default defineComponent({ export default defineComponent({
components: { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonItem, IonLabel, IonInput, IonButton, IonAlert, IonIcon }, components: {
IonContent,
IonHeader,
IonPage,
IonTitle,
IonToolbar,
IonItem,
IonLabel,
IonInput,
IonButton,
IonAlert,
IonIcon,
IonModal,
IonButtons,
IonTextarea,
},
setup: () => { setup: () => {
const name = ref('') const name = ref("");
const pk = ref('') const pk = ref("");
const alertOpen = ref(false) const alertOpen = ref(false);
const alertMsg = ref('') const alertMsg = ref("");
const route = useRoute() const route = useRoute();
const isEdit = route.path.includes('/edit') const isEdit = route.path.includes("/edit");
const paramAddress = route.params.address ?? "" const paramAddress = route.params.address ?? "";
let accountsProm: Promise<Account[] | undefined> const mnemonicModal = ref(false);
let settingsProm: Promise<Settings | undefined> const mnemonic = ref("");
const mnemonicIndex = ref(0);
let accountsProm: Promise<Account[] | undefined>;
let settingsProm: Promise<Settings | undefined>;
const resetFields = () => { const resetFields = () => {
name.value = '' name.value = "";
pk.value = '' pk.value = "";
} };
const openModal = async () => { const openModal = async () => {
const modal = await modalController.create({ const modal = await modalController.create({
component: UnlockModal, component: UnlockModal,
componentProps: { componentProps: {
unlockType: 'addAccount' unlockType: "addAccount",
} },
});
}); modal.present();
modal.present(); const { role, data } = await modal.onWillDismiss();
const { role, data } = await modal.onWillDismiss(); if (role === "confirm") return data;
if(role === 'confirm') return data return false;
return false };
}
onIonViewWillEnter(async () => { onIonViewWillEnter(async () => {
if(isEdit && paramAddress) { if (isEdit && paramAddress) {
accountsProm = getAccounts() accountsProm = getAccounts();
settingsProm = getSettings() settingsProm = getSettings();
const accounts = await accountsProm as Account[] const accounts = (await accountsProm) as Account[];
const acc = accounts.find(account => account.address === paramAddress) const acc = accounts.find((account) => account.address === paramAddress);
if(acc) { if (acc) {
name.value = acc.name name.value = acc.name;
} }
} }
}) });
const onAddAccount = async () => { const onAddAccount = async () => {
let p1 = Promise.resolve() let p1 = Promise.resolve();
if(pk.value.length === 64){ if (pk.value.length === 64) {
pk.value = `0x${pk.value.trim()}` pk.value = `0x${pk.value.trim()}`;
} }
if(pk.value.length !== 66) { if (pk.value.length !== 66) {
alertMsg.value = "Provided private key is invalid." alertMsg.value = "Provided private key is invalid.";
alertOpen.value = true alertOpen.value = true;
return return;
} }
const wallet = new ethers.Wallet(pk.value);
const wallet = new ethers.Wallet(pk.value) if (!accountsProm) {
if(!accountsProm) { accountsProm = getAccounts();
accountsProm = getAccounts() }
if (!settingsProm) {
settingsProm = getSettings();
}
const accounts = (await accountsProm) as Account[];
const settings = (await settingsProm) as Settings;
if (settings.enableStorageEnctyption) {
const pass = await openModal();
if (!pass) {
alertMsg.value = "Cannot add account with encryption password.";
alertOpen.value = true;
return;
} }
if(!settingsProm) { const cryptoParams = await getCryptoParams(pass);
settingsProm = getSettings() if ((accounts.length ?? 0) < 1) {
} p1 = saveSelectedAccount({
const accounts = await accountsProm as Account[] address: wallet.address,
const settings = await settingsProm as Settings name: name.value,
if( settings.enableStorageEnctyption) { pk: pk.value,
const pass = await openModal() encPk: await encrypt(pk.value, cryptoParams),
if(!pass){ });
alertMsg.value = "Cannot add account with encryption password."
alertOpen.value = true
return
}
const cryptoParams = await getCryptoParams(pass)
if((accounts.length ?? 0) < 1 ){
p1 = saveSelectedAccount({
address: wallet.address,
name: name.value,
pk: pk.value,
encPk: await encrypt(pk.value, cryptoParams)
})
} else { } else {
if(accounts.find(account => account.address === wallet.address)){ if (accounts.find((account) => account.address === wallet.address)) {
alertMsg.value = "Account already exists." alertMsg.value = "Account already exists.";
return alertOpen.value = true return (alertOpen.value = true);
} }
} }
const p2 = saveAccount({ const p2 = saveAccount({
address: wallet.address, address: wallet.address,
name: name.value, name: name.value,
pk: pk.value, pk: pk.value,
encPk: await encrypt(pk.value, cryptoParams) encPk: await encrypt(pk.value, cryptoParams),
}) });
await Promise.all([p1, p2]) await Promise.all([p1, p2]);
}else { } else {
if((accounts.length ?? 0) < 1 ){ if ((accounts.length ?? 0) < 1) {
p1 = saveSelectedAccount({ p1 = saveSelectedAccount({
address: wallet.address, address: wallet.address,
name: name.value, name: name.value,
pk: pk.value, pk: pk.value,
encPk: '' encPk: "",
}) });
} else { } else {
if(accounts.find(account => account.address === wallet.address)){ if (accounts.find((account) => account.address === wallet.address)) {
alertMsg.value = "Account already exists." alertMsg.value = "Account already exists.";
return alertOpen.value = true return (alertOpen.value = true);
} }
} }
const p2 = saveAccount({ const p2 = saveAccount({
address: wallet.address, address: wallet.address,
name: name.value, name: name.value,
pk: pk.value, pk: pk.value,
encPk: '' encPk: "",
}) });
await Promise.all([p1, p2]) await Promise.all([p1, p2]);
} }
if(isEdit) { if (isEdit) {
router.push('/tabs/accounts') router.push("/tabs/accounts");
}else { } else {
router.push('/tabs/home') router.push("/tabs/home");
} }
resetFields() resetFields();
} };
const generateRandomPk = () => { const generateRandomPk = () => {
pk.value = getRandomPk() pk.value = getRandomPk();
} };
const getRandomName = () => { const getRandomName = () => {
name.value = smallRandomString() name.value = smallRandomString();
} };
const onCancel = () => { const onCancel = () => {
if(isEdit) { if (isEdit) {
router.push('/tabs/accounts') router.push("/tabs/accounts");
}else { } else {
router.push('/tabs/home') router.push("/tabs/home");
} }
} };
const extractMnemonic = () => {
mnemonic.value = mnemonic.value.trim().replace(/\s+/g, " ");
mnemonicIndex.value = Number(mnemonicIndex.value);
if (mnemonic.value.split(" ").length !== 12) {
alertMsg.value = "Invalid mnemonic.";
alertOpen.value = true;
return;
}
if (mnemonicIndex.value < 0) {
alertMsg.value = "Invalid index.";
alertOpen.value = true;
return;
}
pk.value = getFromMemonic(mnemonic.value, mnemonicIndex.value);
mnemonicModal.value = false;
};
return { return {
name, name,
pk, pk,
onAddAccount, onAddAccount,
onCancel, onCancel,
alertOpen, alertOpen,
alertMsg, alertMsg,
generateRandomPk, generateRandomPk,
getRandomName, getRandomName,
clipboardOutline, clipboardOutline,
paste, paste,
isEdit isEdit,
} mnemonicModal,
mnemonic,
} mnemonicIndex,
extractMnemonic,
};
},
}); });
</script> </script>