mirror of
https://github.com/andrei0x309/clear-wallet.git
synced 2024-11-18 23:41:10 +00:00
added: extract from Mnemonic support when adding account
This commit is contained in:
parent
83b88709d4
commit
0bb63c2941
@ -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) => {
|
||||||
|
@ -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>
|
||||||
|
|
||||||
@ -14,183 +14,289 @@
|
|||||||
</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-item>
|
||||||
<ion-label>Get Random PK</ion-label>
|
<ion-label>Get Random PK</ion-label>
|
||||||
<ion-button @click="generateRandomPk" >Generate</ion-button>
|
<ion-button @click="generateRandomPk">Generate</ion-button>
|
||||||
</ion-item>
|
</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) {
|
if (!settingsProm) {
|
||||||
settingsProm = getSettings()
|
settingsProm = getSettings();
|
||||||
}
|
}
|
||||||
const accounts = await accountsProm as Account[]
|
const accounts = (await accountsProm) as Account[];
|
||||||
const settings = await settingsProm as Settings
|
const settings = (await settingsProm) as Settings;
|
||||||
if( settings.enableStorageEnctyption) {
|
if (settings.enableStorageEnctyption) {
|
||||||
const pass = await openModal()
|
const pass = await openModal();
|
||||||
if(!pass){
|
if (!pass) {
|
||||||
alertMsg.value = "Cannot add account with encryption password."
|
alertMsg.value = "Cannot add account with encryption password.";
|
||||||
alertOpen.value = true
|
alertOpen.value = true;
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const cryptoParams = await getCryptoParams(pass)
|
const cryptoParams = await getCryptoParams(pass);
|
||||||
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: await encrypt(pk.value, cryptoParams)
|
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,
|
||||||
@ -203,9 +309,12 @@ export default defineComponent({
|
|||||||
getRandomName,
|
getRandomName,
|
||||||
clipboardOutline,
|
clipboardOutline,
|
||||||
paste,
|
paste,
|
||||||
isEdit
|
isEdit,
|
||||||
}
|
mnemonicModal,
|
||||||
|
mnemonic,
|
||||||
}
|
mnemonicIndex,
|
||||||
|
extractMnemonic,
|
||||||
|
};
|
||||||
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
Loading…
Reference in New Issue
Block a user