UNPKG

@logosnetwork/logos-webwallet-sdk

Version:

Create Logos wallets with or without a full Logos node

1,324 lines 59.8 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); var mqttPattern_1 = __importDefault(require("./Utils/mqttPattern")); var isomorphic_ws_1 = __importDefault(require("isomorphic-ws")); var reconnecting_websocket_1 = __importDefault(require("reconnecting-websocket")); var logos_rpc_client_1 = __importDefault(require("@logosnetwork/logos-rpc-client")); var Utils_1 = require("./Utils/Utils"); var pbkdf2_1 = require("pbkdf2"); var nacl_1 = __importDefault(require("tweetnacl/nacl")); var blake2b_1 = __importDefault(require("./Utils/blake2b")); var big_integer_1 = __importDefault(require("big-integer")); var mqtt_1 = require("mqtt"); var TokenAccount_1 = __importDefault(require("./TokenAccount")); var LogosAccount_1 = __importDefault(require("./LogosAccount")); /** * ## Wallet * The wallet is the primary way you will interact with the SDK. */ var Wallet = /** @class */ (function () { /** * ### Instantiating * ```typescript * import Wallet from '@logosnetwork/logos-webwallet-sdk' * const wallet = new Wallet({ * password: null, * seed: null, * deterministicKeyIndex: 0, * currentAccountAddress: null, * accounts: {}, * tokenAccounts: {}, * walletID: null, * batchSends: true, * fullSync: true, * lazyErrors: false, * tokenSync: false, * validateSync: true, * ws: false, * mqtt: defaultMQTT, * rpc: defaultRPC * }) * ``` * * All wallet options are optional defaults are shown in the example above * * |Wallet Option| Description | * |--|--| * | [[password]] | Password is used to encrypt and decrypt the wallet data | * | [[seed]] | Seed is the deterministic entropy that we will use to generate key pairs from | * | [[deterministicKeyIndex]] | index of where you wish to start generating key paris from | * | [[currentAccountAddress]] | the current selected account address | * | [[accounts]] | [[AccountMap]] of all the [[LogosAccount|logos accounts]] in the Wallet | * | [[tokenAccounts]] | [[TokenAccountMap]] of all the [[TokenAccount|token accounts]] in the Wallet | * | [[walletID]] | identifier of this wallet instance | * | [[batchSends]] | when batchsends is true the SDK automatically combines send transactions to reduce overall amount of transactions | * | [[fullSync]] | when fullSync is true the SDK will load the full history of the TokenAccounts and Accounts in the system. This is recommend to be true when working with tokens. | * | [[lazyErrors]] | when lazyErrors is true the SDK will not throw errors for transactions that have insufficient funds and will queue the transactions until the account has the funds to complete the action. | * | [[tokenSync]] | when tokenSync is true the SDK will load and sync the TokenAccounts that have interacted with the LogosAccounts. | * | [[validateSync]] | when validateSync is true the SDK will check all signatures of all the requests in the account chains. This is recommended to be true but when syncing an account with a long history this can be computationally heavy. | * | [[ws]] | when ws is true connect to local logos node websocket. You should only use mqtt or ws not both! mqtt will take priority over WS the MQTT setup uses less resources at the current time. | * | [[mqtt]] | address of your mqtt server `'wss://pla.bs:8443'` is the default server. Check out the logos backend repo to run your own backend mqtt. | * | [[rpc]] | Node information where you are sending requests to. See [[RPCOptions]]. Do not use the ip address of a delegate node, it won't work and also delegates shouldn't have RPC enabled... | */ function Wallet(options) { if (options === void 0) { options = { password: null, seed: null, deterministicKeyIndex: 0, currentAccountAddress: null, accounts: {}, tokenAccounts: {}, walletID: null, batchSends: true, fullSync: true, lazyErrors: false, tokenSync: false, validateSync: true, ws: false, p2pPropagation: false, mqtt: Utils_1.defaultMQTT, rpc: Utils_1.defaultRPC, version: 1 }; } this.loadOptions(options); } Wallet.prototype.loadOptions = function (options) { /** * Password used to encrypt and decrypt the wallet data * @type {string} * @private */ if (options.password !== undefined) { this._password = options.password; } else { this._password = null; } /** * Deterministic Key Index is used to generate accounts * @type {number} * @private */ if (options.deterministicKeyIndex !== undefined) { this._deterministicKeyIndex = options.deterministicKeyIndex; } else { this._deterministicKeyIndex = 0; } /** * Current Account address is the public key of the current account * @type {string} * @private */ if (options.currentAccountAddress !== undefined) { this._currentAccountAddress = options.currentAccountAddress; } else { this._currentAccountAddress = null; } /** * Wallet Identifer * @type {string} * @private */ if (options.walletID !== undefined) { this._walletID = options.walletID; } else { this._walletID = Utils_1.uint8ToHex(nacl_1.default.randomBytes(32)); } /** * Batch Sends - When lots of requests are pending auto batch them togeather for speed * @type {boolean} * @private */ if (options.batchSends !== undefined) { this._batchSends = options.batchSends; } else { this._batchSends = true; } /** * Full Sync - Should we fully sync and validate the full request chain or just sync the request * @type {boolean} * @private */ if (options.fullSync !== undefined) { this._fullSync = options.fullSync; } else { this._fullSync = true; } /** * Sync Tokens - Syncs all associated token's of the accounts on the account sync instead of on use * @type {boolean} * @private */ if (options.tokenSync !== undefined) { this._tokenSync = options.tokenSync; } else { this._tokenSync = false; } /** * Validate Sync * if this option is true the SDK will generate hashes of each requests based on the content data and verify signatures * This should always be true when using a untrusted RPC node * @type {boolean} * @private */ if (options.validateSync !== undefined) { this._validateSync = options.validateSync; } else { this._validateSync = true; } /** * Lazy Errors - Do not reject invalid requests when adding to pending chain * * Lazy errors will not prevent you from creating blocks but only from broadcasting them * * @type {boolean} * @private */ if (options.lazyErrors !== undefined) { this._lazyErrors = options.lazyErrors; } else { this._lazyErrors = false; } /** * RPC enabled * @type {RPCOptions | false} * @private */ if (options.rpc !== undefined) { this._rpc = options.rpc; } else { this._rpc = Utils_1.defaultRPC; } /** * PBKDF2 Iterations * I don't think people need to edit this * NIST guidelines recommend 10,000 so lets do that * @type {number} * @private */ this._iterations = 10000; /** * Seed used to generate accounts * @type {string} The 32 byte seed hex encoded * @private */ if (options.seed !== undefined) { this._seed = options.seed; } else { this._seed = Utils_1.uint8ToHex(nacl_1.default.randomBytes(32)); } /** * Array of accounts in this wallet * @type {Map<string, Account>} * @private */ if (options.accounts !== undefined) { this._accounts = {}; for (var account in options.accounts) { if (this._currentAccountAddress === null) { this._currentAccountAddress = account; } var accountOptions = options.accounts[account]; accountOptions.wallet = this; this._accounts[account] = new LogosAccount_1.default(accountOptions); } } else { this._accounts = {}; } /** * Array of accounts in this wallet * @type {Map<string, TokenAccount>} * @private */ if (options.tokenAccounts !== undefined) { this._tokenAccounts = {}; for (var account in options.tokenAccounts) { var accountOptions = options.tokenAccounts[account]; accountOptions.wallet = this; this._tokenAccounts[account] = new TokenAccount_1.default(accountOptions); } } else { this._tokenAccounts = {}; } /** * MQTT host to listen for data * @type {string | boolean} The mqtt websocket address (false if you don't want this) * @private */ if (options.mqtt !== undefined) { this._mqtt = options.mqtt; } else { this._mqtt = Utils_1.defaultMQTT; } /** * Use local websockets * @type {boolean} true if you want to use WS * @private */ if (options.ws !== undefined) { this._ws = options.ws; } else { this._ws = false; } /** * Use local websockets * @type {boolean} true if you want to use WS * @private */ if (options.p2pPropagation !== undefined) { this._p2pPropagation = options.p2pPropagation; } else { this._p2pPropagation = false; } this._delegates = []; this._wsConnected = false; this.wsConnect(); }; Object.defineProperty(Wallet.prototype, "walletID", { /** * The id of the wallet * #### Example * ```typescript * const walletID = wallet.walletID * ``` */ get: function () { return this._walletID; }, /** * The id of the wallet * #### Example * ```typescript * wallet.walletID = '2c0a4be6b9ccda9158ed96f0dd596f72dad66015e8444c64e2ea0b9c7e553ec6' * ``` */ set: function (id) { this._walletID = id; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "batchSends", { /** * Is the wallet batching requests * #### Example * ```typescript * const isBatchingSends = wallet.batchSends * ``` */ get: function () { return this._batchSends; }, /** * Is the wallet batching requests * #### Example * ```typescript * wallet.batchSends = true * ``` */ set: function (val) { this._batchSends = val; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "fullSync", { /** * Full Sync - syncs the entire send and recieve chains * This is recommend to be true when using an untrusted RPC node * In the future this will be safe when we have BLS sig validation of Request Blocks * #### Example * ```typescript * const isFullSyncing = wallet.fullSync * ``` */ get: function () { return this._fullSync; }, /** * Full Sync - syncs the entire send and recieve chains * This is recommend to be true when using an untrusted RPC node * In the future this will be safe when we have BLS sig validation of Request Blocks * #### Example * ```typescript * wallet.fullSync = true * ``` */ set: function (val) { this._fullSync = val; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "tokenSync", { /** * Sync Tokens - Syncs all associated token's of the accounts on the account sync instead of on use * #### Example * ```typescript * const areTokensSyncing = wallet.tokenSync * ``` */ get: function () { return this._tokenSync; }, /** * Sync Tokens - Syncs all associated token's of the accounts on the account sync instead of on use * #### Example * ```typescript * wallet.tokenSync = false * ``` */ set: function (val) { this._tokenSync = val; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "validateSync", { /** * Validate Sync * if this option is true the SDK will generate hashes of each requests based on the content data and verify signatures * This should always be true when using a untrusted RPC node * #### Example * ```typescript * const isValidatingSignatures = wallet.validateSync * ``` */ get: function () { return this._validateSync; }, /** * Validate Sync * if this option is true the SDK will generate hashes of each requests based on the content data and verify signatures * This should always be true when using a untrusted RPC node * #### Example * ```typescript * wallet.validateSync = false * ``` */ set: function (val) { this._validateSync = val; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "lazyErrors", { /** * Lazy Errors allows you to add request that are not valid for the current pending balances to the pending chain * #### Example * ```typescript * const delayingErros = wallet.lazyErrors * ``` */ get: function () { return this._lazyErrors; }, /** * Lazy Errors allows you to add request that are not valid for the current pending balances to the pending chain * #### Example * ```typescript * wallet.lazyErrors = false * ``` */ set: function (val) { this._lazyErrors = val; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "ws", { /** * When ws is true connect to local logos node websocket * If mqtt is set then logos node websocket will not be used * #### Example * ```typescript * const usingLogosWebsocket = wallet.ws * ``` */ get: function () { return this._ws; }, /** * When ws is true connect to local logos node websocket * If mqtt is set then logos node websocket will not be used * #### Example * ```typescript * wallet.ws = true * ``` */ set: function (val) { this.wsDisconnect(); this._ws = val; this.wsConnect(); }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "p2pPropagation", { /** * When p2pPropagation is true public to local logos node instead of delegates * #### Example * ```typescript * const usingP2pPropagation = wallet.p2pPropagation * ``` */ get: function () { return this._p2pPropagation; }, /** * When p2pPropagation is true public to local logos node instead of delegates * #### Example * ```typescript * wallet.p2pPropagation = true * ``` */ set: function (val) { this._p2pPropagation = val; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "accounts", { /** * [[AccountMap]] of all the [[LogosAccount|LogosAccounts]] in the wallet * #### Example * ```typescript * const accounts = wallet.accounts * ``` */ get: function () { return this._accounts; }, /** * [[AccountMap]] of all the [[LogosAccount|LogosAccounts]] in the wallet * #### Example * ```typescript * wallet.accounts = { * 'lgs_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo': new LogosAccount({ * privateKey: 34F0A37AAD20F4A260F0A5B3CB3D7FB50673212263E58A380BC10474BB039CE4 * }) * } * ``` */ set: function (accounts) { this._accounts = accounts; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "tokenAccounts", { /** * [[TokenAccountMap]] of all the [[TokenAccount|TokenAccounts]] in the wallet * #### Example * ```typescript * const tokenAccounts = wallet.tokenAccounts * ``` */ get: function () { return this._tokenAccounts; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "account", { /** * Returns the current [[LogosAccount]] of the wallet * #### Example * ```typescript * const account = wallet.account * ``` */ get: function () { return this.accounts[this.currentAccountAddress]; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "currentAccountAddress", { /** * The current account address * #### Example * ```typescript * const currentAccountAddress = wallet.currentAccountAddress * ``` */ get: function () { return this._currentAccountAddress; }, /** * The current account address * #### Example * ```typescript * wallet.currentAccountAddress = 'lgs_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo' * ``` */ set: function (address) { if (!Object.prototype.hasOwnProperty.call(this.accounts, address)) throw new Error("Account " + address + " does not exist in this wallet."); this._currentAccountAddress = address; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "balance", { /** * The current balance of all the [[LogosAccount|LogosAccounts]] in reason * #### Example * ```typescript * const walletBalanceInReason = wallet.balance * ``` */ get: function () { var totalBalance = big_integer_1.default(0); for (var account in this.accounts) { totalBalance.add(big_integer_1.default(this.accounts[account].balance)); } return totalBalance.toString(); }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "mqtt", { /** * The mqtt host for listening to confirmations from Logos consensus * #### Example * ```typescript * const mqttWsAddress = wallet.mqtt * ``` */ get: function () { return this._mqtt; }, /** * The mqtt host for listening to confirmations from Logos consensus * #### Example * ```typescript * wallet.mqtt = 'wss://pla.bs:8443' * ``` */ set: function (val) { this.wsDisconnect(); this._mqtt = val; this.wsConnect(); }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "rpc", { /** * The [[RPCOptions]] for connecting to the RPC or set this to false to disable communication * #### Example * ```typescript * const rpcInfo = wallet.rpc * ``` */ get: function () { return this._rpc; }, /** * The [[RPCOptions]] for connecting to the RPC or set this to false to disable communication * #### Example * ```typescript * wallet.rpc = { * proxy: 'https://pla.bs', * nodeURL: '3.215.28.211', * nodePort: '55000', * wsPort: '18000' * } * ``` */ set: function (val) { // Reset MQTT or WS this.wsDisconnect(); var nodeChanged = val && this._rpc && this._rpc.nodeURL !== val.nodeURL; // Update wallet info this._rpc = val; // Fetch delegates && reset wallets in case of new network if (nodeChanged) { this.reset(); if (!this.p2pPropagation) this.fetchDelegates(); } // Reconnect to MQTT or WS this.wsConnect(); }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "rpcClient", { /** * Returns a Logos RPC Client Instance * * @returns {Logos} * #### Example * ```typescript * const rpcClient = wallet.rpcClient * ``` */ get: function () { if (this.rpc) { var rpcInfo = { url: "http://" + this.rpc.nodeURL + ":" + this.rpc.nodePort }; if (this.rpc.proxy) rpcInfo.proxyURL = this.rpc.proxy; return new logos_rpc_client_1.default(rpcInfo); } else { return null; } }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "password", { /** * The password of the wallet * in the future we will remove the ability to store the password and request it in realtime so it is in memory for less time * #### Example * ```typescript * const password = wallet.password * ``` */ get: function () { return this._password; }, /** * The password of the wallet * in the future we will remove the ability to store the password and request it in realtime so it is in memory for less time * #### Example * ```typescript * wallet.password = 'password' * ``` */ set: function (password) { this._password = password; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "delegates", { /** * The current delegates of the network * #### Example * ```typescript * const delegates = wallet.delegates * ``` */ get: function () { return this._delegates; }, /** * The current delegates of the network * #### Example * ```typescript * wallet.delegates = ['3.215.28.211'] // Should be 32 length but I cba * ``` */ set: function (delegates) { this._delegates = delegates; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "seed", { /** * Return the seed of the wallet * in the future we will remove the ability to access the seed unless you pass a password * #### Example * ```typescript * const seed = wallet.seed * ``` */ get: function () { return this._seed; }, /** * Return the seed of the wallet * in the future we will remove the ability to access the seed unless you pass a password * #### Example * ```typescript * wallet.seed = '6A4C54C619A784891D5DBCA1FCC5FA08D6B910B49A51BEA13C3DC913BB45AF13' * ``` */ set: function (hexSeed) { if (!/[0-9A-F]{64}/i.test(hexSeed)) throw new Error('Invalid Hex Seed.'); this._seed = hexSeed; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "synced", { /** * Return boolean if all the accounts in the wallet are synced * #### Example * ```typescript * const isWalletSynced = wallet.synced * ``` */ get: function () { for (var address in this.tokenAccounts) { if (!this.tokenAccounts[address].synced) { return false; } } for (var address in this.accounts) { if (!this.accounts[address].synced) { return false; } } return true; }, enumerable: true, configurable: true }); Object.defineProperty(Wallet.prototype, "pendingRequests", { /** * Return all the requests that are pending in every [[LogosAccount]] in this wallet * #### Example * ```typescript * const pendingRequests = wallet.pendingRequests * ``` */ get: function () { var pendingRequests = []; for (var _i = 0, _a = Object.values(this.accounts); _i < _a.length; _i++) { var account = _a[_i]; pendingRequests.concat(account.pendingChain); } return pendingRequests; }, enumerable: true, configurable: true }); /** * Generates and sets a random seed for the wallet * * #### Example * ```typescript * wallet.createSeed() * ``` */ Wallet.prototype.createSeed = function (overwrite) { if (overwrite === void 0) { overwrite = false; } if (this.seed && !overwrite) throw new Error('Seed already exists. To overwrite set the seed or set overwrite to true'); this.seed = Utils_1.uint8ToHex(nacl_1.default.randomBytes(32)); return this.seed; }; /** * Adds a account to the wallet * * #### Example * ```typescript * wallet.addAccount(new LogosAccount( * { * privateKey: 34F0A37AAD20F4A260F0A5B3CB3D7FB50673212263E58A380BC10474BB039CE4 * } * )) * ``` */ Wallet.prototype.addAccount = function (account) { this.accounts[account.address] = account; if (this.mqtt && this._wsConnected) this.subscribe("account/" + account.address); return this.accounts[account.address]; }; /** * Removes an account to the wallet * * #### Example * ```typescript * wallet.removeAccount('lgs_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpiij4txtdo') * ``` */ Wallet.prototype.removeAccount = function (address) { if (this.accounts[address]) { delete this.accounts[address]; if (this.mqtt && this._wsConnected) this.unsubscribe("account/" + address); if (address === this.currentAccountAddress) { this.currentAccountAddress = Object.keys(this.accounts)[0]; } return true; } return false; }; /** * Adds a tokenAccount to the wallet * * #### Example * ```typescript * const tokenAccount = await wallet.createTokenAccount('lgs_3q69z3kf6cq9n9smago3p1ptuyqy9pa3mdykyi9o8f7gnof47qdyxj9gejxd') * wallet.addTokenAccount(tokenAccount) * ``` */ Wallet.prototype.addTokenAccount = function (tokenAccount) { this.tokenAccounts[tokenAccount.address] = tokenAccount; if (this.mqtt && this._wsConnected) this.subscribe("account/" + tokenAccount.address); return this.tokenAccounts[tokenAccount.address]; }; /** * Create a TokenAccount * * You are allowed to add a tokenAccount using the address * * #### Example * ```typescript * const tokenAccount = await wallet.createTokenAccount('lgs_3q69z3kf6cq9n9smago3p1ptuyqy9pa3mdykyi9o8f7gnof47qdyxj9gejxd') * ``` */ Wallet.prototype.createTokenAccount = function (address, issuance) { if (issuance === void 0) { issuance = null; } return __awaiter(this, void 0, void 0, function () { var tokenAccount; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.tokenAccounts[address]) return [3 /*break*/, 1]; return [2 /*return*/, this.tokenAccounts[address]]; case 1: tokenAccount = new TokenAccount_1.default({ address: address, wallet: this, issuance: issuance }); if (this.mqtt && this._wsConnected) this.subscribe("account/" + tokenAccount.address); this.tokenAccounts[tokenAccount.address] = tokenAccount; if (!(this.rpc && !issuance)) return [3 /*break*/, 3]; return [4 /*yield*/, this.tokenAccounts[tokenAccount.address].sync()]; case 2: _a.sent(); return [3 /*break*/, 4]; case 3: if (!this.rpc) console.warn('RPC not ENABLED TOKEN ACTIONS - TokenAccount cannot sync'); this.tokenAccounts[tokenAccount.address].synced = true; _a.label = 4; case 4: return [2 /*return*/, this.tokenAccounts[tokenAccount.address]]; } }); }); }; /** * Create an account * * You are allowed to create an account using your seed, precalculated account options, or a privateKey * * @param {LogosAccountOptions} options - the options to populate the account. If you send just private key it will generate the account from that privateKey. If you just send index it will genereate the account from that determinstic seed index. * @param {boolean} setCurrent - sets the current account to newly created accounts this is default true * @returns {Promise<LogosAccount>} * * #### Example * ```typescript * const account = await wallet.createAccount() * ``` */ Wallet.prototype.createAccount = function (options, setCurrent) { if (options === void 0) { options = null; } if (setCurrent === void 0) { setCurrent = true; } return __awaiter(this, void 0, void 0, function () { var accountInfo, accountOptions, account; return __generator(this, function (_a) { switch (_a.label) { case 0: accountInfo = null; if (options === null) { // No options generate from seed if (!this.seed) throw new Error('Cannot generate an account without a seed! Make sure to first set your seed or pass a private key or explicitly pass the options for the account.'); accountInfo = this.generateAccountOptionsFromSeed(this._deterministicKeyIndex); this._deterministicKeyIndex++; } else { if (options.privateKey !== undefined) { accountInfo = this.generateAccountOptionsFromPrivateKey(options.privateKey); } else if (options.index !== undefined) { if (!this.seed) throw new Error('Cannot generate an account without a seed! Make sure to first set your seed or pass a private key or explicitly pass the options for the account.'); accountInfo = this.generateAccountOptionsFromSeed(options.index); } else { if (!this.seed) throw new Error('Cannot generate an account without a seed! Make sure to first set your seed or pass a private key or explicitly pass the options for the account.'); accountInfo = this.generateAccountOptionsFromSeed(this._deterministicKeyIndex); this._deterministicKeyIndex++; } } accountOptions = __assign(__assign({}, accountInfo), { wallet: this, label: "Account " + Object.values(this.accounts).length }); account = new LogosAccount_1.default(accountOptions); this.addAccount(account); if (!this.rpc) return [3 /*break*/, 2]; return [4 /*yield*/, this.accounts[account.address].sync()]; case 1: _a.sent(); return [3 /*break*/, 3]; case 2: this.accounts[account.address].synced = true; _a.label = 3; case 3: if (setCurrent || this.currentAccountAddress === null) this.currentAccountAddress = account.address; return [2 /*return*/, this.accounts[account.address]]; } }); }); }; /** * Updates the balance of all the accounts * @returns {void} * #### Example * ```typescript * wallet.recalculateWalletBalancesFromChain() * ``` */ Wallet.prototype.recalculateWalletBalancesFromChain = function () { for (var _i = 0, _a = Object.values(this.accounts); _i < _a.length; _i++) { var account = _a[_i]; account.updateBalancesFromChain(); } }; /** * Finds the request object of the specified hash of one of our accounts * * @param {string} hash - The hash of the request we are looking for the object of * @returns {Request | false } false if no request object of the specified hash was found * #### Example * ```typescript * wallet.getRequest('E8CA715349FFD12DE7CB76045CAAA52448655F3B34624A1E31514763C81C4795') * ``` */ Wallet.prototype.getRequest = function (hash) { for (var _i = 0, _a = Object.values(this.accounts); _i < _a.length; _i++) { var account = _a[_i]; var request = account.getRequest(hash); if (request) { return request; } return false; } return false; }; /** * Encrypts and packs the wallet data in a hex string * * @returns {string} * #### Example * ```typescript * wallet.encrypt() * ``` */ Wallet.prototype.encrypt = function () { var encryptedWallet = JSON.stringify(this.toJSON()); encryptedWallet = Utils_1.stringToHex(encryptedWallet); var WalletBuffer = Buffer.from(encryptedWallet, 'hex'); var checksum = new blake2b_1.default().update(WalletBuffer).digest(); var salt = Buffer.from(nacl_1.default.randomBytes(16)); var localPassword = ''; if (!this._password) { localPassword = 'password'; } else { localPassword = this._password; } var key = pbkdf2_1.pbkdf2Sync(localPassword, salt, this._iterations, 32, 'sha512'); var options = { mode: Utils_1.AES.CBC, padding: Utils_1.Iso10126 }; var encryptedBytes = Utils_1.AES.encrypt(WalletBuffer, key, salt, options); var payload = Buffer.concat([Buffer.from(checksum), salt, encryptedBytes]); // decrypt to check if wallet was corrupted during ecryption somehow if (this.decrypt(payload) === false) { return this.encrypt(); // try again, shouldnt happen often } return payload.toString('hex'); }; /** * Scans the accounts to make sure they are synced and if they are not synced it syncs them * * @param {boolean} - encrypted wallet * @returns {Promise<boolean>} * #### Example * ```typescript * const isWalletSynced = await wallet.sync() * ``` */ Wallet.prototype.sync = function (force) { if (force === void 0) { force = false; } return __awaiter(this, void 0, void 0, function () { var _this = this; return __generator(this, function (_a) { return [2 /*return*/, new Promise(function (resolve) { var isSyncedPromises = []; for (var account in _this.accounts) { if (!_this.accounts[account].synced || force) { isSyncedPromises.push(_this.accounts[account].isSynced()); } } for (var tokenAccount in _this.tokenAccounts) { if (!_this.tokenAccounts[tokenAccount].synced || force) { isSyncedPromises.push(_this.tokenAccounts[tokenAccount].isSynced()); } } if (isSyncedPromises.length > 0) { Promise.all(isSyncedPromises).then(function (values) { var syncPromises = []; for (var _i = 0, values_1 = values; _i < values_1.length; _i++) { var isSynced = values_1[_i]; if (!isSynced.synced) { if (isSynced.type === 'LogosAccount') { syncPromises.push(_this.accounts[isSynced.account].sync()); } else if (isSynced.type === 'TokenAccount') { if (isSynced.remove) { delete _this.tokenAccounts[isSynced.account]; } else { syncPromises.push(_this.tokenAccounts[isSynced.account].sync()); } } } } if (syncPromises.length > 0) { Promise.all(syncPromises).then(function () { resolve(true); }); } else { resolve(true); } }); } else { resolve(true); } })]; }); }); }; /** * Constructs the wallet from an encrypted base64 encoded wallet and the password * * @param {string} - encrypted wallet * @returns {Wallet} wallet data * #### Example * ```typescript * const wallet = wallet.load(encryptedWalletData) * ``` */ Wallet.prototype.load = function (encryptedWallet) { this.accounts = {}; var decryptedBytes = this.decrypt(encryptedWallet); if (decryptedBytes === false) throw new Error('Wallet is corrupted or has been tampered.'); var walletData = JSON.parse(decryptedBytes.toString('utf8')); this.loadOptions(walletData); return this; }; /** * Fetches the delegates from the server and sets our delegate list * * @returns {Promise<string[]>} returns the list of active delegates ips * #### Example * ```typescript * const delegates = await wallet.fetchDelegates() * ``` */ Wallet.prototype.fetchDelegates = function () { return __awaiter(this, void 0, void 0, function () { var delegates, index; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.rpcClient) return [3 /*break*/, 2]; return [4 /*yield*/, this.rpcClient.epochs.delegateIPs()]; case 1: delegates = _a.sent(); this.delegates = []; for (index in delegates) { if (Utils_1.testnetDelegates[delegates[index].ip]) { this.delegates.push(Utils_1.testnetDelegates[delegates[index].ip]); } else { this.delegates.push(delegates[index].ip); } } return [2 /*return*/, this.delegates]; case 2: return [2 /*return*/, null]; } }); }); }; /** * Decrypts the wallet data * * @param {Buffer | string} - encrypted wallet * @returns {Buffer | false} The request data or returns false if it is unable to decrypt the data * @private */ Wallet.prototype.decrypt = function (encryptedWallet) { var bytes = null; if (encryptedWallet instanceof Buffer) { bytes = encryptedWallet; } else { bytes = Buffer.from(encryptedWallet, 'hex'); } var checksum = bytes.slice(0, 32); var salt = bytes.slice(32, 48); var payload = bytes.slice(48); var localPassword = ''; if (!this._password) { localPassword = 'password'; } else { localPassword = this._password; } var key = pbkdf2_1.pbkdf2Sync(localPassword, salt, this._iterations, 32, 'sha512'); var options = { padding: Utils_1.Iso10126 }; var decryptedBytes = Utils_1.AES.decrypt(payload, key, salt, options); var hash = new blake2b_1.default().update(decryptedBytes).digest('hex'); if (hash !== checksum.toString('hex').toUpperCase()) return false; return decryptedBytes; }; /** * Generates an account based on the determinstic index of the key * * @param {number} - The determinstic seed index * @returns {AccountOptions} The minimal account options to create the account * @private */ Wallet.prototype.generateAccountOptionsFromSeed = function (index) { if (this.seed.length !== 64) throw new Error('Invalid Seed.'); var indexBytes = Utils_1.hexToUint8(Utils_1.decToHex(index, 4)); var privateKey = new blake2b_1.default() .update(Utils_1.hexToUint8(this.seed)) .update(indexBytes) .digest(); var publicKey = nacl_1.default.sign.keyPair.fromSecretKey(privateKey).publicKey; var address = Utils_1.accountFromHexKey(Utils_1.uint8ToHex(publicKey)); return { privateKey: Utils_1.uint8ToHex(privateKey), publicKey: Utils_1.uint8ToHex(publicKey), address: address, index: index }; }; /** * Generates an account based on the given private key * * @param {string} - The private key * @returns {AccountOptions} The minimal account options to create the account * @private */ Wallet.prototype.generateAccountOptionsFromPrivateKey = function (privateKey) { if (privateKey.length !== 64) throw new Error('Invalid Private Key length. Should be 32 bytes.'); if (!/[0-9A-F]{64}/i.test(privateKey)) throw new Error('Invalid Hex Private Key.'); var publicKey = nacl_1.default.sign.keyPair.fromSecretKey(Utils_1.hexToUint8(privateKey)).publicKey; var address = Utils_1.accountFromHexKey(Utils_1.uint8ToHex(publicKey)); return { privateKey: privateKey, publicKey: Utils_1.uint8ToHex(publicKey), address: address }; }; /** * Subscribe to the mqtt topic * * @param {string} topic - topic to subscribe to * @returns {void} * @private */ Wallet.prototype.subscribe = function (topic) { if (this._wsConnected && this._mqttClient) { this._mqttClient.subscribe(topic, function (err) { if (!err) { console.info("subscribed to " + topic); } else { console.error(err); } }); } }; /** * Unsubscribe to the mqtt topic * * @param {string} topic - topic to unsubscribe to * @returns {void} *