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
JavaScript
"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