UNPKG

@colony/purser-software

Version:

A javascript library to interact with a software Ethereum wallet, based on the ethers.js library

272 lines (246 loc) 10 kB
import _regeneratorRuntime from "@babel/runtime/regenerator"; import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator"; import { Wallet as EthersWallet } from 'ethers/wallet'; import { isValidMnemonic, fromMnemonic } from 'ethers/utils/hdnode'; import { isSecretStorageWallet } from 'ethers/utils/json-wallet'; import { derivationPathSerializer, userInputValidator } from '@colony/purser-core/helpers'; import { objectToErrorString, getRandomValues, warning } from '@colony/purser-core/utils'; import { PATH, CHAIN_IDS } from '@colony/purser-core/defaults'; import SoftwareWallet from './class'; import { REQUIRED_PROPS as REQUIRED_PROPS_SOFTWARE } from './defaults'; import { staticMethods as messages } from './messages'; /** * Open an existing wallet * Using either `mnemonic`, `private key` or `encrypted keystore` * * This will try to extract the private key from a mnemonic (if available), * and create a new SoftwareWallet instance using whichever key is available. * (the on passed in or the one extracted from the mnemonic). * * @TODO Reduce code repetition * * With some clever refactoring we might be able to only call the SoftwareWallet * constructor a single time for all three methods of opening the wallet * * @method open * * @param {string} password Optional password used to generate an encrypted keystore * @param {string} privateKey Private key to open the wallet with * @param {string} mnemonic Mnemonic string to open the wallet with * @param {string} keystore JSON formatted keystore string to open the wallet with. * Only works if you also send in a password * @param {number} chainId The id of the network to use, defaults to mainnet (1) * * All the above params are sent in as props of an {WalletArgumentsType} object. * * @return {WalletType} A new wallet object (or undefined) if somehwere along * the line an error is thrown. */ export var open = /*#__PURE__*/ function () { var _ref = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee() { var argumentObject, password, privateKey, mnemonic, keystore, _argumentObject$chain, chainId, extractedPrivateKey, derivationPath, keystoreWallet, mnemonicWallet, privateKeyWallet, _args = arguments; return _regeneratorRuntime.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: argumentObject = _args.length > 0 && _args[0] !== undefined ? _args[0] : {}; /* * Validate the trasaction's object input */ userInputValidator({ firstArgument: argumentObject, requiredEither: REQUIRED_PROPS_SOFTWARE.OPEN_WALLET }); password = argumentObject.password, privateKey = argumentObject.privateKey, mnemonic = argumentObject.mnemonic, keystore = argumentObject.keystore, _argumentObject$chain = argumentObject.chainId, chainId = _argumentObject$chain === void 0 ? CHAIN_IDS.HOMESTEAD : _argumentObject$chain; /* * @TODO Re-add use ability to control derivation path * When opening the wallet. But only if this proves to be a needed feature. */ derivationPath = derivationPathSerializer({ change: PATH.CHANGE, addressIndex: PATH.INDEX }); _context.prev = 4; if (!(keystore && isSecretStorageWallet(keystore) && password)) { _context.next = 13; break; } _context.next = 8; return EthersWallet.fromEncryptedJson(keystore, password); case 8: keystoreWallet = _context.sent; /* * Set the keystore and password props on the instance object. * * So that we can make use of them inside the SoftwareWallet * constructor, as the Ethers Wallet instance object will * be passed down. * * @TODO Better passing of values * * This needs to be refactored to pass values to the SoftwareWallet * class in a less repetitious way */ keystoreWallet.keystore = keystore; keystoreWallet.password = password; keystoreWallet.chainId = chainId; return _context.abrupt("return", new SoftwareWallet(keystoreWallet)); case 13: /* * @TODO Detect if existing but not valid mnemonic, and warn the user */ if (mnemonic && isValidMnemonic(mnemonic)) { mnemonicWallet = fromMnemonic(mnemonic).derivePath(derivationPath); extractedPrivateKey = mnemonicWallet.privateKey; } /* * @TODO Detect if existing but not valid private key, and warn the user */ privateKeyWallet = new EthersWallet(privateKey || extractedPrivateKey); /* * Set the mnemonic and password props on the instance object. * * So that we can make use of them inside the SoftwareWallet * constructor, as the Ethers Wallet instance object will * be passed down. * * @TODO Better passing of values * * This needs to be refactored to pass values to the SoftwareWallet * class in a less repetitious way */ privateKeyWallet.password = password; privateKeyWallet.chainId = chainId; /* * @NOTE mnemonic prop was renamed due to naming conflict with getter-only * ethers prop */ privateKeyWallet.originalMnemonic = mnemonic; return _context.abrupt("return", new SoftwareWallet(privateKeyWallet)); case 21: _context.prev = 21; _context.t0 = _context["catch"](4); throw new Error("".concat(messages.open, " ").concat(objectToErrorString({ password: password, privateKey: privateKey, mnemonic: mnemonic, keystore: keystore }), " Error: ").concat(_context.t0.message)); case 24: case "end": return _context.stop(); } } }, _callee, this, [[4, 21]]); })); return function open() { return _ref.apply(this, arguments); }; }(); /** * Create a new wallet. * * This will use EtherWallet's `createRandom()` (with defaults and entropy) * and use the resulting private key to instantiate a new SoftwareWallet. * * @method create * * @param {Uint8Array} entropy An unsigned 8bit integer Array to provide extra randomness * @param {string} password Optional password used to generate an encrypted keystore * @param {number} chainId The id of the network to use, defaults to mainnet (1) * * All the above params are sent in as props of an {WalletArgumentsType} object. * * @return {WalletType} A new wallet object */ export var create = /*#__PURE__*/ function () { var _ref2 = _asyncToGenerator( /*#__PURE__*/ _regeneratorRuntime.mark(function _callee2() { var argumentObject, password, _argumentObject$entro, entropy, _argumentObject$chain2, chainId, basicWallet, _args2 = arguments; return _regeneratorRuntime.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: argumentObject = _args2.length > 0 && _args2[0] !== undefined ? _args2[0] : {}; /* * Validate the trasaction's object input */ userInputValidator({ firstArgument: argumentObject }); password = argumentObject.password, _argumentObject$entro = argumentObject.entropy, entropy = _argumentObject$entro === void 0 ? getRandomValues(new Uint8Array(65536)) : _argumentObject$entro, _argumentObject$chain2 = argumentObject.chainId, chainId = _argumentObject$chain2 === void 0 ? CHAIN_IDS.HOMESTEAD : _argumentObject$chain2; _context2.prev = 3; if (!entropy || entropy && !(entropy instanceof Uint8Array)) { warning(messages.noEntrophy); basicWallet = EthersWallet.createRandom(); } else { basicWallet = EthersWallet.createRandom({ extraEntropy: entropy }); } /* * Set the password prop on the instance object. * * So that we can make use of them inside the SoftwareWallet * constructor, as the Ethers Wallet instance object will * be passed down. * * @TODO Better passing of values * * This needs to be refactored to pass values to the SoftwareWallet * class in a less repetitious way */ basicWallet.password = password; basicWallet.chainId = chainId; /* * @NOTE mnemonic prop was renamed due to naming conflict with getter-only * ethers prop */ basicWallet.originalMnemonic = basicWallet.mnemonic; return _context2.abrupt("return", new SoftwareWallet(basicWallet)); case 11: _context2.prev = 11; _context2.t0 = _context2["catch"](3); throw new Error("".concat(messages.create, " Error: ").concat(_context2.t0.message)); case 14: case "end": return _context2.stop(); } } }, _callee2, this, [[3, 11]]); })); return function create() { return _ref2.apply(this, arguments); }; }(); var softwareWallet = { open: open, create: create }; export default softwareWallet;