From da2988c1f2a9029b7869e0ce49fc35839988b193 Mon Sep 17 00:00:00 2001 From: SDargarh Date: Thu, 8 Feb 2024 18:00:03 +0530 Subject: [PATCH 1/6] Refactored recover vault logic and account labeling --- CHANGELOG.md | 7 +- package-lock.json | 4 +- package.json | 2 +- src/lib/keyring.js | 25 +++-- src/lib/vault.js | 24 +++-- src/utils/helper.js | 231 ++++++++++++++++++++------------------------ 6 files changed, 147 insertions(+), 146 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 34cc79f..a1bcf1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -628,4 +628,9 @@ ### 2.4.6 (2024-02-2) -* Updated recover vault to handle recurring addresses in logs \ No newline at end of file +* Updated recover vault to handle recurring addresses in logs + +### 2.4.7 (2024-02-08) + +* Refactored recover vault logic and generalized it for evm and non evm chains +* updated labeling for evm and non evm wallet accounts \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index cd41a58..e8a2de0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@getsafle/safle-vault", - "version": "2.4.6", + "version": "2.4.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@getsafle/safle-vault", - "version": "2.4.6", + "version": "2.4.7", "license": "MIT", "dependencies": { "@getsafle/asset-controller": "^1.0.10", diff --git a/package.json b/package.json index 3b5d492..88b382e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@getsafle/safle-vault", - "version": "2.4.6", + "version": "2.4.7", "description": "Safle Vault is a non-custodial, flexible and highly available crypto wallet which can be used to access dapps, send/receive crypto and store identity. Vault SDK is used to manage the vault and provide methods to generate vault, add new accounts, update the state and also enable the user to perform several vault related operations.", "main": "src/index.js", "scripts": { diff --git a/src/lib/keyring.js b/src/lib/keyring.js index 75ae291..6013292 100644 --- a/src/lib/keyring.js +++ b/src/lib/keyring.js @@ -221,6 +221,8 @@ class Keyring { const acc = await this.getAccounts(); if (Chains.evmChains.hasOwnProperty(this.chain) || this.chain === 'ethereum') { + + let labelPrefix = 'EVM' const accounts = await this.keyringInstance.getAccounts(); const keyring = await this.keyringInstance.getKeyringForAccount(accounts[0]); @@ -229,7 +231,7 @@ class Keyring { const newAccount = await this.keyringInstance.getAccounts(); - this.decryptedVault.eth.public.push({ address: newAccount[newAccount.length - 1], isDeleted: false, isImported: false, label: `Wallet ${acc.response.length + 1}` }) + this.decryptedVault.eth.public.push({ address: newAccount[newAccount.length - 1], isDeleted: false, isImported: false, label: `${labelPrefix} Wallet ${acc.response.length + 1}` }) this.decryptedVault.eth.numberOfAccounts++; const encryptedVault = await helper.cryptography(JSON.stringify(this.decryptedVault), JSON.stringify(encryptionKey), 'encryption'); @@ -245,6 +247,8 @@ class Keyring { let newAddress; + let labelPrefix = Chains.nonEvmChains[this.chain] + if (this[this.chain] === undefined) { const keyringInstance = await helper.getCoinInstance(this.chain, mnemonic); @@ -254,14 +258,14 @@ class Keyring { newAddress = address; - const publicData = [ { address, isDeleted: false, isImported: false, label: `${this.chain[0].toUpperCase() + this.chain.slice(1)} Wallet ${acc.response ? acc.response.length + 1 : 1}` } ]; + const publicData = [ { address, isDeleted: false, isImported: false, label: `${labelPrefix} Wallet ${acc.response ? acc.response.length + 1 : 1}` } ]; this.decryptedVault[this.chain] = { public: publicData, numberOfAccounts: 1 }; } else { const { address } = await this[this.chain].addAccount(); newAddress = address; - (this.decryptedVault[this.chain] === undefined) ? this.decryptedVault[this.chain] = { public: [ { address: newAddress, isDeleted: false, isImported: false, label: `${this.chain[0].toUpperCase() + this.chain.slice(1)} Wallet ${acc.response.length + 1}` } ], numberOfAccounts: 1 } : this.decryptedVault[this.chain].public.push({ address: newAddress, isDeleted: false, isImported: false, label: `${this.chain[0].toUpperCase() + this.chain.slice(1)} Wallet ${acc.response.length + 1}` }); + (this.decryptedVault[this.chain] === undefined) ? this.decryptedVault[this.chain] = { public: [ { address: newAddress, isDeleted: false, isImported: false, label: `${labelPrefix} Wallet ${acc.response.length + 1}` } ], numberOfAccounts: 1 } : this.decryptedVault[this.chain].public.push({ address: newAddress, isDeleted: false, isImported: false, label: `${labelPrefix} Wallet ${acc.response.length + 1}` }); this.decryptedVault[this.chain].numberOfAccounts++; } @@ -643,6 +647,8 @@ class Keyring { } if (Chains.evmChains.hasOwnProperty(this.chain) || this.chain === 'ethereum') { + + let labelPrefix = 'EVM' const keyringInstance = await helper.getCoinInstance(this.chain); @@ -663,13 +669,14 @@ class Keyring { } if (this.decryptedVault.importedWallets === undefined) { - this.decryptedVault.importedWallets = { evmChains: { data: [{ address, privateKey: encryptedPrivKey, isDeleted: false, isImported: true, label: `Wallet ${numOfAcc + 1}` }] } }; + this.decryptedVault.importedWallets = { evmChains: { data: [{ address, privateKey: encryptedPrivKey, isDeleted: false, isImported: true, label: `${labelPrefix} Wallet ${numOfAcc + 1}` }] } }; } else if (this.decryptedVault.importedWallets.evmChains === undefined) { - this.decryptedVault.importedWallets.evmChains = { data: [{ address, privateKey: encryptedPrivKey, isDeleted: false, isImported: true, label: `Wallet ${numOfAcc + 1}` }] }; + this.decryptedVault.importedWallets.evmChains = { data: [{ address, privateKey: encryptedPrivKey, isDeleted: false, isImported: true, label: `${labelPrefix} Wallet ${numOfAcc + 1}` }] }; } else { - this.decryptedVault.importedWallets.evmChains.data.push({ address, privateKey: encryptedPrivKey, isDeleted: false, isImported: true, label: `Wallet ${numOfAcc + 1}` }); + this.decryptedVault.importedWallets.evmChains.data.push({ address, privateKey: encryptedPrivKey, isDeleted: false, isImported: true, label: `${labelPrefix} Wallet ${numOfAcc + 1}` }); } } else { + let labelPrefix = Chains.nonEvmChains[this.chain] const { response: mnemonic } = await this.exportMnemonic(pin); if (this[this.chain] === undefined) { @@ -699,16 +706,16 @@ class Keyring { if (this.decryptedVault.importedWallets === undefined) { let object = {}; - const data = [ { address, isDeleted: false, isImported: true, privateKey: encryptedPrivKey, label: `${this.chain[0].toUpperCase() + this.chain.slice(1)} Wallet ${numOfAcc + 1}` } ]; + const data = [ { address, isDeleted: false, isImported: true, privateKey: encryptedPrivKey, label: `${labelPrefix} Wallet ${numOfAcc + 1}` } ]; object[this.chain] = { data }; this.decryptedVault.importedWallets = object; } else if (this.decryptedVault.importedWallets[this.chain] === undefined) { - const data = [ { address, isDeleted: false, isImported: true, privateKey: encryptedPrivKey, label: `${this.chain[0].toUpperCase() + this.chain.slice(1)} Wallet ${numOfAcc + 1}` } ]; + const data = [ { address, isDeleted: false, isImported: true, privateKey: encryptedPrivKey, label: `${labelPrefix} Wallet ${numOfAcc + 1}` } ]; this.decryptedVault.importedWallets[this.chain] = { data }; } else { - this.decryptedVault.importedWallets[this.chain].data.push({ address, isDeleted: false, isImported: true, privateKey: encryptedPrivKey, label: `${this.chain[0].toUpperCase() + this.chain.slice(1)} Wallet ${numOfAcc + 1}` }); + this.decryptedVault.importedWallets[this.chain].data.push({ address, isDeleted: false, isImported: true, privateKey: encryptedPrivKey, label: `${labelPrefix} Wallet ${numOfAcc + 1}` }); } } diff --git a/src/lib/vault.js b/src/lib/vault.js index 1eee836..ac01833 100644 --- a/src/lib/vault.js +++ b/src/lib/vault.js @@ -90,13 +90,13 @@ class Vault extends Keyring { const privData = await helper.generatePrivData(mnemonic, pin); - const rawVault = { eth: { public: [ { address: accounts[0], isDeleted: false, isImported: false, label: 'Wallet 1' } ], private: privData, numberOfAccounts: 1 }} + const rawVault = { eth: { public: [ { address: accounts[0], isDeleted: false, isImported: false, label: 'EVM Wallet 1' } ], private: privData, numberOfAccounts: 1 }} this.initializeSupportedChainKeyringController(mnemonic); for (const chain of Object.keys(Chains.nonEvmChains)) { const {address: addedAcc } = await this[chain].addAccount(); - let label = `${chain.charAt(0).toUpperCase() + chain.substr(1).toLowerCase()} Wallet 1` + let label = `${Chains.nonEvmChains[chain]} Wallet 1` rawVault[chain] = { public: [ { address: addedAcc, isDeleted: false, isImported: false, label: label } ], numberOfAccounts: 1 } } @@ -129,8 +129,14 @@ class Vault extends Keyring { const vaultState = await this.keyringInstance.createNewVaultAndRestore(JSON.stringify(encryptionKey), mnemonic); - const accountsArray = await helper.removeEmptyAccounts(vaultState.keyrings[0].accounts[0], this.keyringInstance, vaultState, unmarshalApiKey, recoverMechanism, logs); - + let accountsArray = []; + if(recoverMechanism === 'transactions') { + accountsArray = await helper.getAccountsFromTransactions(vaultState.keyrings[0].accounts[0], this.keyringInstance, vaultState, unmarshalApiKey) + } + else if (recoverMechanism === 'logs') { + accountsArray = await helper.getAccountsFromLogs('ethereum', this.keyringInstance, vaultState, logs, vaultState.keyrings[0].accounts[0]) + } + const privData = await helper.generatePrivData(mnemonic, pin); const numberOfAccounts = accountsArray.length; @@ -141,14 +147,14 @@ class Vault extends Keyring { //generate other chain's keyring instance and get accounts from logs let obj = {} - for ( let chainData of nonEvmChainList) { - - const keyringInstance = await helper.getCoinInstance(chainData.toLowerCase(), mnemonic); + for ( let chain of nonEvmChainList) { + const keyringInstance = await helper.getCoinInstance(chain.toLowerCase(), mnemonic); - const accArray = await helper.getAccountsFromLogs(keyringInstance, vaultState, recoverMechanism, logs); + let {address} = await keyringInstance.addAccount(); + const accArray = await helper.getAccountsFromLogs(chain, keyringInstance, vaultState, logs, address); const numberOfAcc = accArray.length; - rawVault[chainData.toLowerCase()] = { public: accArray, numberOfAccounts: numberOfAcc } + rawVault[chain.toLowerCase()] = { public: accArray, numberOfAccounts: numberOfAcc } } diff --git a/src/utils/helper.js b/src/utils/helper.js index 68eb121..8cd2e46 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -27,127 +27,126 @@ async function generatePrivData(mnemonic, pin) { return priv; } - -async function removeEmptyAccounts(indexAddress, keyringInstance, vaultState, unmarshalApiKey, recoverMechanism, logs) { +async function getAccountsFromTransactions(indexAddress, keyringInstance, vaultState, unmarshalApiKey) { const keyring = keyringInstance.getKeyringsByType(vaultState.keyrings[0].type); let zeroCounter = 0; - let accountCheckList = []; let accountsArray = []; - accountsArray.push({ address: indexAddress, isDeleted: false, isImported: false, label: 'Wallet 1' }); - accountCheckList.push(indexAddress) - let labelCounter = 2; // as an initial wallet is already created above with label 'Wallet 1' - const chains = Object.keys(Chains.evmChains); - - let newAccountAddr = indexAddress - - if( recoverMechanism === 'logs'){ - for(let i=0; i < logs.length; i++){ - if (logs[i].action === 'add-account' && (chains.includes(logs[i].chain) || logs[i].chain === undefined)){ - if (accountCheckList.includes(newAccountAddr)) { - vaultState = await keyringInstance.addNewAccount(keyring[0]); - newAccountAddr = Web3.utils.toChecksumAddress(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1]) - } - - if (logs[i].address.toLowerCase() !== newAccountAddr.toLowerCase() && !accountCheckList.includes(logs[i].address.toLowerCase())) { - do { - const label = this.createWalletLabels('all', labelCounter); - accountsArray.push({ address: newAccountAddr.toLowerCase(), isDeleted: false, isImported: false, label, isExported: false }); - accountCheckList.push(newAccountAddr.toLowerCase()) - labelCounter++; - let vaultState = await keyringInstance.addNewAccount(keyring[0]); - newAccountAddr = Web3.utils.toChecksumAddress(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1]) - } - while(logs[i].address.toLowerCase() !== newAccountAddr.toLowerCase() && !accountCheckList.includes(logs[i].address.toLowerCase())) - } - - if (logs[i].address.toLowerCase() === newAccountAddr.toLowerCase()) { - const label = this.createWalletLabels('all', labelCounter); - accountsArray.push({ address: newAccountAddr.toLowerCase(), isDeleted: false, isImported: false, label, isExported: false }); - accountCheckList.push(newAccountAddr.toLowerCase()) - labelCounter++; - } - - } - if(logs[i].action === 'delete-account' && (chains.includes(logs[i].chain) || logs[i].chain === undefined)) { - let ind = accountsArray.findIndex((acc) => acc.address === Web3.utils.toChecksumAddress(logs[i].address)) - ind >= 0 ? accountsArray[ind].isDeleted = true : false; + accountsArray.push({ address: indexAddress, isDeleted: false, isImported: false, label: 'EVM Wallet 1' }); + + do { + zeroCounter = 0; + for(let i=0; i < 5; i++) { + const vaultState = await keyringInstance.addNewAccount(keyring[0]); + + const ethActivity = await getETHTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'ethereum', unmarshalApiKey); + const polygonActivity = await getPolygonTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'polygon', unmarshalApiKey); + const bscActivity = await getBSCTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'bsc', unmarshalApiKey); + const label = this.createWalletLabels('EVM', i+2); + + if (!ethActivity && !polygonActivity && !bscActivity) { + accountsArray.push({ address: vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], isDeleted: true, isImported: false, label, isExported: false }); + zeroCounter++; + } else { + accountsArray.push({ address: vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], isDeleted: false, isImported: false, label, isExported: false }); + zeroCounter = 0; } } + } - } else if( recoverMechanism === 'transactions'){ - do { - zeroCounter = 0; - for(let i=0; i < 5; i++) { - const vaultState = await keyringInstance.addNewAccount(keyring[0]); - - const ethActivity = await getETHTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'ethereum', unmarshalApiKey); - const polygonActivity = await getPolygonTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'polygon', unmarshalApiKey); - const bscActivity = await getBSCTransactions(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], 'bsc', unmarshalApiKey); - const label = this.createWalletLabels('all', i+2); + while (zeroCounter < 5 ) + + return accountsArray; +} + +async function getAccountsFromLogs(chain, chainInstance, vaultState, logs, indexAddress) { - if (!ethActivity && !polygonActivity && !bscActivity) { - accountsArray.push({ address: vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], isDeleted: true, isImported: false, label, isExported: false }); - zeroCounter++; - } else { - accountsArray.push({ address: vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1], isDeleted: false, isImported: false, label, isExported: false }); - zeroCounter = 0; - } - } + let accountsArray = []; + const accountCheckSet = new Set(); + const evmChains = Object.keys(Chains.evmChains); + let generatedAddress = indexAddress + let labelCounter = 1; + let keyring, labelPrifix + + if (chain === 'ethereum' || chain === undefined || evmChains.includes(chain)) { + keyring = chainInstance.getKeyringsByType(vaultState.keyrings[0].type); + labelPrifix = 'EVM'; + } else { + labelPrifix = Chains.nonEvmChains[chain]; + } + + // create account sequentially using chain specific keyring instance + const createNewAddress = async (chain, chainInstance) => { + let address + if (chain === 'ethereum' || chain === undefined) { + vaultState = await chainInstance.addNewAccount(keyring[0]) + address = (Web3.utils.toChecksumAddress(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1])).toLowerCase() } - - while (zeroCounter < 5 ) + else { + address = (await chainInstance.addAccount()).address.toLowerCase(); + } + return address; } - return accountsArray; -} -async function getAccountsFromLogs(keyringInstance, vaultState, recoverMechanism, logs) { + // create account data wrt vault structure + const createAccountObject = async (generatedAddress) => { + const label = this.createWalletLabels(labelPrifix, labelCounter++); + return { address: generatedAddress, isDeleted: false, isImported: false, label, isExported: false }; + }; + + // add the initial address in the account array + if (!indexAddress){ + return accountsArray + } + else if(!logs) { + const account = await createAccountObject(indexAddress); + accountsArray.push(account); + accountCheckSet.add(account.address.toLowerCase()); + return accountsArray + } + else { + const account = await createAccountObject(indexAddress); + accountsArray.push(account); + accountCheckSet.add(account.address.toLowerCase()); + } + + + const addAccountIfVerified = async (logAddress) => { - //if mech = transaction - generate one acc for bitcoin + if (accountCheckSet.has(generatedAddress)) { + // If the previous generated address was added in the check set, generate a new address + generatedAddress = await createNewAddress(chain, chainInstance); + } - let accountsArray = []; - let accountCheckList = []; - let {address} = await keyringInstance.addAccount(); - const label = this.createWalletLabels('bitcoin', 1); - accountsArray.push({ address: address, isDeleted: false, isImported: false, label, isExported: false }); - accountCheckList.push(address) - let labelCounter = 2; - const chains = Object.keys(Chains.nonEvmChains); - - if( recoverMechanism === 'logs'){ - for(let i=0; i < logs.length; i++){ - if (logs[i].action === 'add-account' && (chains.includes(logs[i].chain))){ - - if (accountCheckList.includes(address)) { - address = (await keyringInstance.addAccount()).address; - } - - if (logs[i].address.toLowerCase() !== address.toLowerCase() && !accountCheckList.includes(logs[i].address.toLowerCase())) { - do { - const label = this.createWalletLabels('bitcoin', labelCounter); - accountsArray.push({ address: address.toLowerCase(), isDeleted: false, isImported: false, label, isExported: false }); - accountCheckList.push(address.toLowerCase()) - labelCounter++; - address = (await keyringInstance.addAccount()).address; - } - while(logs[i].address.toLowerCase() !== address.toLowerCase() && !accountCheckList.includes(logs[i].address.toLowerCase())) - } - - if (logs[i].address.toLowerCase() === address.toLowerCase()) { - const label = this.createWalletLabels('bitcoin', labelCounter); - accountsArray.push({ address: address.toLowerCase(), isDeleted: false, isImported: false, label, isExported: false }); - accountCheckList.push(address.toLowerCase()) - labelCounter ++; + while (logAddress !== generatedAddress && !accountCheckSet.has(logAddress)) { + // Generate new addresses till the one matching the logAddress, which has not been addded to checkset yet + const account = await createAccountObject(generatedAddress); + accountsArray.push(account); + accountCheckSet.add(account.address.toLowerCase()); + generatedAddress = await createNewAddress(chain, chainInstance); + } + + if (logAddress === generatedAddress) { + // If the address matches, add it to the accounts array + const account = await createAccountObject(generatedAddress); + accountsArray.push(account); + accountCheckSet.add(account.address.toLowerCase()); + } + }; + + for (let log of logs) { + const logAddress = log?.address?.toLowerCase(); + if (log.action === 'add-account' && log?.chain === chain) { + await addAccountIfVerified(logAddress); + } else if (log.action === 'delete-account' && Chains.nonEvmChains.hasOwnProperty(log.chain)) { + const index = accountsArray.findIndex((acc) => acc.address === logAddress); + if (index !== -1) { + accountsArray[index].isDeleted = true; } - - } - if(logs[i].action === 'delete-account' && (chains.includes(logs[i].chain))) { - let ind = accountsArray.findIndex((acc) => acc.address.toLowerCase() === logs[i].address.toLowerCase()) - ind >= 0 ? accountsArray[ind].isDeleted = true : false; - } } } + return accountsArray; } @@ -284,32 +283,16 @@ function validateEncryptionKey(data, encryptionKey, encryptor, isCustomEncryptor } -function createWalletLabels(labelObj = 'all', walletIndex = 1) { - let labels = {}; - - const chains = Object.keys(Chains.evmChains); - - if (labelObj === 'all') { - chains.forEach(chain => labels[chain] = `${chain.charAt(0).toUpperCase() + chain.substr(1).toLowerCase()} Wallet ${walletIndex}` ); - } - else if (labelObj === 'bitcoin') { - labels = `${labelObj.charAt(0).toUpperCase() + labelObj.substr(1).toLowerCase()} Wallet ${walletIndex}`; - } - else { - chains.forEach(chain => { - if (labels[chain] !== undefined) { - labels[chain] = `${chain.charAt(0).toUpperCase() + chain.substr(1).toLowerCase()} Wallet ${walletIndex}`; - } - }) - } - +function createWalletLabels(labelPrefix, walletIndex = 1) { + let labels = `${labelPrefix} Wallet ${walletIndex}`; return labels; } module.exports = { stringToArrayBuffer, generatePrivData, - removeEmptyAccounts, + // removeEmptyAccounts, + getAccountsFromTransactions, getAccountsFromLogs, getCoinInstance, getAssetDetails, From e9d3a5329cf6f8a69596cc7c6157e10135f09b10 Mon Sep 17 00:00:00 2001 From: SDargarh Date: Thu, 8 Feb 2024 18:13:53 +0530 Subject: [PATCH 2/6] check for empty logs updated --- src/utils/helper.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/helper.js b/src/utils/helper.js index 8cd2e46..3ff2f83 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -60,7 +60,7 @@ async function getAccountsFromTransactions(indexAddress, keyringInstance, vaultS return accountsArray; } -async function getAccountsFromLogs(chain, chainInstance, vaultState, logs, indexAddress) { +async function getAccountsFromLogs(chain, chainInstance, vaultState, logs = [], indexAddress) { let accountsArray = []; const accountCheckSet = new Set(); @@ -99,7 +99,7 @@ async function getAccountsFromLogs(chain, chainInstance, vaultState, logs, inde if (!indexAddress){ return accountsArray } - else if(!logs) { + else if(!logs.length) { const account = await createAccountObject(indexAddress); accountsArray.push(account); accountCheckSet.add(account.address.toLowerCase()); From 2c3f95e5098154015acf7f1121d6e7d8f23ad574 Mon Sep 17 00:00:00 2001 From: SDargarh Date: Mon, 12 Feb 2024 22:13:55 +0530 Subject: [PATCH 3/6] Integrated restore account logs for vault recovery --- CHANGELOG.md | 3 +- src/utils/helper.js | 107 +++++++++++++++++++++----------------------- 2 files changed, 54 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1bcf1e..6f876f7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -633,4 +633,5 @@ ### 2.4.7 (2024-02-08) * Refactored recover vault logic and generalized it for evm and non evm chains -* updated labeling for evm and non evm wallet accounts \ No newline at end of file +* updated labeling for evm and non evm wallet accounts +* Integrated restore account logs for vault recovery \ No newline at end of file diff --git a/src/utils/helper.js b/src/utils/helper.js index 3ff2f83..6fbbdda 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -60,94 +60,91 @@ async function getAccountsFromTransactions(indexAddress, keyringInstance, vaultS return accountsArray; } -async function getAccountsFromLogs(chain, chainInstance, vaultState, logs = [], indexAddress) { - - let accountsArray = []; - const accountCheckSet = new Set(); - const evmChains = Object.keys(Chains.evmChains); - let generatedAddress = indexAddress + +async function getAccountsFromLogs(chain, chainInstance, vaultState, logs = [], indexAddress) { + + const accountsMap = new Map(); + let generatedAddress = indexAddress; let labelCounter = 1; - let keyring, labelPrifix - if (chain === 'ethereum' || chain === undefined || evmChains.includes(chain)) { - keyring = chainInstance.getKeyringsByType(vaultState.keyrings[0].type); - labelPrifix = 'EVM'; - } else { - labelPrifix = Chains.nonEvmChains[chain]; + if (!indexAddress) { + return []; } - // create account sequentially using chain specific keyring instance + // Create a new address based on the blockchain type const createNewAddress = async (chain, chainInstance) => { - let address + let address; if (chain === 'ethereum' || chain === undefined) { - vaultState = await chainInstance.addNewAccount(keyring[0]) - address = (Web3.utils.toChecksumAddress(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1])).toLowerCase() - } - else { + keyring = chainInstance.getKeyringsByType(vaultState.keyrings[0].type); + vaultState = await chainInstance.addNewAccount(keyring[0]); + address = (Web3.utils.toChecksumAddress(vaultState.keyrings[0].accounts[vaultState.keyrings[0].accounts.length - 1])).toLowerCase(); + } else { address = (await chainInstance.addAccount()).address.toLowerCase(); } return address; - } + }; - // create account data wrt vault structure + // Create an account object with a label based on the blockchain type const createAccountObject = async (generatedAddress) => { - const label = this.createWalletLabels(labelPrifix, labelCounter++); + const labelPrefix = chain === 'ethereum' || chain === undefined || Chains.evmChains[chain] ? 'EVM' : Chains.nonEvmChains[chain]; + const label = this.createWalletLabels(labelPrefix, labelCounter++); return { address: generatedAddress, isDeleted: false, isImported: false, label, isExported: false }; }; - // add the initial address in the account array + // If indexAddress is empty, return the values of the accounts map if (!indexAddress){ - return accountsArray - } - else if(!logs.length) { - const account = await createAccountObject(indexAddress); - accountsArray.push(account); - accountCheckSet.add(account.address.toLowerCase()); - return accountsArray - } - else { - const account = await createAccountObject(indexAddress); - accountsArray.push(account); - accountCheckSet.add(account.address.toLowerCase()); + return Array.from(accountsMap.values()); + } else { + // Set the indexAddress account in the accounts map + accountsMap.set(indexAddress, await createAccountObject(indexAddress)); } - + // Add account if verified based on the log address const addAccountIfVerified = async (logAddress) => { + if (accountsMap.has(logAddress)) { + let account = accountsMap.get(logAddress); + if (account.isDeleted === true) { + account.isDeleted = false; + } + } - if (accountCheckSet.has(generatedAddress)) { - // If the previous generated address was added in the check set, generate a new address + if (accountsMap.has(generatedAddress)) { generatedAddress = await createNewAddress(chain, chainInstance); } - - while (logAddress !== generatedAddress && !accountCheckSet.has(logAddress)) { - // Generate new addresses till the one matching the logAddress, which has not been addded to checkset yet - const account = await createAccountObject(generatedAddress); - accountsArray.push(account); - accountCheckSet.add(account.address.toLowerCase()); + + while (logAddress !== generatedAddress && !accountsMap.has(logAddress)) { + accountsMap.set(generatedAddress, await createAccountObject(generatedAddress)); generatedAddress = await createNewAddress(chain, chainInstance); } - + if (logAddress === generatedAddress) { - // If the address matches, add it to the accounts array - const account = await createAccountObject(generatedAddress); - accountsArray.push(account); - accountCheckSet.add(account.address.toLowerCase()); + accountsMap.set(generatedAddress, await createAccountObject(generatedAddress)); } }; + // Iterate through the logs and update the accounts map accordingly for (let log of logs) { const logAddress = log?.address?.toLowerCase(); - if (log.action === 'add-account' && log?.chain === chain) { - await addAccountIfVerified(logAddress); - } else if (log.action === 'delete-account' && Chains.nonEvmChains.hasOwnProperty(log.chain)) { - const index = accountsArray.findIndex((acc) => acc.address === logAddress); - if (index !== -1) { - accountsArray[index].isDeleted = true; + if (log?.chain === chain || (chain === 'ethereum' && log?.chain === undefined)) { + if (log.action === 'add-account') { + await addAccountIfVerified(logAddress); + } + else if(log.action === 'restore-account') { + const account = accountsMap.get(logAddress); + if (account) { + account.isDeleted = false; + } + } else if (log.action === 'delete-account') { + const account = accountsMap.get(logAddress); + if (account) { + account.isDeleted = true; } + } } } - return accountsArray; + // Return the values of the accounts map as an array + return Array.from(accountsMap.values()); } async function getETHTransactions(address, network, unmarshalApiKey) { From ed2a4e4ab79982e25fd06b2ca0fad29d6604497e Mon Sep 17 00:00:00 2001 From: SDargarh Date: Tue, 13 Feb 2024 11:00:45 +0530 Subject: [PATCH 4/6] check for empty logs updated --- src/utils/helper.js | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/utils/helper.js b/src/utils/helper.js index 6fbbdda..d19bfbd 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -67,10 +67,6 @@ async function getAccountsFromLogs(chain, chainInstance, vaultState, logs = [], let generatedAddress = indexAddress; let labelCounter = 1; - if (!indexAddress) { - return []; - } - // Create a new address based on the blockchain type const createNewAddress = async (chain, chainInstance) => { let address; @@ -93,11 +89,14 @@ async function getAccountsFromLogs(chain, chainInstance, vaultState, logs = [], // If indexAddress is empty, return the values of the accounts map if (!indexAddress){ - return Array.from(accountsMap.values()); + return []; } else { // Set the indexAddress account in the accounts map accountsMap.set(indexAddress, await createAccountObject(indexAddress)); } + if(!logs.length) { + return Array.from(accountsMap.values()); + } // Add account if verified based on the log address const addAccountIfVerified = async (logAddress) => { From 9ec4537ecebf132a717b7adb8d6ce39acb4ee119 Mon Sep 17 00:00:00 2001 From: SDargarh Date: Tue, 13 Feb 2024 11:02:44 +0530 Subject: [PATCH 5/6] removed comment --- src/utils/helper.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/utils/helper.js b/src/utils/helper.js index d19bfbd..ccb0aea 100644 --- a/src/utils/helper.js +++ b/src/utils/helper.js @@ -287,7 +287,6 @@ function createWalletLabels(labelPrefix, walletIndex = 1) { module.exports = { stringToArrayBuffer, generatePrivData, - // removeEmptyAccounts, getAccountsFromTransactions, getAccountsFromLogs, getCoinInstance, From d47f2747b8978514e452fc80bd35ab7b40bbb501 Mon Sep 17 00:00:00 2001 From: SDargarh Date: Tue, 20 Feb 2024 11:54:00 +0530 Subject: [PATCH 6/6] resolve conflicts --- CHANGELOG.md | 2 +- package-lock.json | 4 ++-- package.json | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee73d1e..0b4126c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -637,7 +637,7 @@ * Removed function `getAssets()` to get the list of assets of all the accounts associated as it is moved to an api service. * Updated avalanche, base and zkEVM controllers. -### 2.4.7 (2024-02-08) +### 2.5.1 (2024-02-20) * Refactored recover vault logic and generalized it for evm and non evm chains * updated labeling for evm and non evm wallet accounts diff --git a/package-lock.json b/package-lock.json index 63a17fb..38b1cc6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@getsafle/safle-vault", - "version": "2.5.0", + "version": "2.5.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@getsafle/safle-vault", - "version": "2.5.0", + "version": "2.5.1", "license": "MIT", "dependencies": { "@getsafle/safle-identity-wallet": "^1.3.0", diff --git a/package.json b/package.json index aada76a..3f7709f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@getsafle/safle-vault", - "version": "2.5.0", + "version": "2.5.1", "description": "Safle Vault is a non-custodial, flexible and highly available crypto wallet which can be used to access dapps, send/receive crypto and store identity. Vault SDK is used to manage the vault and provide methods to generate vault, add new accounts, update the state and also enable the user to perform several vault related operations.", "main": "src/index.js", "scripts": {