Skip to content

Commit

Permalink
Merge pull request #270 from getsafle/feature-integrate-bitcoin
Browse files Browse the repository at this point in the history
Feature integrate bitcoin
  • Loading branch information
sshubhamagg authored Oct 31, 2023
2 parents dc8c707 + f2bd53c commit d85c7be
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 48 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ coverage
.coverage
safle-vault.code-workspace
vault_test.json
vault_test_bitcoin.js
11 changes: 10 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -558,4 +558,13 @@

### 1.30.08 (2023-10-09) Nightly version : only for experimental use

* Updated pin while resetting imported wallets
* Updated pin while resetting imported wallets

### 1.31.0 (2023-10-09) Nightly version : only for experimental use

* Integrated upgraded bitcoin controller into vault
* Enable backward compatibility in vault recovery for evm chains for undefined chain parameter in logs
* Generate a default wallet for every chain when generating vault
* Add fallback for recover vault - the default address to be recovered for every chain
* Updated recover vault to create default wallet address for bitcoin for preexisting vaults
* Updated test result for bitcoin
12 changes: 6 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@getsafle/safle-vault",
"version": "1.30.08",
"version": "1.31.0",
"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": {
Expand Down Expand Up @@ -48,7 +48,7 @@
"@getsafle/safle-identity-wallet": "^1.3.0",
"@getsafle/transaction-controller": "^1.9.3",
"@getsafle/vault-arbitrum-controller": "^1.0.7",
"@getsafle/vault-bitcoin-controller": "^1.2.3",
"@getsafle/vault-bitcoin-controller": "^2.0.1",
"@getsafle/vault-bsc-controller": "^1.2.2",
"@getsafle/vault-eth-controller": "^1.4.5",
"@getsafle/vault-mantle-controller": "^1.0.0",
Expand Down
80 changes: 50 additions & 30 deletions src/lib/keyring.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ class Keyring {

if (_.get(this.decryptedVault, `importedWallets.${importedChain}`) !== undefined && this.decryptedVault.importedWallets[importedChain].data.some(element => element.address === address) == true) {
isImportedAddress = true;
} else if (this.decryptedVault[chain] !== undefined && this.decryptedVault[chain].public.some(element => Web3.utils.toChecksumAddress(element.address) === Web3.utils.toChecksumAddress(address)) == true) {
} else if (this.decryptedVault[chain] !== undefined && this.decryptedVault[chain].public.some(element => element.address.toLowerCase() === address.toLowerCase()) == true) {
isImportedAddress = false;
} else {
return { error: ERROR_MESSAGE.ADDRESS_NOT_PRESENT };
Expand Down Expand Up @@ -318,6 +318,12 @@ class Keyring {
return { response: signedMessage.message };
}

if (Chains?.[this.chain]){
const { signedMessage } = await this[this.chain].signMessage(data, address, privateKey);
return { response: signedMessage };

}

return { error: ERROR_MESSAGE.UNSUPPORTED_NON_EVM_FUNCTIONALITY }

}
Expand Down Expand Up @@ -355,7 +361,7 @@ class Keyring {
return { error: ERROR_MESSAGE.INCORRECT_PIN };
};

const { error, response } = await this.exportPrivateKey(rawTx.from.toLowerCase(), pin);
const { error, response } = await this.exportPrivateKey(rawTx.from, pin);

if (error) {
return { error };
Expand Down Expand Up @@ -383,7 +389,15 @@ class Keyring {
return { response: signedTx };
}

const { signedTransaction } = await this[this.chain].signTransaction(rawTx, privateKey);
if(isImported) {
const { signedTransaction } = await this[this.chain].signTransaction(rawTx, privateKey);
return { response: signedTransaction };
}
else{
const { signedTransaction } = await this[this.chain].signTransaction(rawTx);
return { response: signedTransaction };
}


return { response: signedTransaction };
}
Expand Down Expand Up @@ -411,10 +425,11 @@ class Keyring {

const activeChains = await this.getActiveChains();

const valuesToRemove = Object.keys(Chains.evmChains);
const evmChainList = Object.keys(Chains.evmChains);

const filteredChains = activeChains.response.filter(activeChains => !valuesToRemove.includes(activeChains.chain));
const filteredChains = activeChains.response.filter(activeChains => !evmChainList.includes(activeChains.chain));

//generate other chain's keyring instance and add accounts to it as per decrypted vault
if (filteredChains.length > 0) {
filteredChains.forEach(async (chainData) => {
const { response: mnemonic } = await this.exportMnemonic(pin);
Expand All @@ -436,6 +451,7 @@ class Keyring {

const mnemonic = await helper.cryptography(decryptedVault.eth.private.encryptedMnemonic, pin, 'decryption');

// clearing vault state and adding new accounts as per decrypted vault
const restoredVault = await this.keyringInstance.createNewVaultAndRestore(JSON.stringify(encryptionKey), mnemonic);

const numberOfAcc = this.decryptedVault.eth.numberOfAccounts;
Expand Down Expand Up @@ -723,7 +739,7 @@ class Keyring {

const activeChains = await this.getActiveChains();

const valuesToRemove = Object.keys(Chains.evmChains);
const evmChainList = Object.keys(Chains.evmChains);

accounts.evm.generatedWallets = ({ ...decryptedVault.eth.public })

Expand All @@ -733,7 +749,7 @@ class Keyring {
accounts.evm.importedWallets = ({ ...decryptedVault.importedWallets.evmChains.data });
}

const filteredChains = activeChains.response.filter(activeChains => !valuesToRemove.includes(activeChains.chain));
const filteredChains = activeChains.response.filter(activeChains => !evmChainList.includes(activeChains.chain));

let nonEvmAccs = [];

Expand All @@ -743,18 +759,12 @@ class Keyring {

if (containsGenerated) {
nonEvmAccs = decryptedVault[chain].public.filter((address) => address.isDeleted === false);

let result = nonEvmAccs.map(a => { return { address: a.address, label: a.label }});

accounts[chain] = { generatedWallets: [ ...result ] };
accounts[chain] = { generatedWallets: { ...decryptedVault[chain].public } };
}

if (containsImported) {
nonEvmAccs = decryptedVault.importedWallets[chain].data.filter((address) => address.isDeleted === false);

let result = nonEvmAccs.map(a => { return { address: a.address, label: a.label }});

(accounts[chain] === undefined) ? accounts[chain] = { importedWallets: [ ...result ] } : accounts[chain].importedWallets = [ ...result ];
(accounts[chain] === undefined) ? accounts[chain] = { importedWallets: { ...decryptedVault.importedWallets[chain].data } } : accounts[chain].importedWallets = { ...decryptedVault.importedWallets[chain].data };
}
});

Expand All @@ -780,7 +790,7 @@ class Keyring {
return { response: balance };
}

const balance = await Chains[this.chain].getBalance(address, rpcUrl);
const balance = await Chains[this.chain].getBalance(address);

return { response: balance };
}
Expand All @@ -796,7 +806,7 @@ class Keyring {
return { error: ERROR_MESSAGE.INCORRECT_PIN };
};

const { error, response } = await this.exportPrivateKey(address.toLowerCase(), pin);
const { error, response } = await this.exportPrivateKey(address, pin);

if (error) {
return { error };
Expand Down Expand Up @@ -859,12 +869,17 @@ class Keyring {
return { error: ERROR_MESSAGE.ADDRESS_NOT_PRESENT };
}
if (typeof this.decryptedVault[chain].public[objIndex].label === 'string' || this.decryptedVault[chain].public[objIndex].label instanceof String){
const chains = Object.keys(Chains.evmChains);
let obj = chains.reduce(function(acc, curr) {
acc[curr] = newLabel;
return acc;
}, {});
if(chain === 'eth') {
const chains = Object.keys(Chains.evmChains);
let obj = chains.reduce(function(acc, curr) {
acc[curr] = newLabel;
return acc;
}, {});
this.decryptedVault[chain].public[objIndex].label = obj;
}
else {
this.decryptedVault[chain].public[objIndex].label = newLabel;
}
}
else{
(chain === 'eth') ? this.decryptedVault[chain].public[objIndex].label[chainName] = newLabel : this.decryptedVault[chain].public[objIndex].label = newLabel;
Expand All @@ -881,19 +896,24 @@ class Keyring {
}

async resetAllImportedWallets(currentPin, newPin) {
const chain = (Chains.evmChains.hasOwnProperty(this.chain) || this.chain === 'ethereum') ? 'eth' : this.chain;
const importedChain = (chain === 'eth') ? 'evmChains' : chain;

if (_.get(this.decryptedVault, `importedWallets.${importedChain}`) === undefined) {
if (_.get(this.decryptedVault, `importedWallets`) === undefined) {
return null;
}

let data = this.decryptedVault.importedWallets[importedChain].data
for(let i = 0; i < data.length; i++) {
let decryptedPrivKey = await helper.cryptography(data[i].privateKey, currentPin, 'decryption');
let encryptedPrivKey = await helper.cryptography(decryptedPrivKey, newPin, 'encryption');
this.decryptedVault.importedWallets[importedChain].data[i].privateKey = encryptedPrivKey
let importedChains = Object.keys(this.decryptedVault.importedWallets)

for (let importedChain of importedChains)
{
let data = this.decryptedVault.importedWallets[importedChain].data
for(let i = 0; i < data.length; i++) {
let decryptedPrivKey = await helper.cryptography(data[i].privateKey, currentPin, 'decryption');
let encryptedPrivKey = await helper.cryptography(decryptedPrivKey, newPin, 'encryption');
this.decryptedVault.importedWallets[importedChain].data[i].privateKey = encryptedPrivKey

}
}

}


Expand Down
3 changes: 2 additions & 1 deletion src/lib/test/keyring.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ describe('exportPrivateKey' , ()=>{
let result = await vault.exportPrivateKey(null,pin)
}
catch(e){
expect(e.message).toBe('Given address "null" is not a valid Ethereum address.')
expect(e.message).toBe("Cannot read properties of null (reading 'toLowerCase')")
}


Expand Down Expand Up @@ -291,6 +291,7 @@ describe('getActiveChains',()=>{
chains=result.response
expect({
response: [
{ chain: 'bitcoin', symbol: 'BTC' },
{ chain: 'ethereum', symbol: 'ETH' },
{ chain: 'bsc', symbol: 'BSC' },
{ chain: 'polygon', symbol: 'MATIC' },
Expand Down
34 changes: 32 additions & 2 deletions src/lib/vault.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const CryptoJS = require('crypto-js');
const { KeyringController } = require('@getsafle/vault-eth-controller');
const BitcoinKeyringController= require('@getsafle/vault-bitcoin-controller').KeyringController ;
const bip39 = require('bip39');

const helper = require('../utils/helper');
Expand Down Expand Up @@ -49,6 +50,11 @@ class Vault extends Keyring {
this.keyringInstance = keyringController;
}

initializeSupportedChainKeyringController(mnemonic) {
const keyringController = new BitcoinKeyringController({mnemonic:mnemonic});
this["bitcoin"] = keyringController;
}

async generateMnemonic(entropy) {
var mnemonic;

Expand Down Expand Up @@ -84,7 +90,15 @@ 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: '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`
rawVault[chain] = { public: [ { address: addedAcc, isDeleted: false, isImported: false, label: label } ], numberOfAccounts: 1 }
}

const vault = await helper.cryptography(JSON.stringify(rawVault), JSON.stringify(encryptionKey), 'encryption');

Expand Down Expand Up @@ -121,7 +135,23 @@ class Vault extends Keyring {

const numberOfAccounts = accountsArray.length;

const rawVault = { eth: { public: accountsArray, private: privData, numberOfAccounts } }
let rawVault = { eth: { public: accountsArray, private: privData, numberOfAccounts } }

const nonEvmChainList = Object.keys(Chains.nonEvmChains);

//generate other chain's keyring instance and get accounts from logs
let obj = {}
for ( let chainData of nonEvmChainList) {
const { response: mnemonic } = await this.exportMnemonic(pin);

const keyringInstance = await helper.getCoinInstance(chainData.toLowerCase(), mnemonic);

const accArray = await helper.getAccountsFromLogs(keyringInstance, vaultState, recoverMechanism, logs);
const numberOfAcc = accArray.length;

rawVault[chainData.toLowerCase()] = { public: accArray, numberOfAcc }

}

this.decryptedVault = rawVault

Expand Down
Loading

0 comments on commit d85c7be

Please sign in to comment.