@harmony-js/transaction
Version:
transaction package for harmony
526 lines • 26.5 kB
JavaScript
"use strict";
/**
* @packageDocumentation
* @module harmony-transaction
* @hidden
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TransactionBase = void 0;
var tslib_1 = require("tslib");
var crypto_1 = require("@harmony-js/crypto");
var utils_1 = require("@harmony-js/utils");
var network_1 = require("@harmony-js/network");
var types_1 = require("./types");
var utils_2 = require("./utils");
var TransactionBase = /** @class */ (function () {
function TransactionBase(messenger, txStatus) {
this.blockNumbers = [];
this.confirmations = 0;
this.confirmationCheck = 0;
this.cxStatus = types_1.TxStatus.INTIALIZED;
this.cxBlockNumbers = [];
this.cxConfirmations = 0;
this.cxConfirmationCheck = 0;
this.messenger = messenger;
this.txStatus = txStatus;
this.emitter = new network_1.Emitter();
this.id = '0x';
this.shardID = this.messenger.currentShard;
}
TransactionBase.normalizeAddress = function (address) {
if (address === '0x') {
return '0x';
}
else if (crypto_1.HarmonyAddress.isValidChecksum(address) ||
crypto_1.HarmonyAddress.isValidBech32(address) ||
crypto_1.HarmonyAddress.isValidBech32TestNet(address)) {
return crypto_1.getAddress(address).checksum;
}
else {
throw new Error("Address format is not supported");
}
};
TransactionBase.prototype.setMessenger = function (messenger) {
this.messenger = messenger;
};
TransactionBase.prototype.setTxStatus = function (txStatus) {
this.txStatus = txStatus;
};
TransactionBase.prototype.getTxStatus = function () {
return this.txStatus;
};
TransactionBase.prototype.setCxStatus = function (cxStatus) {
this.cxStatus = cxStatus;
};
TransactionBase.prototype.getCxStatus = function () {
return this.cxStatus;
};
// get status
TransactionBase.prototype.isInitialized = function () {
return this.getTxStatus() === types_1.TxStatus.INTIALIZED;
};
TransactionBase.prototype.isSigned = function () {
return this.getTxStatus() === types_1.TxStatus.SIGNED;
};
TransactionBase.prototype.isPending = function () {
return this.getTxStatus() === types_1.TxStatus.PENDING;
};
TransactionBase.prototype.isRejected = function () {
return this.getTxStatus() === types_1.TxStatus.REJECTED;
};
TransactionBase.prototype.isConfirmed = function () {
return this.getTxStatus() === types_1.TxStatus.CONFIRMED;
};
TransactionBase.prototype.isCxPending = function () {
return this.getCxStatus() === types_1.TxStatus.PENDING;
};
TransactionBase.prototype.isCxRejected = function () {
return this.getCxStatus() === types_1.TxStatus.REJECTED;
};
TransactionBase.prototype.isCxConfirmed = function () {
return this.getCxStatus() === types_1.TxStatus.CONFIRMED;
};
TransactionBase.prototype.observed = function () {
return this.emitter;
};
TransactionBase.prototype.trackTx = function (txHash, shardID) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var res, currentBlock, currentBlock;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.messenger) {
throw new Error('Messenger not found');
}
return [4 /*yield*/, this.messenger.send(network_1.RPCMethod.GetTransactionReceipt, txHash, this.messenger.chainType, typeof shardID === 'string' ? Number.parseInt(shardID, 10) : shardID)];
case 1:
res = _a.sent();
if (!(res.isResult() && res.result !== null)) return [3 /*break*/, 5];
this.receipt = res.result;
this.emitReceipt(this.receipt);
this.id = res.result.transactionHash;
this.confirmations += 1;
if (!this.receipt) return [3 /*break*/, 2];
if (this.receipt.status && this.receipt.status === '0x1') {
this.receipt.byzantium = true;
this.txStatus = types_1.TxStatus.CONFIRMED;
}
else if (this.receipt.status && this.receipt.status === '0x0') {
this.receipt.byzantium = true;
this.txStatus = types_1.TxStatus.REJECTED;
}
else if (this.receipt.status === undefined && this.receipt.root) {
this.receipt.byzantium = false;
this.txStatus = types_1.TxStatus.CONFIRMED;
}
return [2 /*return*/, true];
case 2:
this.txStatus = types_1.TxStatus.PENDING;
return [4 /*yield*/, this.getBlockNumber(shardID)];
case 3:
currentBlock = _a.sent();
this.blockNumbers.push('0x' + currentBlock.toString('hex'));
this.confirmationCheck += 1;
return [2 /*return*/, false];
case 4: return [3 /*break*/, 7];
case 5:
this.txStatus = types_1.TxStatus.PENDING;
return [4 /*yield*/, this.getBlockNumber(shardID)];
case 6:
currentBlock = _a.sent();
this.blockNumbers.push('0x' + currentBlock.toString('hex'));
this.confirmationCheck += 1;
return [2 /*return*/, false];
case 7: return [2 /*return*/];
}
});
});
};
TransactionBase.prototype.txConfirm = function (txHash, maxAttempts, interval, shardID) {
if (maxAttempts === void 0) { maxAttempts = 20; }
if (interval === void 0) { interval = 1000; }
return tslib_1.__awaiter(this, void 0, void 0, function () {
var oldBlock, checkBlock, attempt, newBlock, nextBlock, err_1, result, error_1;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!(this.messenger.provider instanceof network_1.HttpProvider)) return [3 /*break*/, 13];
this.txStatus = types_1.TxStatus.PENDING;
return [4 /*yield*/, this.getBlockNumber(shardID)];
case 1:
oldBlock = _a.sent();
checkBlock = oldBlock;
attempt = 0;
_a.label = 2;
case 2:
if (!(attempt < maxAttempts)) return [3 /*break*/, 12];
_a.label = 3;
case 3:
_a.trys.push([3, 8, , 9]);
return [4 /*yield*/, this.getBlockNumber(shardID)];
case 4:
newBlock = _a.sent();
nextBlock = checkBlock.add(new crypto_1.BN(attempt === 0 ? attempt : 1));
if (!newBlock.gte(nextBlock)) return [3 /*break*/, 6];
checkBlock = newBlock;
this.emitTrack({
txHash: txHash,
attempt: attempt,
currentBlock: checkBlock.toString(),
shardID: shardID,
});
return [4 /*yield*/, this.trackTx(txHash, shardID)];
case 5:
if (_a.sent()) {
this.emitConfirm(this.txStatus);
return [2 /*return*/, this];
}
return [3 /*break*/, 7];
case 6:
attempt = attempt - 1 >= 0 ? attempt - 1 : 0;
_a.label = 7;
case 7: return [3 /*break*/, 9];
case 8:
err_1 = _a.sent();
this.txStatus = types_1.TxStatus.REJECTED;
this.emitConfirm(this.txStatus);
throw err_1;
case 9:
if (!(attempt + 1 < maxAttempts)) return [3 /*break*/, 11];
// await sleep(interval * attempt);
return [4 /*yield*/, utils_2.sleep(interval)];
case 10:
// await sleep(interval * attempt);
_a.sent();
_a.label = 11;
case 11:
attempt += 1;
return [3 /*break*/, 2];
case 12:
this.txStatus = types_1.TxStatus.REJECTED;
this.emitConfirm(this.txStatus);
throw new Error("The transaction is still not confirmed after " + maxAttempts + " attempts.");
case 13:
_a.trys.push([13, 18, , 19]);
return [4 /*yield*/, this.trackTx(txHash, shardID)];
case 14:
if (!_a.sent()) return [3 /*break*/, 15];
this.emitConfirm(this.txStatus);
return [2 /*return*/, this];
case 15: return [4 /*yield*/, this.socketConfirm(txHash, maxAttempts, shardID)];
case 16:
result = _a.sent();
return [2 /*return*/, result];
case 17: return [3 /*break*/, 19];
case 18:
error_1 = _a.sent();
this.txStatus = types_1.TxStatus.REJECTED;
this.emitConfirm(this.txStatus);
throw new Error("The transaction is still not confirmed after " + maxAttempts * interval + " mil seconds.");
case 19: return [2 /*return*/];
}
});
});
};
TransactionBase.prototype.socketConfirm = function (txHash, maxAttempts, shardID) {
var _this = this;
if (maxAttempts === void 0) { maxAttempts = 20; }
return new Promise(function (resolve, reject) {
var newHeads = Promise.resolve(new network_1.NewHeaders(_this.messenger, typeof shardID === 'string' ? Number.parseInt(shardID, 10) : shardID));
newHeads.then(function (p) {
p.onData(function (data) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var blockNumber;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
blockNumber = this.messenger.chainPrefix === 'hmy'
? data.params.result.Header.number
: data.params.result.number;
this.emitTrack({
txHash: txHash,
attempt: this.confirmationCheck,
currentBlock: utils_1.hexToNumber(blockNumber),
shardID: shardID,
});
if (!!this.blockNumbers.includes(blockNumber)) return [3 /*break*/, 5];
return [4 /*yield*/, this.trackTx(txHash, shardID)];
case 1:
if (!_a.sent()) return [3 /*break*/, 3];
this.emitConfirm(this.txStatus);
return [4 /*yield*/, p.unsubscribe()];
case 2:
_a.sent();
resolve(this);
return [3 /*break*/, 5];
case 3:
if (!(this.confirmationCheck === maxAttempts)) return [3 /*break*/, 5];
this.txStatus = types_1.TxStatus.REJECTED;
this.emitConfirm(this.txStatus);
return [4 /*yield*/, p.unsubscribe()];
case 4:
_a.sent();
resolve(this);
_a.label = 5;
case 5: return [2 /*return*/];
}
});
}); }).onError(function (error) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
this.txStatus = types_1.TxStatus.REJECTED;
this.emitConfirm(this.txStatus);
this.emitError(error);
return [4 /*yield*/, p.unsubscribe()];
case 1:
_a.sent();
reject(error);
return [2 /*return*/];
}
});
}); });
});
});
};
TransactionBase.prototype.emitTransactionHash = function (transactionHash) {
this.emitter.emit(utils_2.TransactionEvents.transactionHash, transactionHash);
};
TransactionBase.prototype.emitReceipt = function (receipt) {
this.emitter.emit(utils_2.TransactionEvents.receipt, receipt);
};
TransactionBase.prototype.emitError = function (error) {
this.emitter.emit(utils_2.TransactionEvents.error, error);
};
TransactionBase.prototype.emitConfirm = function (data) {
this.emitter.emit(utils_2.TransactionEvents.confirmation, data);
};
TransactionBase.prototype.emitTrack = function (data) {
this.emitter.emit(utils_2.TransactionEvents.track, data);
};
TransactionBase.prototype.emitCxReceipt = function (receipt) {
this.emitter.emit(utils_2.TransactionEvents.cxReceipt, receipt);
};
TransactionBase.prototype.emitCxConfirm = function (data) {
this.emitter.emit(utils_2.TransactionEvents.cxConfirmation, data);
};
TransactionBase.prototype.emitCxTrack = function (data) {
this.emitter.emit(utils_2.TransactionEvents.cxTrack, data);
};
TransactionBase.prototype.getBlockNumber = function (shardID) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var currentBlock, error_2;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, this.messenger.send(network_1.RPCMethod.BlockNumber, [], this.messenger.chainPrefix, typeof shardID === 'string' ? Number.parseInt(shardID, 10) : shardID)];
case 1:
currentBlock = _a.sent();
if (currentBlock.isError()) {
throw currentBlock.message;
}
return [2 /*return*/, new crypto_1.BN(currentBlock.result.replace('0x', ''), 'hex')];
case 2:
error_2 = _a.sent();
throw error_2;
case 3: return [2 /*return*/];
}
});
});
};
TransactionBase.prototype.getBlockByNumber = function (blockNumber) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var block, error_3;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0, 2, , 3]);
return [4 /*yield*/, this.messenger.send(network_1.RPCMethod.GetBlockByNumber, [blockNumber, true], this.messenger.chainPrefix, typeof this.shardID === 'string' ? Number.parseInt(this.shardID, 10) : this.shardID)];
case 1:
block = _a.sent();
if (block.isError()) {
throw block.message;
}
return [2 /*return*/, block.result];
case 2:
error_3 = _a.sent();
throw error_3;
case 3: return [2 /*return*/];
}
});
});
};
TransactionBase.prototype.cxConfirm = function (txHash, maxAttempts, interval, toShardID) {
if (maxAttempts === void 0) { maxAttempts = 20; }
if (interval === void 0) { interval = 1000; }
return tslib_1.__awaiter(this, void 0, void 0, function () {
var oldBlock, checkBlock, attempt, newBlock, nextBlock, err_2, result, error_4;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!(this.messenger.provider instanceof network_1.HttpProvider)) return [3 /*break*/, 13];
return [4 /*yield*/, this.getBlockNumber(toShardID)];
case 1:
oldBlock = _a.sent();
checkBlock = oldBlock;
attempt = 0;
_a.label = 2;
case 2:
if (!(attempt < maxAttempts)) return [3 /*break*/, 12];
_a.label = 3;
case 3:
_a.trys.push([3, 8, , 9]);
return [4 /*yield*/, this.getBlockNumber(toShardID)];
case 4:
newBlock = _a.sent();
nextBlock = checkBlock.add(new crypto_1.BN(attempt === 0 ? attempt : 1));
if (!newBlock.gte(nextBlock)) return [3 /*break*/, 6];
checkBlock = newBlock;
this.emitCxTrack({
txHash: txHash,
attempt: attempt,
currentBlock: checkBlock.toString(),
toShardID: toShardID,
});
return [4 /*yield*/, this.trackCx(txHash, toShardID)];
case 5:
if (_a.sent()) {
this.emitCxConfirm(this.cxStatus);
return [2 /*return*/, this];
}
return [3 /*break*/, 7];
case 6:
attempt = attempt - 1 >= 0 ? attempt - 1 : 0;
_a.label = 7;
case 7: return [3 /*break*/, 9];
case 8:
err_2 = _a.sent();
this.cxStatus = types_1.TxStatus.REJECTED;
this.emitCxConfirm(this.cxStatus);
throw err_2;
case 9:
if (!(attempt + 1 < maxAttempts)) return [3 /*break*/, 11];
return [4 /*yield*/, utils_2.sleep(interval)];
case 10:
_a.sent();
_a.label = 11;
case 11:
attempt += 1;
return [3 /*break*/, 2];
case 12:
this.cxStatus = types_1.TxStatus.REJECTED;
this.emitCxConfirm(this.cxStatus);
throw new Error("The transaction is still not confirmed after " + maxAttempts + " attempts.");
case 13:
_a.trys.push([13, 18, , 19]);
return [4 /*yield*/, this.trackCx(txHash, toShardID)];
case 14:
if (!_a.sent()) return [3 /*break*/, 15];
this.emitCxConfirm(this.cxStatus);
return [2 /*return*/, this];
case 15: return [4 /*yield*/, this.socketCxConfirm(txHash, maxAttempts, toShardID)];
case 16:
result = _a.sent();
return [2 /*return*/, result];
case 17: return [3 /*break*/, 19];
case 18:
error_4 = _a.sent();
this.cxStatus = types_1.TxStatus.REJECTED;
this.emitCxConfirm(this.cxStatus);
throw new Error("The transaction is still not confirmed after " + maxAttempts * interval + " mil seconds.");
case 19: return [2 /*return*/];
}
});
});
};
TransactionBase.prototype.trackCx = function (txHash, toShardID) {
return tslib_1.__awaiter(this, void 0, void 0, function () {
var res, currentBlock;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!this.messenger) {
throw new Error('Messenger not found');
}
return [4 /*yield*/, this.messenger.send(network_1.RPCMethod.GetCXReceiptByHash, txHash, this.messenger.chainPrefix, typeof toShardID === 'string' ? Number.parseInt(toShardID, 10) : toShardID)];
case 1:
res = _a.sent();
if (!(res.isResult() && res.result !== null)) return [3 /*break*/, 2];
this.emitCxReceipt(res.result);
this.cxStatus = types_1.TxStatus.CONFIRMED;
return [2 /*return*/, true];
case 2: return [4 /*yield*/, this.getBlockNumber(toShardID)];
case 3:
currentBlock = _a.sent();
this.cxBlockNumbers.push('0x' + currentBlock.toString('hex'));
this.cxConfirmationCheck += 1;
this.cxStatus = types_1.TxStatus.PENDING;
return [2 /*return*/, false];
}
});
});
};
TransactionBase.prototype.socketCxConfirm = function (txHash, maxAttempts, toShardID) {
var _this = this;
if (maxAttempts === void 0) { maxAttempts = 20; }
return new Promise(function (resolve, reject) {
var newHeads = Promise.resolve(new network_1.NewHeaders(_this.messenger, typeof toShardID === 'string' ? Number.parseInt(toShardID, 10) : toShardID));
newHeads.then(function (p) {
p.onData(function (data) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
var blockNumber;
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
blockNumber = this.messenger.chainPrefix === 'hmy'
? data.params.result.Header.number
: data.params.result.number;
this.emitCxTrack({
txHash: txHash,
attempt: this.cxConfirmationCheck,
currentBlock: utils_1.hexToNumber(blockNumber),
toShardID: toShardID,
});
if (!!this.blockNumbers.includes(blockNumber)) return [3 /*break*/, 5];
return [4 /*yield*/, this.trackCx(txHash, toShardID)];
case 1:
if (!_a.sent()) return [3 /*break*/, 3];
this.emitCxConfirm(this.cxStatus);
return [4 /*yield*/, p.unsubscribe()];
case 2:
_a.sent();
resolve(this);
return [3 /*break*/, 5];
case 3:
if (!(this.cxConfirmationCheck === maxAttempts)) return [3 /*break*/, 5];
this.cxStatus = types_1.TxStatus.REJECTED;
this.emitCxConfirm(this.cxStatus);
return [4 /*yield*/, p.unsubscribe()];
case 4:
_a.sent();
resolve(this);
_a.label = 5;
case 5: return [2 /*return*/];
}
});
}); }).onError(function (error) { return tslib_1.__awaiter(_this, void 0, void 0, function () {
return tslib_1.__generator(this, function (_a) {
switch (_a.label) {
case 0:
this.cxStatus = types_1.TxStatus.REJECTED;
this.emitCxConfirm(this.cxStatus);
this.emitError(error);
return [4 /*yield*/, p.unsubscribe()];
case 1:
_a.sent();
reject(error);
return [2 /*return*/];
}
});
}); });
});
});
};
return TransactionBase;
}());
exports.TransactionBase = TransactionBase;
//# sourceMappingURL=transactionBase.js.map