<template> <section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> <!-- Breadcrumb --> <div id="ViewBreadcrumb" class="mb-8"> <h1 class="text-lg text-center font-light relative px-7"> <!-- Cancel --> <button @click="$router.go(-1)" class="text-lg text-center px-2 py-1 absolute -left-2 -top-1" > <fa icon="chevron-left"></fa> </button> Derive from Existing Identity </h1> </div> <!-- Import Account Form --> <div> <p class="text-center text-xl mb-4 font-light"> Will increment the maximum known derivation path from the existing seed. </p> <p v-if="didArrays.length > 1"> Choose existing DIDs from same seed phrase to compute derivation. </p> <ul class="mb-4"> <li class="block bg-slate-100 rounded-md flex items-center px-4 py-3 mb-2" v-for="dids in didArrays" :key="dids[0]" @click="switchAccount(dids[0])" > <fa v-if="dids[0] == selectedArrayFirstDid" icon="circle" class="fa-fw text-blue-400 text-xl mr-3" ></fa> <fa v-else icon="circle" class="fa-fw text-slate-400 text-xl mr-3" ></fa> <span class="overflow-hidden"> <div class="text-sm text-slate-500 truncate"> <code>{{ dids.join(",") }}</code> </div> </span> </li> </ul> </div> <div class="mt-8"> <div class="grid grid-cols-1 sm:grid-cols-2 gap-2"> <button @click="incrementDerivation()" class="block w-full text-center text-lg font-bold uppercase bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md" > Increment and Import </button> <button @click="onCancelClick()" type="button" class="block w-full text-center text-md uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-1.5 py-2 rounded-md" > Cancel </button> </div> </div> </section> </template> <script lang="ts"> import { Component, Vue } from "vue-facing-decorator"; import { DEFAULT_ROOT_DERIVATION_PATH, deriveAddress, newIdentifier, nextDerivationPath, } from "@/libs/crypto"; import { accountsDB, db } from "@/db/index"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; @Component({ components: {}, }) export default class ImportAccountView extends Vue { derivationPath = DEFAULT_ROOT_DERIVATION_PATH; didArrays: Array<Array<string>> = []; selectedArrayFirstDid = ""; async mounted() { await accountsDB.open(); const accounts = await accountsDB.accounts.toArray(); const seedDids: Record<string, Array<string>> = {}; accounts.forEach((account) => { const prevDids: Array<string> = seedDids[account.mnemonic] || []; seedDids[account.mnemonic] = prevDids.concat([account.did]); }); this.didArrays = Object.values(seedDids); this.selectedArrayFirstDid = this.didArrays[0][0]; } public onCancelClick() { this.$router.back(); } public switchAccount(did: string) { this.selectedArrayFirstDid = did; } public async incrementDerivation() { await accountsDB.open(); // find the maximum derivation path for the selected DIDs const selectedArray: Array<string> = this.didArrays.find((dids) => dids[0] === this.selectedArrayFirstDid) || []; const allMatchingAccounts = await accountsDB.accounts .where("did") .anyOf(...selectedArray) .toArray(); const accountWithMaxDeriv = allMatchingAccounts[0]; allMatchingAccounts.slice(1).forEach((account) => { if (account.derivationPath > accountWithMaxDeriv.derivationPath) { accountWithMaxDeriv.derivationPath = account.derivationPath; } }); // increment the last number in that max derivation path const newDerivPath = nextDerivationPath(accountWithMaxDeriv.derivationPath); const mne: string = accountWithMaxDeriv.mnemonic; const [address, privateHex, publicHex] = deriveAddress(mne, newDerivPath); const newId = newIdentifier(address, publicHex, privateHex, newDerivPath); try { await accountsDB.accounts.add({ dateCreated: new Date().toISOString(), derivationPath: newDerivPath, did: newId.did, identity: JSON.stringify(newId), mnemonic: mne, publicKeyHex: newId.keys[0].publicKeyHex, }); // record that as the active DID await db.open(); await db.settings.update(MASTER_SETTINGS_KEY, { activeDid: newId.did, }); this.$router.push({ name: "account" }); } catch (err) { console.error("Error saving mnemonic & updating settings:", err); } } } </script>