UNPKG

airgap-coin-lib

Version:

The airgap-coin-lib is a protocol agnostic library to prepare, sign and broadcast cryptocurrency transactions.

546 lines 27.8 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); 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 }; } }; Object.defineProperty(exports, "__esModule", { value: true }); var sodium = require("libsodium-wrappers"); var index_1 = require("../../dependencies/src/axios-0.19.0/index"); var bignumber_1 = require("../../dependencies/src/bignumber.js-9.0.0/bignumber"); var index_2 = require("../../dependencies/src/bip39-2.5.0/index"); var bs58check = require("../../dependencies/src/bs58check-2.1.2/index"); var index_3 = require("../../dependencies/src/hd-wallet-js-b216450e56954a6e82ace0aade9474673de5d9d5/src/index"); var rlp = require("../../dependencies/src/rlp-2.2.3/index"); var base64Check_1 = require("../../utils/base64Check"); var padStart_1 = require("../../utils/padStart"); var ProtocolSymbols_1 = require("../../utils/ProtocolSymbols"); var utils_1 = require("../ethereum/utils/utils"); var NonExtendedProtocol_1 = require("../NonExtendedProtocol"); var AeternityCryptoClient_1 = require("./AeternityCryptoClient"); var AeternityProtocolOptions_1 = require("./AeternityProtocolOptions"); var AeternityProtocol = /** @class */ (function (_super) { __extends(AeternityProtocol, _super); function AeternityProtocol(options) { if (options === void 0) { options = new AeternityProtocolOptions_1.AeternityProtocolOptions(); } var _this = _super.call(this) || this; _this.options = options; _this.symbol = 'AE'; _this.name = 'æternity'; _this.marketSymbol = 'ae'; _this.feeSymbol = 'ae'; _this.decimals = 18; _this.feeDecimals = 18; _this.identifier = ProtocolSymbols_1.MainProtocolSymbols.AE; _this.feeDefaults = { low: '0.00021', medium: '0.000315', high: '0.00084' }; _this.units = [ { unitSymbol: 'AE', factor: '1' } ]; _this.supportsHD = false; _this.standardDerivationPath = "m/44h/457h/0h/0h/0h"; _this.addressIsCaseSensitive = true; _this.addressValidationPattern = '^ak_+[1-9A-Za-z]{49,50}$'; _this.addressPlaceholder = 'ak_abc...'; // ae specifics _this.defaultNetworkId = 'ae_mainnet'; _this.feesURL = 'https://api-airgap.gke.papers.tech/fees'; return _this; } AeternityProtocol.prototype.getBlockExplorerLinkForAddress = function (address) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.options.network.blockExplorer.getAddressLink(address)]; }); }); }; AeternityProtocol.prototype.getBlockExplorerLinkForTxId = function (txId) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.options.network.blockExplorer.getTransactionLink(txId)]; }); }); }; AeternityProtocol.prototype.getPublicKeyFromMnemonic = function (mnemonic, derivationPath, password) { return __awaiter(this, void 0, void 0, function () { var secret; return __generator(this, function (_a) { secret = index_2.mnemonicToSeed(mnemonic, password); return [2 /*return*/, this.getPublicKeyFromHexSecret(secret, derivationPath)]; }); }); }; AeternityProtocol.prototype.getPrivateKeyFromMnemonic = function (mnemonic, derivationPath, password) { return __awaiter(this, void 0, void 0, function () { var secret; return __generator(this, function (_a) { secret = index_2.mnemonicToSeed(mnemonic, password); return [2 /*return*/, this.getPrivateKeyFromHexSecret(secret, derivationPath)]; }); }); }; /** * Returns the PublicKey as String, derived from a supplied hex-string * @param secret HEX-Secret from BIP39 * @param derivationPath DerivationPath for Key */ AeternityProtocol.prototype.getPublicKeyFromHexSecret = function (secret, derivationPath) { return __awaiter(this, void 0, void 0, function () { var publicKey; return __generator(this, function (_a) { publicKey = index_3.generateWalletUsingDerivationPath(Buffer.from(secret, 'hex'), derivationPath).publicKey; return [2 /*return*/, Buffer.from(publicKey).toString('hex')]; }); }); }; /** * Returns the PrivateKey as Buffer, derived from a supplied hex-string * @param secret HEX-Secret from BIP39 * @param derivationPath DerivationPath for Key */ AeternityProtocol.prototype.getPrivateKeyFromHexSecret = function (secret, derivationPath) { return __awaiter(this, void 0, void 0, function () { var secretKey; return __generator(this, function (_a) { secretKey = index_3.generateWalletUsingDerivationPath(Buffer.from(secret, 'hex'), derivationPath).secretKey; return [2 /*return*/, Buffer.from(secretKey)]; }); }); }; AeternityProtocol.prototype.getAddressFromPublicKey = function (publicKey) { return __awaiter(this, void 0, void 0, function () { var base58; return __generator(this, function (_a) { base58 = bs58check.encode(Buffer.from(publicKey, 'hex')); return [2 /*return*/, "ak_" + base58]; }); }); }; AeternityProtocol.prototype.getAddressesFromPublicKey = function (publicKey) { return __awaiter(this, void 0, void 0, function () { var address; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)]; case 1: address = _a.sent(); return [2 /*return*/, [address]]; } }); }); }; AeternityProtocol.prototype.getTransactionsFromPublicKey = function (publicKey, limit, cursor) { return __awaiter(this, void 0, void 0, function () { var _a; return __generator(this, function (_b) { switch (_b.label) { case 0: _a = this.getTransactionsFromAddresses; return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)]; case 1: return [2 /*return*/, _a.apply(this, [[_b.sent()], limit, cursor])]; } }); }); }; AeternityProtocol.prototype.getTransactionsFromAddresses = function (addresses, limit, cursor) { return __awaiter(this, void 0, void 0, function () { var allTransactions, transactions; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, Promise.all(addresses.map(function (address) { var url = cursor ? _this.options.network.rpcUrl + "/middleware/transactions/account/" + address + "?page=" + cursor.page + "&limit=" + limit : _this.options.network.rpcUrl + "/middleware/transactions/account/" + address + "?page=1&limit=" + limit; return index_1.default.get(url); }))]; case 1: allTransactions = _a.sent(); transactions = [].concat.apply([], allTransactions.map(function (axiosData) { return axiosData.data || []; })); transactions = transactions.map(function (obj) { var parsedTimestamp = parseInt(obj.time, 10); var airGapTx = { amount: new bignumber_1.default(obj.tx.amount).toString(10), fee: new bignumber_1.default(obj.tx.fee).toString(10), from: [obj.tx.sender_id], isInbound: addresses.indexOf(obj.tx.recipient_id) !== -1, protocolIdentifier: _this.identifier, network: _this.options.network, to: [obj.tx.recipient_id], hash: obj.hash, blockHeight: obj.block_height }; if (obj.tx.payload) { airGapTx.data = obj.tx.payload; } if (!isNaN(parsedTimestamp)) { airGapTx.timestamp = Math.round(parsedTimestamp / 1000); } return airGapTx; }); return [2 /*return*/, { transactions: transactions, cursor: { page: cursor ? cursor.page + 1 : 2 } }]; } }); }); }; AeternityProtocol.prototype.getPageNumber = function (limit, offset) { if (limit <= 0 || offset < 0) { return 1; } return 1 + Math.floor(offset / limit); // We need +1 here because pages start at 1 }; AeternityProtocol.prototype.signWithPrivateKey = function (privateKey, transaction) { return __awaiter(this, void 0, void 0, function () { var rawTx, signature, txObj, txArray, rlpEncodedTx, signedEncodedTx; return __generator(this, function (_a) { switch (_a.label) { case 0: rawTx = this.decodeTx(transaction.transaction); return [4 /*yield*/, sodium.ready]; case 1: _a.sent(); signature = sodium.crypto_sign_detached(Buffer.concat([Buffer.from(transaction.networkId), rawTx]), privateKey); txObj = { tag: this.toHexBuffer(11), version: this.toHexBuffer(1), signatures: [Buffer.from(signature)], transaction: rawTx }; txArray = Object.keys(txObj).map(function (a) { return txObj[a]; }); rlpEncodedTx = rlp.encode(txArray); signedEncodedTx = "tx_" + base64Check_1.default.encode(rlpEncodedTx); return [2 /*return*/, signedEncodedTx]; } }); }); }; AeternityProtocol.prototype.decodeTx = function (transaction) { var rawTx; try { rawTx = base64Check_1.default.decode(transaction.replace('tx_', '')); return rawTx; } catch (error) { // } try { rawTx = bs58check.decode(transaction.replace('tx_', '')); return rawTx; } catch (error) { // } throw new Error('invalid TX-encoding'); }; AeternityProtocol.prototype.getTransactionDetails = function (unsignedTx) { return __awaiter(this, void 0, void 0, function () { var transaction, rlpEncodedTx, rlpDecodedTx, airgapTx, _a; return __generator(this, function (_b) { switch (_b.label) { case 0: transaction = unsignedTx.transaction.transaction; rlpEncodedTx = this.decodeTx(transaction); rlpDecodedTx = rlp.decode(rlpEncodedTx, false); _a = { amount: new bignumber_1.default(parseInt(rlpDecodedTx[4].toString('hex'), 16)).toString(10), fee: new bignumber_1.default(parseInt(rlpDecodedTx[5].toString('hex'), 16)).toString(10) }; return [4 /*yield*/, this.getAddressFromPublicKey(rlpDecodedTx[2].slice(1).toString('hex'))]; case 1: _a.from = [_b.sent()], _a.isInbound = false, _a.protocolIdentifier = this.identifier, _a.network = this.options.network; return [4 /*yield*/, this.getAddressFromPublicKey(rlpDecodedTx[3].slice(1).toString('hex'))]; case 2: airgapTx = (_a.to = [_b.sent()], _a.data = (rlpDecodedTx[8] || '').toString('utf8'), _a.transactionDetails = unsignedTx.transaction, _a); return [2 /*return*/, [airgapTx]]; } }); }); }; AeternityProtocol.prototype.getTransactionDetailsFromSigned = function (signedTx) { return __awaiter(this, void 0, void 0, function () { var rlpEncodedTx, rlpDecodedTx, unsignedAeternityTransaction; return __generator(this, function (_a) { rlpEncodedTx = this.decodeTx(signedTx.transaction); rlpDecodedTx = rlp.decode(rlpEncodedTx, false); unsignedAeternityTransaction = { publicKey: '', transaction: { networkId: 'ae_mainnet', transaction: "tx_" + base64Check_1.default.encode(rlpDecodedTx[3]) } }; return [2 /*return*/, this.getTransactionDetails(unsignedAeternityTransaction)]; }); }); }; AeternityProtocol.prototype.getBalanceOfAddresses = function (addresses) { return __awaiter(this, void 0, void 0, function () { var balance, _i, addresses_1, address, data, error_1; return __generator(this, function (_a) { switch (_a.label) { case 0: balance = new bignumber_1.default(0); _i = 0, addresses_1 = addresses; _a.label = 1; case 1: if (!(_i < addresses_1.length)) return [3 /*break*/, 6]; address = addresses_1[_i]; _a.label = 2; case 2: _a.trys.push([2, 4, , 5]); return [4 /*yield*/, index_1.default.get(this.options.network.rpcUrl + "/v2/accounts/" + address)]; case 3: data = (_a.sent()).data; balance = balance.plus(new bignumber_1.default(data.balance)); return [3 /*break*/, 5]; case 4: error_1 = _a.sent(); // if node returns 404 (which means 'no account found'), go with 0 balance if (error_1.response.status !== 404) { throw error_1; } return [3 /*break*/, 5]; case 5: _i++; return [3 /*break*/, 1]; case 6: return [2 /*return*/, balance.toString(10)]; } }); }); }; AeternityProtocol.prototype.getBalanceOfPublicKey = function (publicKey) { return __awaiter(this, void 0, void 0, function () { var address; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)]; case 1: address = _a.sent(); return [2 /*return*/, this.getBalanceOfAddresses([address])]; } }); }); }; AeternityProtocol.prototype.getBalanceOfPublicKeyForSubProtocols = function (publicKey, subProtocols) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { throw Promise.reject('get balance of sub protocols not supported'); }); }); }; AeternityProtocol.prototype.getAvailableBalanceOfAddresses = function (addresses) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, this.getBalanceOfAddresses(addresses)]; }); }); }; AeternityProtocol.prototype.estimateMaxTransactionValueFromPublicKey = function (publicKey, recipients, fee) { return __awaiter(this, void 0, void 0, function () { var balance, balanceWrapper, maxFee, estimatedFeeDefaults, amountWithoutFees; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.getBalanceOfPublicKey(publicKey)]; case 1: balance = _a.sent(); balanceWrapper = new bignumber_1.default(balance); if (!(fee !== undefined)) return [3 /*break*/, 2]; maxFee = new bignumber_1.default(fee); return [3 /*break*/, 4]; case 2: return [4 /*yield*/, this.estimateFeeDefaultsFromPublicKey(publicKey, recipients, [balance])]; case 3: estimatedFeeDefaults = _a.sent(); maxFee = new bignumber_1.default(estimatedFeeDefaults.medium).shiftedBy(this.decimals); if (maxFee.gte(balanceWrapper)) { maxFee = new bignumber_1.default(0); } _a.label = 4; case 4: amountWithoutFees = balanceWrapper.minus(maxFee); if (amountWithoutFees.isNegative()) { amountWithoutFees = new bignumber_1.default(0); } return [2 /*return*/, amountWithoutFees.toFixed()]; } }); }); }; AeternityProtocol.prototype.estimateFeeDefaultsFromPublicKey = function (publicKey, recipients, values, data) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, index_1.default.get(this.feesURL)]; case 1: return [2 /*return*/, (_a.sent()).data]; } }); }); }; AeternityProtocol.prototype.prepareTransactionFromPublicKey = function (publicKey, recipients, values, fee, payload) { return __awaiter(this, void 0, void 0, function () { var nonce, address, accountResponse, error_2, balance, _a, sender, recipient, txObj, txArray, rlpEncodedTx, preparedTx; return __generator(this, function (_b) { switch (_b.label) { case 0: nonce = 1; return [4 /*yield*/, this.getAddressFromPublicKey(publicKey)]; case 1: address = _b.sent(); _b.label = 2; case 2: _b.trys.push([2, 4, , 5]); return [4 /*yield*/, index_1.default.get(this.options.network.rpcUrl + "/v2/accounts/" + address)]; case 3: accountResponse = (_b.sent()).data; nonce = accountResponse.nonce + 1; return [3 /*break*/, 5]; case 4: error_2 = _b.sent(); // if node returns 404 (which means 'no account found'), go with nonce 0 if (error_2.response && error_2.response.status !== 404) { throw error_2; } return [3 /*break*/, 5]; case 5: _a = bignumber_1.default.bind; return [4 /*yield*/, this.getBalanceOfPublicKey(publicKey)]; case 6: balance = new (_a.apply(bignumber_1.default, [void 0, _b.sent()]))(); if (balance.isLessThan(fee)) { throw new Error('not enough balance'); } sender = publicKey; recipient = bs58check.decode(recipients[0].replace('ak_', '')); txObj = { tag: this.toHexBuffer(12), version: this.toHexBuffer(1), sender_id: Buffer.concat([this.toHexBuffer(1), Buffer.from(sender, 'hex')]), recipient_id: Buffer.concat([this.toHexBuffer(1), recipient]), amount: this.toHexBuffer(new bignumber_1.default(values[0])), fee: this.toHexBuffer(new bignumber_1.default(fee)), ttl: this.toHexBuffer(0), nonce: this.toHexBuffer(nonce), payload: Buffer.from(payload || '') }; txArray = Object.keys(txObj).map(function (a) { return txObj[a]; }); rlpEncodedTx = rlp.encode(txArray); preparedTx = "tx_" + base64Check_1.default.encode(rlpEncodedTx); return [2 /*return*/, { transaction: preparedTx, networkId: this.defaultNetworkId }]; } }); }); }; /** * This is a function that we only use to fix incompatibilitis with old vault versions that are unable to understand b64 encoded Txs. * * @deprecated * @param preparedTx */ AeternityProtocol.prototype.convertTxToBase58 = function (preparedTx) { return { transaction: bs58check.encode(base64Check_1.default.decode(preparedTx.transaction)), networkId: preparedTx.networkId }; }; AeternityProtocol.prototype.broadcastTransaction = function (rawTransaction) { return __awaiter(this, void 0, void 0, function () { var data; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, index_1.default.post(this.options.network.rpcUrl + "/v2/transactions", { tx: rawTransaction }, { headers: { 'Content-Type': 'application/json' } })]; case 1: data = (_a.sent()).data; return [2 /*return*/, data.tx_hash]; } }); }); }; AeternityProtocol.prototype.toHexBuffer = function (value) { var hexString = utils_1.EthereumUtils.toHex(value).substr(2); return Buffer.from(padStart_1.padStart(hexString, hexString.length % 2 === 0 ? hexString.length : hexString.length + 1, '0'), 'hex'); }; AeternityProtocol.prototype.signMessage = function (message, keypair) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, new AeternityCryptoClient_1.AeternityCryptoClient().signMessage(message, keypair)]; }); }); }; AeternityProtocol.prototype.verifyMessage = function (message, signature, publicKey) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, new AeternityCryptoClient_1.AeternityCryptoClient().verifyMessage(message, signature, publicKey)]; }); }); }; AeternityProtocol.prototype.getTransactionStatuses = function (transactionHashes) { return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { return [2 /*return*/, Promise.reject('Transaction status not implemented')]; }); }); }; return AeternityProtocol; }(NonExtendedProtocol_1.NonExtendedProtocol)); exports.AeternityProtocol = AeternityProtocol; //# sourceMappingURL=AeternityProtocol.js.map