airgap-coin-lib
Version:
The airgap-coin-lib is a protocol agnostic library to prepare, sign and broadcast cryptocurrency transactions.
638 lines • 35.1 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 index_1 = require("../../../dependencies/src/axios-0.19.0/index");
var bignumber_1 = require("../../../dependencies/src/bignumber.js-9.0.0/bignumber");
var TezosProtocol_1 = require("../TezosProtocol");
var TezosRewardsCalculationDefault = /** @class */ (function () {
function TezosRewardsCalculationDefault(protocol) {
this.protocol = protocol;
this.blockLevelFieldNameMap = new Map();
this.tezosNodeConstants = {};
}
TezosRewardsCalculationDefault.prototype.getConstants = function (currentLevel, useHead) {
return __awaiter(this, void 0, void 0, function () {
var _a;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
_a = this;
return [4 /*yield*/, index_1.default.get(this.protocol.jsonRPCAPI + "/chains/main/blocks/" + (useHead ? 'head' : currentLevel) + "/context/constants")];
case 1:
_a.tezosNodeConstants = (_b.sent()).data;
return [2 /*return*/];
}
});
});
};
TezosRewardsCalculationDefault.prototype.calculateRewards = function (bakerAddress, cycle, breakdownRewards, currentCycleIn) {
if (breakdownRewards === void 0) { breakdownRewards = true; }
return __awaiter(this, void 0, void 0, function () {
var currentCycle, _a, calculatingLevel, computedRewards, snapshotLevel, bakerInfo, stakingBalance;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
if (!(currentCycleIn !== null && currentCycleIn !== void 0)) return [3 /*break*/, 1];
_a = currentCycleIn;
return [3 /*break*/, 3];
case 1: return [4 /*yield*/, this.protocol.fetchCurrentCycle()];
case 2:
_a = (_b.sent());
_b.label = 3;
case 3:
currentCycle = (_a);
calculatingLevel = this.cycleToBlockLevel(cycle);
return [4 /*yield*/, this.getConstants(calculatingLevel, currentCycle < cycle)];
case 4:
_b.sent();
if (!(cycle < currentCycle)) return [3 /*break*/, 6];
return [4 /*yield*/, this.calculatePastRewards(bakerAddress, cycle, breakdownRewards)];
case 5:
computedRewards = _b.sent();
return [3 /*break*/, 8];
case 6: return [4 /*yield*/, this.calculateFutureRewards(bakerAddress, cycle, currentCycle, breakdownRewards)];
case 7:
computedRewards = _b.sent();
_b.label = 8;
case 8: return [4 /*yield*/, this.computeSnapshotBlockLevel(Math.min(cycle, currentCycle))];
case 9:
snapshotLevel = _b.sent();
return [4 /*yield*/, this.fetchBakerInfo(bakerAddress, snapshotLevel).catch(function () {
return { staking_balance: '0', delegated_contracts: [] };
})];
case 10:
bakerInfo = _b.sent();
stakingBalance = new bignumber_1.default(bakerInfo.staking_balance);
return [2 /*return*/, {
baker: bakerAddress,
stakingBalance: stakingBalance.toFixed(),
bakingRewards: computedRewards.bakingRewards,
endorsingRewards: computedRewards.endorsingRewards,
bakingDeposits: computedRewards.bakingRewardsDetails
.map(function (detail) { return (detail && detail.deposit ? (detail.deposit ? parseInt(detail.deposit) : 0) : 0); })
.reduce(function (a, b) { return a + b; }, 0)
.toString(),
endorsingDeposits: computedRewards.endorsingRewardsDetails
.map(function (detail) { return (detail && detail.deposit ? (detail.deposit ? parseInt(detail.deposit) : 0) : 0); })
.reduce(function (a, b) { return a + b; }, 0)
.toString(),
cycle: cycle,
fees: computedRewards.fees,
totalRewards: computedRewards.totalRewards,
snapshotBlockLevel: snapshotLevel,
delegatedContracts: bakerInfo.delegated_contracts,
bakingRewardsDetails: computedRewards.bakingRewardsDetails,
endorsingRewardsDetails: computedRewards.endorsingRewardsDetails,
deposit: computedRewards.deposit,
endorsingRightsCount: computedRewards.endorsingRightsCount
}];
}
});
});
};
TezosRewardsCalculationDefault.prototype.calculatePastRewards = function (bakerAddress, cycle, breakdownRewards) {
return __awaiter(this, void 0, void 0, function () {
var computedBakingRewards, computedEndorsingRewards, fees, totalRewards, deposit, endorsingRightsCount, bakedBlocks, endorsingOperations, frozenBalance;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
computedBakingRewards = {
totalBakingRewards: '0',
rewardsDetails: []
};
computedEndorsingRewards = {
totalEndorsingRewards: '0',
rewardsDetails: []
};
fees = '0';
totalRewards = '0';
deposit = '0';
endorsingRightsCount = 0;
if (!breakdownRewards) return [3 /*break*/, 6];
return [4 /*yield*/, this.fetchBlocksForBaker(bakerAddress, cycle)];
case 1:
bakedBlocks = _a.sent();
if (!(bakedBlocks.length > 0)) return [3 /*break*/, 3];
return [4 /*yield*/, this.computeBakingRewards(bakedBlocks, false)];
case 2:
computedBakingRewards = _a.sent();
_a.label = 3;
case 3: return [4 /*yield*/, this.fetchEndorsementOperations(cycle, bakerAddress)];
case 4:
endorsingOperations = _a.sent();
return [4 /*yield*/, this.computeEndorsingRewards(endorsingOperations, false)];
case 5:
computedEndorsingRewards = _a.sent();
endorsingRightsCount = endorsingOperations.reduce(function (current, next) { return current + next.number_of_slots; }, 0);
_a.label = 6;
case 6: return [4 /*yield*/, this.fetchFrozenBalances(this.cycleToBlockLevel(cycle + 1), bakerAddress)];
case 7:
frozenBalance = (_a.sent()).find(function (fb) { return fb.cycle == cycle; });
if (frozenBalance) {
fees = frozenBalance.fees;
totalRewards = frozenBalance.rewards;
deposit = frozenBalance.deposit;
}
return [2 /*return*/, {
bakingRewards: computedBakingRewards.totalBakingRewards,
endorsingRewards: computedEndorsingRewards.totalEndorsingRewards,
totalRewards: totalRewards,
fees: fees,
bakingRewardsDetails: computedBakingRewards.rewardsDetails,
endorsingRewardsDetails: computedEndorsingRewards.rewardsDetails,
endorsingRightsCount: endorsingRightsCount,
deposit: deposit
}];
}
});
});
};
TezosRewardsCalculationDefault.prototype.calculateFutureRewards = function (bakerAddress, cycle, currentCycle, breakdownRewards) {
return __awaiter(this, void 0, void 0, function () {
var computedBakingRewards, computedEndorsingRewards, fees, totalRewards, endorsingRightsCount, bakingRights, endorsingRights, frozenBalances, lastFrozenBalance;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
computedBakingRewards = {
totalBakingRewards: '0',
rewardsDetails: []
};
computedEndorsingRewards = {
totalEndorsingRewards: '0',
rewardsDetails: []
};
fees = '0';
totalRewards = '0';
endorsingRightsCount = 0;
if (cycle - currentCycle > 5) {
throw new Error('Provided cycle is invalid');
}
if (!breakdownRewards) return [3 /*break*/, 5];
return [4 /*yield*/, this.fetchBakingRights(bakerAddress, cycle)];
case 1:
bakingRights = _a.sent();
return [4 /*yield*/, this.computeBakingRewards(bakingRights, true)];
case 2:
computedBakingRewards = _a.sent();
return [4 /*yield*/, this.fetchEndorsingRights(bakerAddress, cycle)];
case 3:
endorsingRights = _a.sent();
return [4 /*yield*/, this.computeEndorsingRewards(endorsingRights, true)];
case 4:
computedEndorsingRewards = _a.sent();
endorsingRightsCount = endorsingRights.reduce(function (current, next) { return current + next.number_of_slots; }, 0);
_a.label = 5;
case 5:
totalRewards = new bignumber_1.default(computedBakingRewards.totalBakingRewards)
.plus(new bignumber_1.default(computedEndorsingRewards.totalEndorsingRewards))
.toFixed();
return [4 /*yield*/, this.fetchFrozenBalances('head', bakerAddress)];
case 6:
frozenBalances = _a.sent();
if (frozenBalances.length > 0) {
lastFrozenBalance = frozenBalances[frozenBalances.length - 1];
fees = lastFrozenBalance.fees;
}
return [2 /*return*/, {
bakingRewards: computedBakingRewards.totalBakingRewards,
endorsingRewards: computedEndorsingRewards.totalEndorsingRewards,
totalRewards: totalRewards,
fees: fees,
bakingRewardsDetails: computedBakingRewards.rewardsDetails,
endorsingRewardsDetails: computedEndorsingRewards.rewardsDetails,
endorsingRightsCount: endorsingRightsCount,
deposit: '0'
}];
}
});
});
};
TezosRewardsCalculationDefault.prototype.fetchEndorsementOperations = function (cycle, bakerAddress) {
return __awaiter(this, void 0, void 0, function () {
var cycleStartLevel, cycleEndLevel, query, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
cycleStartLevel = this.cycleToBlockLevel(cycle);
cycleEndLevel = cycleStartLevel + this.tezosNodeConstants.blocks_per_cycle;
query = {
fields: ['level', 'delegate', 'number_of_slots', 'block_level'],
predicates: [
{
field: 'kind',
operation: 'eq',
set: ['endorsement'],
inverse: false
},
{
field: 'level',
operation: 'between',
set: [cycleStartLevel, cycleEndLevel],
inverse: false
},
{
field: 'delegate',
operation: 'eq',
set: [bakerAddress]
}
]
};
return [4 /*yield*/, index_1.default.post(this.protocol.baseApiUrl + "/v2/data/tezos/" + this.protocol.baseApiNetwork + "/operations", query, {
headers: this.protocol.headers
})];
case 1:
result = _a.sent();
return [2 /*return*/, result.data];
}
});
});
};
TezosRewardsCalculationDefault.prototype.fetchFrozenBalances = function (blockLevel, bakerAddress) {
return __awaiter(this, void 0, void 0, function () {
var result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, index_1.default.get(this.protocol.jsonRPCAPI + "/chains/main/blocks/" + blockLevel + "/context/delegates/" + bakerAddress + "/frozen_balance_by_cycle")];
case 1:
result = _a.sent();
return [2 /*return*/, result.data];
}
});
});
};
TezosRewardsCalculationDefault.prototype.computeBakingRewards = function (bakingRights, isFutureCycle) {
return __awaiter(this, void 0, void 0, function () {
var result, rewardsByLevel;
return __generator(this, function (_a) {
result = new bignumber_1.default(0);
rewardsByLevel = [];
result = new bignumber_1.default(bakingRights.length * this.tezosNodeConstants.block_reward);
return [2 /*return*/, { totalBakingRewards: result.toFixed(), rewardsDetails: rewardsByLevel }];
});
});
};
// be aware this function IS NOT identical to the one in TezosRewardCalculation005
TezosRewardsCalculationDefault.prototype.computeEndorsingRewards = function (endorsingRights, isFutureCycle) {
return __awaiter(this, void 0, void 0, function () {
var priorities, rewardsByLevel, levels, result;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
priorities = [];
rewardsByLevel = [];
if (!!isFutureCycle) return [3 /*break*/, 2];
levels = endorsingRights.map(function (er) {
return er.level;
});
if (!(levels.length > 0)) return [3 /*break*/, 2];
return [4 /*yield*/, this.fetchBlockPriorities(levels)];
case 1:
priorities = _a.sent();
_a.label = 2;
case 2:
result = endorsingRights.reduce(function (current, next) {
var priority = 0;
if (!isFutureCycle) {
var block = priorities.find(function (p) { return p.level === next.level; });
if (block === undefined) {
throw new Error('Cannot find block priority');
}
priority = block.priority;
}
var reward = _this.specificEndorsingCalculation(priority, next.number_of_slots);
rewardsByLevel.push({
level: next.level,
amount: reward.toFixed(),
deposit: _this.tezosNodeConstants.endorsement_security_deposit
});
return current.plus(reward);
}, new bignumber_1.default(0));
return [2 /*return*/, { totalEndorsingRewards: result.toFixed(), rewardsDetails: rewardsByLevel }];
}
});
});
};
TezosRewardsCalculationDefault.prototype.specificEndorsingCalculation = function (priority, number_of_slots) {
var multiplier = new bignumber_1.default(this.tezosNodeConstants.endorsement_reward).div(new bignumber_1.default(priority + 1));
var reward = new bignumber_1.default(number_of_slots).times(multiplier);
return reward;
};
TezosRewardsCalculationDefault.prototype.computeSnapshotBlockLevel = function (cycle, blockLevel) {
return __awaiter(this, void 0, void 0, function () {
var level, snapshotNumberResult, snapshotNumber, delegationCycle, firstDelegationCycleBlocklLevel, numberOfSnapshotsBeforeDelegationCycle, totalSnapshotNumber, snapshotBlockLevel;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
level = blockLevel === undefined ? this.cycleToBlockLevel(cycle) : blockLevel;
return [4 /*yield*/, index_1.default.get(this.protocol.jsonRPCAPI + "/chains/main/blocks/" + level + "/context/raw/json/cycle/" + cycle + "/roll_snapshot")];
case 1:
snapshotNumberResult = _a.sent();
snapshotNumber = snapshotNumberResult.data;
delegationCycle = cycle - 7;
firstDelegationCycleBlocklLevel = this.cycleToBlockLevel(delegationCycle);
numberOfSnapshotsBeforeDelegationCycle = firstDelegationCycleBlocklLevel / this.tezosNodeConstants.blocks_per_roll_snapshot;
totalSnapshotNumber = numberOfSnapshotsBeforeDelegationCycle + snapshotNumber + 1;
snapshotBlockLevel = totalSnapshotNumber * this.tezosNodeConstants.blocks_per_roll_snapshot;
return [2 /*return*/, snapshotBlockLevel];
}
});
});
};
TezosRewardsCalculationDefault.prototype.fetchBakerInfo = function (bakerAddress, blockLevel) {
return __awaiter(this, void 0, void 0, function () {
var bakerInfoResult;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, index_1.default.get(this.protocol.jsonRPCAPI + "/chains/main/blocks/" + blockLevel + "/context/delegates/" + bakerAddress)];
case 1:
bakerInfoResult = _a.sent();
return [2 /*return*/, bakerInfoResult.data];
}
});
});
};
TezosRewardsCalculationDefault.prototype.fetchBlocksForBaker = function (bakerAddress, cycle) {
return __awaiter(this, void 0, void 0, function () {
var query, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
query = {
fields: ['level', 'priority'],
predicates: [
{
field: 'baker',
operation: 'eq',
set: [bakerAddress],
inverse: false
},
{
field: 'meta_cycle',
operation: 'eq',
set: [cycle],
inverse: false
}
]
};
return [4 /*yield*/, index_1.default.post(this.protocol.baseApiUrl + "/v2/data/tezos/" + this.protocol.baseApiNetwork + "/blocks", query, {
headers: this.protocol.headers
})];
case 1:
result = _a.sent();
return [2 /*return*/, result.data];
}
});
});
};
TezosRewardsCalculationDefault.prototype.fetchBlockLevelFieldName = function (entity) {
return __awaiter(this, void 0, void 0, function () {
var result;
var _this = this;
return __generator(this, function (_a) {
result = this.blockLevelFieldNameMap.get(entity);
if (result) {
return [2 /*return*/, result];
}
if (this.fetchBlockLevelFieldPromise) {
return [2 /*return*/, this.fetchBlockLevelFieldPromise];
}
this.fetchBlockLevelFieldPromise = index_1.default.get(this.protocol.baseApiUrl + "/v2/metadata/tezos/" + this.protocol.baseApiNetwork + "/" + entity + "/attributes", {
headers: this.protocol.headers
}).then(function (response) {
var name = 'block_level';
var fieldNames = response.data.map(function (metadata) { return metadata.name; });
if (fieldNames.includes('level')) {
name = 'level';
}
_this.blockLevelFieldNameMap.set(entity, name);
return name;
}).finally(function () {
_this.fetchBlockLevelFieldPromise = undefined;
});
return [2 /*return*/, this.fetchBlockLevelFieldPromise];
});
});
};
TezosRewardsCalculationDefault.prototype.fetchBakingRights = function (bakerAddress, cycle) {
return __awaiter(this, void 0, void 0, function () {
var startLevel, endLevel, blockLevelFieldName, query, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
startLevel = this.cycleToBlockLevel(cycle);
endLevel = startLevel + this.tezosNodeConstants.blocks_per_cycle;
return [4 /*yield*/, this.fetchBlockLevelFieldName('baking_rights')];
case 1:
blockLevelFieldName = _a.sent();
query = {
fields: ['priority', blockLevelFieldName],
predicates: [
{
field: 'delegate',
operation: 'eq',
set: [bakerAddress]
},
{
field: 'priority',
operation: 'eq',
set: [0]
},
{
field: blockLevelFieldName,
operation: 'between',
set: [startLevel, endLevel]
}
],
orderBy: [
{
field: blockLevelFieldName,
direction: 'desc'
}
],
limit: 10000
};
return [4 /*yield*/, index_1.default
.post(this.protocol.baseApiUrl + "/v2/data/tezos/" + this.protocol.baseApiNetwork + "/baking_rights", query, {
headers: this.protocol.headers
})
.then(function (result) { return result.data; })
.catch(function (error) { return []; })];
case 2:
result = _a.sent();
return [2 /*return*/, result.map(function (bakingRight) {
return {
level: bakingRight[blockLevelFieldName],
priority: bakingRight.priority,
delegate: bakerAddress
};
})];
}
});
});
};
TezosRewardsCalculationDefault.prototype.fetchEndorsingRights = function (bakerAddress, cycle) {
return __awaiter(this, void 0, void 0, function () {
var startLevel, endLevel, blockLevelFieldName, query, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
startLevel = this.cycleToBlockLevel(cycle);
endLevel = startLevel + this.tezosNodeConstants.blocks_per_cycle;
return [4 /*yield*/, this.fetchBlockLevelFieldName('endorsing_rights')];
case 1:
blockLevelFieldName = _a.sent();
query = {
fields: [blockLevelFieldName],
predicates: [
{
field: 'delegate',
operation: 'eq',
set: [bakerAddress]
},
{
field: blockLevelFieldName,
operation: 'between',
set: [startLevel, endLevel]
}
],
aggregation: [
{
field: 'slot',
function: 'count'
}
],
limit: 100000
};
return [4 /*yield*/, index_1.default
.post(this.protocol.baseApiUrl + "/v2/data/tezos/" + this.protocol.baseApiNetwork + "/endorsing_rights", query, {
headers: this.protocol.headers
})
.then(function (result) { return result.data; })
.catch(function (error) { return []; })];
case 2:
result = _a.sent();
return [2 /*return*/, result.map(function (endorsingRight) {
return {
level: endorsingRight[blockLevelFieldName],
delegate: bakerAddress,
number_of_slots: Number(endorsingRight.count_slot)
};
})];
}
});
});
};
TezosRewardsCalculationDefault.prototype.fetchEndorsementOperationCountAndTotalFees = function (blockLevels) {
return __awaiter(this, void 0, void 0, function () {
var query, result, map, _i, _a, op;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
query = {
fields: ['block_level'],
predicates: [
{
field: 'block_level',
operation: 'in',
set: blockLevels,
inverse: false
}
],
aggregation: [
{
field: 'fee',
function: 'sum'
},
{
field: 'number_of_slots',
function: 'sum'
}
]
};
return [4 /*yield*/, index_1.default.post(this.protocol.baseApiUrl + "/v2/data/tezos/" + this.protocol.baseApiNetwork + "/operations", query, {
headers: this.protocol.headers
})];
case 1:
result = _b.sent();
map = new Map();
for (_i = 0, _a = result.data; _i < _a.length; _i++) {
op = _a[_i];
map.set(op.block_level, op);
}
return [2 /*return*/, map];
}
});
});
};
TezosRewardsCalculationDefault.prototype.fetchBlockPriorities = function (blockLevels) {
return __awaiter(this, void 0, void 0, function () {
var query, result;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
query = {
fields: ['priority', 'level'],
predicates: [
{
field: 'level',
operation: 'in',
set: blockLevels,
inverse: false
}
]
};
return [4 /*yield*/, index_1.default.post(this.protocol.baseApiUrl + "/v2/data/tezos/" + this.protocol.baseApiNetwork + "/blocks", query, {
headers: this.protocol.headers
})];
case 1:
result = _a.sent();
return [2 /*return*/, result.data];
}
});
});
};
TezosRewardsCalculationDefault.prototype.cycleToBlockLevel = function (cycle) {
var _a;
var blockPerCycle = (_a = this.tezosNodeConstants.blocks_per_cycle, (_a !== null && _a !== void 0 ? _a : TezosProtocol_1.TezosProtocol.BLOCKS_PER_CYCLE[this.protocol.options.network.extras.network]));
return (cycle * blockPerCycle) + 1;
};
return TezosRewardsCalculationDefault;
}());
exports.TezosRewardsCalculationDefault = TezosRewardsCalculationDefault;
//# sourceMappingURL=TezosRewardCalculationDefault.js.map