UNPKG

airgap-coin-lib

Version:

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

295 lines 17.3 kB
"use strict"; 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 wasm_crypto_1 = require("@polkadot/wasm-crypto"); var bignumber_1 = require("../../../dependencies/src/bignumber.js-9.0.0/bignumber"); var blake2b_1 = require("../../../utils/blake2b"); var SubstrateAddress_1 = require("./data/account/SubstrateAddress"); var SCALEDecoder_1 = require("./data/scale/SCALEDecoder"); var SCALEArray_1 = require("./data/scale/type/SCALEArray"); var SCALEBytes_1 = require("./data/scale/type/SCALEBytes"); var SCALECompactInt_1 = require("./data/scale/type/SCALECompactInt"); var SCALEEnum_1 = require("./data/scale/type/SCALEEnum"); var SubstrateTransactionMethod_1 = require("./data/transaction/method/SubstrateTransactionMethod"); var SubstrateSignature_1 = require("./data/transaction/SubstrateSignature"); var SubstrateTransaction_1 = require("./data/transaction/SubstrateTransaction"); var SubstrateTransactionPayload_1 = require("./data/transaction/SubstrateTransactionPayload"); var SubstrateTransactionController = /** @class */ (function () { function SubstrateTransactionController(network, nodeClient) { this.network = network; this.nodeClient = nodeClient; } SubstrateTransactionController.prototype.prepareSubmittableTransactions = function (accountId, available, txConfig) { return __awaiter(this, void 0, void 0, function () { var accountInfo, nonce, txs, totalFee; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.nodeClient.getAccountInfo(SubstrateAddress_1.SubstrateAddress.from(accountId, this.network))]; case 1: accountInfo = _a.sent(); if (!accountInfo) { return [2 /*return*/, Promise.reject('Could not fetch all necessary data')]; } nonce = accountInfo.nonce.value; return [4 /*yield*/, Promise.all(txConfig.map(function (tx, index) { return _this.prepareTransactionDetails(tx.type, accountId, tx.tip, nonce.plus(index), tx.args); }))]; case 2: txs = _a.sent(); totalFee = txs.map(function (tx) { return tx.fee; }).reduce(function (total, next) { return total.plus(next); }, new bignumber_1.default(0)); if (new bignumber_1.default(available).lt(totalFee)) { throw new Error("Not enough balance"); } return [2 /*return*/, this.encodeDetails(txs)]; } }); }); }; SubstrateTransactionController.prototype.createTransaction = function (type, accountId, tip, args, chainHeight, nonce) { if (tip === void 0) { tip = 0; } if (args === void 0) { args = {}; } if (chainHeight === void 0) { chainHeight = 1; } if (nonce === void 0) { nonce = 0; } return __awaiter(this, void 0, void 0, function () { var methodId; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.nodeClient.getTransactionMetadata(type)]; case 1: methodId = _a.sent(); return [2 /*return*/, SubstrateTransaction_1.SubstrateTransaction.create(this.network, type, { from: accountId, tip: bignumber_1.default.isBigNumber(tip) ? tip : new bignumber_1.default(tip), methodId: methodId, args: args, era: { chainHeight: chainHeight }, nonce: nonce })]; } }); }); }; SubstrateTransactionController.prototype.createTransactionMethod = function (type, args) { if (args === void 0) { args = {}; } return __awaiter(this, void 0, void 0, function () { var methodId; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.nodeClient.getTransactionMetadata(type)]; case 1: methodId = _a.sent(); return [2 /*return*/, SubstrateTransactionMethod_1.SubstrateTransactionMethod.create(this.network, type, methodId.moduleIndex, methodId.callIndex, args)]; } }); }); }; SubstrateTransactionController.prototype.signTransaction = function (privateKey, transaction, payload) { return __awaiter(this, void 0, void 0, function () { var signature; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.signPayload(privateKey, transaction.signer.asBytes(), payload.encode())]; case 1: signature = _a.sent(); return [2 /*return*/, SubstrateTransaction_1.SubstrateTransaction.fromTransaction(transaction, { signature: signature })]; } }); }); }; SubstrateTransactionController.prototype.encodeDetails = function (txs) { var bytesEncoded = txs.map(function (tx) { var scaleTypes = SCALEEnum_1.SCALEEnum.from(tx.transaction.type); var scaleFee = SCALECompactInt_1.SCALECompactInt.from(tx.fee); return SCALEBytes_1.SCALEBytes.from(scaleTypes.encode() + scaleFee.encode() + tx.transaction.encode() + tx.payload.encode()); }); return SCALEArray_1.SCALEArray.from(bytesEncoded).encode(); }; SubstrateTransactionController.prototype.decodeDetails = function (serialized) { var _this = this; var decoder = new SCALEDecoder_1.SCALEDecoder(this.network, serialized); var encodedTxs = decoder .decodeNextArray(function (_, hex) { return SCALEBytes_1.SCALEBytes.decode(hex); }) .decoded.elements.map(function (bytesEncoded) { return bytesEncoded.bytes.toString('hex'); }); return encodedTxs.map(function (encodedTx) { var txDecoder = new SCALEDecoder_1.SCALEDecoder(_this.network, encodedTx); var type = txDecoder.decodeNextEnum(function (hex) { return SubstrateTransaction_1.SubstrateTransactionType[SubstrateTransaction_1.SubstrateTransactionType[hex]]; }); var fee = txDecoder.decodeNextCompactInt(); var transaction = txDecoder.decodeNextObject(function (network, hex) { return SubstrateTransaction_1.SubstrateTransaction.decode(network, type.decoded.value, hex); }); var payload = txDecoder.decodeNextObject(function (network, hex) { return SubstrateTransactionPayload_1.SubstrateTransactionPayload.decode(network, type.decoded.value, hex); }); return { fee: fee.decoded.value, transaction: transaction.decoded, payload: payload.decoded }; }); }; SubstrateTransactionController.prototype.calculateTransactionFee = function (transaction) { var _a; return __awaiter(this, void 0, void 0, function () { var partialEstimate; return __generator(this, function (_b) { switch (_b.label) { case 0: return [4 /*yield*/, this.nodeClient.getTransferFeeEstimate(transaction.encode())]; case 1: partialEstimate = _b.sent(); if (partialEstimate) { this.nodeClient.saveLastFee(transaction.type, partialEstimate); } return [2 /*return*/, ((_a = partialEstimate) === null || _a === void 0 ? void 0 : _a.plus(transaction.tip.value)) || null]; } }); }); }; SubstrateTransactionController.prototype.estimateTransactionFees = function (accountId, transationTypes) { return __awaiter(this, void 0, void 0, function () { var fees, safetyFactor; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, Promise.all(transationTypes .map(function (_a) { var type = _a[0], args = _a[1]; return [type, args, _this.nodeClient.getSavedLastFee(type, 'largest')]; }) .map(function (_a) { var type = _a[0], args = _a[1], fee = _a[2]; return __awaiter(_this, void 0, void 0, function () { var _b, _c; return __generator(this, function (_d) { switch (_d.label) { case 0: if (!fee) return [3 /*break*/, 1]; _b = fee; return [3 /*break*/, 3]; case 1: _c = this.calculateTransactionFee; return [4 /*yield*/, this.createTransaction(type, SubstrateAddress_1.SubstrateAddress.from(accountId, this.network), 0, args)]; case 2: _b = _c.apply(this, [_d.sent()]); _d.label = 3; case 3: return [2 /*return*/, _b]; } }); }); }))]; case 1: fees = _a.sent(); if (fees.some(function (fee) { return fee === null; })) { return [2 /*return*/, Promise.reject('Could not estimate transaction fees.')]; } safetyFactor = 1.2; return [2 /*return*/, fees.reduce(function (sum, next) { return sum.plus(next); }, new bignumber_1.default(0)).multipliedBy(safetyFactor)]; } }); }); }; SubstrateTransactionController.prototype.prepareTransactionDetails = function (type, accountId, tip, nonce, args) { if (args === void 0) { args = {}; } return __awaiter(this, void 0, void 0, function () { var chainHeight, results, transaction, fee, lastHash, genesisHash, runtimeVersion, payload; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, this.nodeClient.getCurrentHeight()]; case 1: chainHeight = _a.sent(); return [4 /*yield*/, Promise.all([ this.createTransaction(type, accountId, tip, args, chainHeight, nonce), this.nodeClient.getLastBlockHash(), this.nodeClient.getFirstBlockHash(), this.nodeClient.getRuntimeVersion() ])]; case 2: results = _a.sent(); if (results.some(function (result) { return result === null; })) { return [2 /*return*/, Promise.reject('Could not fetch all necessary data.')]; } transaction = results[0]; return [4 /*yield*/, this.calculateTransactionFee(transaction)]; case 3: fee = _a.sent(); if (!fee) { return [2 /*return*/, Promise.reject('Could not fetch all necessary data.')]; } lastHash = results[1]; genesisHash = results[2]; runtimeVersion = results[3]; payload = SubstrateTransactionPayload_1.SubstrateTransactionPayload.create(transaction, { specVersion: runtimeVersion.specVersion, transactionVersion: runtimeVersion.transactionVersion, genesisHash: genesisHash, lastHash: lastHash }); return [2 /*return*/, { fee: fee, transaction: transaction, payload: payload }]; } }); }); }; SubstrateTransactionController.prototype.signPayload = function (privateKey, publicKey, payload, signatureType) { if (signatureType === void 0) { signatureType = SubstrateSignature_1.SubstrateSignatureType.Sr25519; } return __awaiter(this, void 0, void 0, function () { return __generator(this, function (_a) { switch (signatureType) { case SubstrateSignature_1.SubstrateSignatureType.Sr25519: return [2 /*return*/, this.signSr25519Payload(privateKey, publicKey, payload)]; default: return [2 /*return*/, Promise.reject('Signature type not supported.')]; } return [2 /*return*/]; }); }); }; SubstrateTransactionController.prototype.signSr25519Payload = function (privateKey, publicKey, payload) { return __awaiter(this, void 0, void 0, function () { var payloadBuffer, message, signature; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, wasm_crypto_1.waitReady()]; case 1: _a.sent(); payloadBuffer = Buffer.from(payload, 'hex'); message = payloadBuffer.length > 256 ? blake2b_1.blake2bAsBytes(payloadBuffer, 256) : payloadBuffer; signature = wasm_crypto_1.sr25519Sign(publicKey, privateKey, message); return [2 /*return*/, SubstrateSignature_1.SubstrateSignature.create(SubstrateSignature_1.SubstrateSignatureType.Sr25519, signature)]; } }); }); }; return SubstrateTransactionController; }()); exports.SubstrateTransactionController = SubstrateTransactionController; //# sourceMappingURL=SubstrateTransactionController.js.map