@colony/purser-core
Version:
A collection of helpers, utils, validators and normalizers to assist the individual purser modules
351 lines (314 loc) • 11.8 kB
JavaScript
import _regeneratorRuntime from "@babel/runtime/regenerator";
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
import _classCallCheck from "@babel/runtime/helpers/esm/classCallCheck";
import _createClass from "@babel/runtime/helpers/esm/createClass";
import { computeAddress } from 'ethers/utils';
import HDKey from 'hdkey';
import { safeIntegerValidator, hexSequenceValidator, addressValidator } from './validators';
import { addressNormalizer, hexSequenceNormalizer } from './normalizers';
import { genericClass as messages } from './messages';
import { HEX_HASH_TYPE, DESCRIPTORS, SPLITTER, CHAIN_IDS } from './defaults';
import { TYPE_GENERIC, SUBTYPE_GENERIC } from './types';
var GETTERS = DESCRIPTORS.GETTERS,
SETTERS = DESCRIPTORS.SETTERS,
WALLET_PROPS = DESCRIPTORS.WALLET_PROPS,
GENERIC_PROPS = DESCRIPTORS.GENERIC_PROPS;
/*
* "Private" (internal) variable(s).
*
* These are used as return values from getters which don't have an accompanying setter,
* but we still want to set them internally.
*/
var internalPublicKey;
var internalDerivationPath;
/*
* @TODO Support extra props
*
* Support the extra props required for the software wallet (privateKey, mnemonic, etc...)
* Also, we need to find a way to extend both this and the `ethers` wallet class
*/
var GenericWallet =
/*#__PURE__*/
function () {
/*
* Both `publicKey` and `derivationPath` are getters.
*/
/*
* @TODO Add specific Flow types
*
* For the three main wallet methods
*/
function GenericWallet(_ref) {
var _this = this;
var publicKey = _ref.publicKey,
chainCode = _ref.chainCode,
rootDerivationPath = _ref.rootDerivationPath,
_ref$addressCount = _ref.addressCount,
addressCount = _ref$addressCount === void 0 ? 10 : _ref$addressCount,
_ref$chainId = _ref.chainId,
chainId = _ref$chainId === void 0 ? CHAIN_IDS.HOMESTEAD : _ref$chainId;
_classCallCheck(this, GenericWallet);
/*
* Validate address count (this comes from the end user)
*/
safeIntegerValidator(addressCount);
/*
* Validate the `publicKey` and `chainCode` hex sequences. These come from
* various external services, and we shouldn't trust them.
*/
hexSequenceValidator(publicKey);
hexSequenceValidator(chainCode);
/*
* Derive the public key with the address index, so we can get the address
*/
var hdKey = new HDKey();
/*
* Sadly Flow doesn't have the correct types for node's Buffer Object
*/
/* $FlowFixMe */
hdKey.publicKey = Buffer.from(publicKey, HEX_HASH_TYPE);
/*
* Sadly Flow doesn't have the correct types for node's Buffer Object
*/
/* $FlowFixMe */
hdKey.chainCode = Buffer.from(chainCode, HEX_HASH_TYPE);
var otherAddresses = Array.from(
/*
* We default to `1`, but this time, to prevent the case where the
* user passes in the value `0` manually (which will break the array map)
*/
new Array(addressCount || 1), function (value, index) {
var addressObject = {};
var derivationKey = hdKey.deriveChild(index);
/*
* Set this individual address's derivation path
*/
addressObject.derivationPath = rootDerivationPath.substr(-1) === SPLITTER ? "".concat(rootDerivationPath).concat(index) : "".concat(rootDerivationPath).concat(SPLITTER).concat(index);
/*
* This is the derrived public key, not the one originally fetched one
*/
var derivedPublicKey = derivationKey.publicKey.toString(HEX_HASH_TYPE);
addressObject.publicKey = hexSequenceNormalizer(derivedPublicKey);
/*
* Generate the address from the derived public key
*/
var addressFromPublicKey = computeAddress(
/*
* Sadly Flow doesn't have the correct types for node's Buffer Object
*/
/* $FlowFixMe */
derivationKey.publicKey);
/*
* Also validate the address that comes from the `HDKey` library.
*/
addressValidator(addressFromPublicKey);
addressObject.address = addressNormalizer(addressFromPublicKey);
return addressObject;
});
/*
* Set the "private" (internal) variables values
*/
internalPublicKey = otherAddresses[0].publicKey;
internalDerivationPath = otherAddresses[0].derivationPath;
/*
* Set the Wallet Object's values
*
* We're using `defineProperties` instead of strait up assignment, so that
* we can customize the prop's descriptors
*
* @TODO Reduce code repetition when setting Class props
*
* We do this here and in the software wallet, so it might make sense to
* write a helper method for this.
*/
Object.defineProperties(this, {
address: Object.assign({}, {
value: otherAddresses[0].address
}, SETTERS),
chainId: Object.assign({}, {
value: chainId
}, WALLET_PROPS),
type: Object.assign({}, {
value: TYPE_GENERIC
}, GENERIC_PROPS),
subtype: Object.assign({}, {
value: SUBTYPE_GENERIC
}, GENERIC_PROPS),
/**
* Set the default address/public key/path one of the (other) addresses from the array.
* This is usefull since most methods (sign, signMessage) use this props as defaults.
*
* There's an argument to be made here that we can derive new addresses only when this
* method gets called.
*
* This would be helpful to offload the initial cost of deriving a number
* of `addressCount` addresses.
*
* On the other hand, if we do this, we won't be able to show the user what
* addresses are available up front.
*
* @method setDefaultAddress
*
* @param {number} addressIndex The address index from the array
*
* @return {Promise<boolean>} True if it was able to set it, false otherwise
*/
setDefaultAddress: Object.assign({}, {
/*
* @TODO Accept both number and object as argument
* To make the arguments consistent across the wallet instance methods
*/
value: function () {
var _value = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee() {
var addressIndex,
_args = arguments;
return _regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
addressIndex = _args.length > 0 && _args[0] !== undefined ? _args[0] : 0;
safeIntegerValidator(addressIndex);
if (!(addressIndex >= 0 && addressIndex < otherAddresses.length)) {
_context.next = 7;
break;
}
/*
* Address count will always be at least `1` (the first derived address).
*
* This method is useful (can be used) only when the user generated more than
* one address when instantiating the Wallet.
*/
_this.address = otherAddresses[addressIndex].address;
internalPublicKey = otherAddresses[addressIndex].publicKey;
internalDerivationPath = otherAddresses[addressIndex].derivationPath;
return _context.abrupt("return", true);
case 7:
throw new Error("".concat(messages.addressIndexOutsideRange, ": index (").concat(addressIndex, ") count (").concat(addressCount, ")"));
case 8:
case "end":
return _context.stop();
}
}
}, _callee, this);
}));
return function value() {
return _value.apply(this, arguments);
};
}()
}, WALLET_PROPS),
/*
* These are just a placeholder static methods. They should be replaced (or deleted at least)
* with methods that actually has some functionality.
*/
sign: Object.assign({}, {
value: function () {
var _value2 = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee2() {
return _regeneratorRuntime.wrap(function _callee2$(_context2) {
while (1) {
switch (_context2.prev = _context2.next) {
case 0:
case "end":
return _context2.stop();
}
}
}, _callee2, this);
}));
return function value() {
return _value2.apply(this, arguments);
};
}()
}, GENERIC_PROPS),
signMessage: Object.assign({}, {
value: function () {
var _value3 = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee3() {
return _regeneratorRuntime.wrap(function _callee3$(_context3) {
while (1) {
switch (_context3.prev = _context3.next) {
case 0:
case "end":
return _context3.stop();
}
}
}, _callee3, this);
}));
return function value() {
return _value3.apply(this, arguments);
};
}()
}, GENERIC_PROPS),
verifyMessage: Object.assign({}, {
value: function () {
var _value4 = _asyncToGenerator(
/*#__PURE__*/
_regeneratorRuntime.mark(function _callee4() {
return _regeneratorRuntime.wrap(function _callee4$(_context4) {
while (1) {
switch (_context4.prev = _context4.next) {
case 0:
case "end":
return _context4.stop();
}
}
}, _callee4, this);
}));
return function value() {
return _value4.apply(this, arguments);
};
}()
}, GENERIC_PROPS)
});
/*
* The `otherAddresses` prop is only available if we have more than one.
*
* Otherwise it's pointless since it just repeats information (first index
* is also the default one).
*/
if (addressCount > 1) {
Object.defineProperty(this, 'otherAddresses', Object.assign({}, {
/*
* Map out the publicKey and derivation path from the `otherAddresses`
* array that gets assigned to the Wallet instance.
*
* The user should only have access to `the publicKey` and `derivationPath` from the
* default account (set via `setDefaultAddress()`)
*/
value: otherAddresses.map(function (_ref2) {
var address = _ref2.address;
return address;
})
}, WALLET_PROPS));
}
}
/*
* Public Key Getter
*/
/* eslint-disable-next-line class-methods-use-this */
_createClass(GenericWallet, [{
key: "publicKey",
get: function get() {
return Promise.resolve(internalPublicKey);
}
/* eslint-disable-next-line class-methods-use-this */
}, {
key: "derivationPath",
get: function get() {
return Promise.resolve(internalDerivationPath);
}
}]);
return GenericWallet;
}();
/*
* We need to use `defineProperties` to make props enumerable.
* When adding them via a `Class` getter/setter it will prevent that by default
*/
export { GenericWallet as default };
Object.defineProperties(GenericWallet.prototype, {
publicKey: GETTERS,
derivationPath: GETTERS
});