airgap-coin-lib
Version:
The airgap-coin-lib is a protocol agnostic library to prepare, sign and broadcast cryptocurrency transactions.
880 lines • 115 kB
JavaScript
"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 __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
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 taquito_local_forging_1 = require("../../dependencies/src/@taquito/local-forging-6.3.5-beta.0/packages/taquito-local-forging/src/taquito-local-forging");
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 IAirGapTransaction_1 = require("../../interfaces/IAirGapTransaction");
var ErrorWithData_1 = require("../../utils/ErrorWithData");
var ProtocolSymbols_1 = require("../../utils/ProtocolSymbols");
var subProtocols_1 = require("../../utils/subProtocols");
var NonExtendedProtocol_1 = require("../NonExtendedProtocol");
var TezosRewardCalculation005_1 = require("./rewardcalculation/TezosRewardCalculation005");
var TezosRewardCalculation006_1 = require("./rewardcalculation/TezosRewardCalculation006");
var TezosRewardCalculationDefault_1 = require("./rewardcalculation/TezosRewardCalculationDefault");
var TezosProtocolOptions_1 = require("./TezosProtocolOptions");
var TezosOperationType_1 = require("./types/TezosOperationType");
var TezosCryptoClient_1 = require("./TezosCryptoClient");
var assert_1 = require("../../utils/assert");
var MAX_OPERATIONS_PER_GROUP = 200;
var GAS_LIMIT_PLACEHOLDER = '1040000';
var STORAGE_LIMIT_PLACEHOLDER = '60000';
var MINIMAL_FEE = 100;
var MINIMAL_FEE_PER_GAS_UNIT = 0.1;
var MINIMAL_FEE_PER_BYTE = 1;
var TezosDelegatorAction;
(function (TezosDelegatorAction) {
TezosDelegatorAction["DELEGATE"] = "delegate";
TezosDelegatorAction["UNDELEGATE"] = "undelegate";
TezosDelegatorAction["CHANGE_BAKER"] = "change_baker";
})(TezosDelegatorAction = exports.TezosDelegatorAction || (exports.TezosDelegatorAction = {}));
// 8.25%
var SELF_BOND_REQUIREMENT = 0.0825;
var TezosNetwork;
(function (TezosNetwork) {
TezosNetwork["MAINNET"] = "mainnet";
TezosNetwork["DELPHINET"] = "delphinet";
})(TezosNetwork = exports.TezosNetwork || (exports.TezosNetwork = {}));
var TezosProtocol = /** @class */ (function (_super) {
__extends(TezosProtocol, _super);
function TezosProtocol(options) {
if (options === void 0) { options = new TezosProtocolOptions_1.TezosProtocolOptions(); }
var _this = _super.call(this) || this;
_this.options = options;
_this.symbol = 'XTZ';
_this.name = 'Tezos';
_this.marketSymbol = 'xtz';
_this.feeSymbol = 'xtz';
_this.decimals = 6;
_this.feeDecimals = 6; // micro tez is the smallest, 1000000 microtez is 1 tez
_this.identifier = ProtocolSymbols_1.MainProtocolSymbols.XTZ;
// tezbox default
_this.feeDefaults = {
low: '0.001420',
medium: '0.001520',
high: '0.003000'
};
_this.units = [
{
unitSymbol: 'XTZ',
factor: '1'
}
];
_this.supportsHD = false;
_this.standardDerivationPath = "m/44h/1729h/0h/0h";
_this.addressIsCaseSensitive = true;
_this.addressValidationPattern = '^(tz1|tz2|tz3|KT1)[1-9A-Za-z]{33}$';
_this.addressPlaceholder = 'tz1...';
// https://gitlab.com/tezos/tezos/-/blob/master/docs/whitedoc/proof_of_stake.rst
// cycle has 4096 blocks, which are at least one minute apart
_this.minCycleDuration = 4096 * 60 * 1000; // ms
_this.transactionFee = new bignumber_1.default('1400');
_this.originationSize = new bignumber_1.default('257');
_this.storageCostPerByte = new bignumber_1.default('1000');
_this.revealFee = new bignumber_1.default('1300');
_this.activationBurn = _this.originationSize.times(_this.storageCostPerByte);
_this.originationBurn = _this.originationSize.times(_this.storageCostPerByte); // https://tezos.stackexchange.com/a/787
// Tezos - We need to wrap these in Buffer due to non-compatible browser polyfills
_this.tezosPrefixes = {
tz1: Buffer.from(new Uint8Array([6, 161, 159])),
tz2: Buffer.from(new Uint8Array([6, 161, 161])),
tz3: Buffer.from(new Uint8Array([6, 161, 164])),
kt: Buffer.from(new Uint8Array([2, 90, 121])),
edpk: Buffer.from(new Uint8Array([13, 15, 37, 217])),
edsk: Buffer.from(new Uint8Array([43, 246, 78, 7])),
edsig: Buffer.from(new Uint8Array([9, 245, 205, 134, 18])),
branch: Buffer.from(new Uint8Array([1, 52]))
};
_this.headers = { 'Content-Type': 'application/json', apiKey: 'airgap00391' };
_this.rewardCalculations = {
alpha: new TezosRewardCalculationDefault_1.TezosRewardsCalculationDefault(_this),
babylon: new TezosRewardCalculation005_1.TezosRewardsCalculation005(_this),
cartha: new TezosRewardCalculation006_1.TezosRewardsCalculation006(_this)
};
_this.headers.apiKey = options.network.extras.conseilApiKey;
return _this;
}
Object.defineProperty(TezosProtocol.prototype, "subProtocols", {
get: function () {
return subProtocols_1.getSubProtocolsByIdentifier(this.identifier, this.options.network);
},
enumerable: true,
configurable: true
});
Object.defineProperty(TezosProtocol.prototype, "jsonRPCAPI", {
// TODO: Should we remove these getters and replace the calls to `this.options.network...`?
get: function () {
return this.options.network.rpcUrl;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TezosProtocol.prototype, "baseApiUrl", {
get: function () {
return this.options.network.extras.conseilUrl;
},
enumerable: true,
configurable: true
});
Object.defineProperty(TezosProtocol.prototype, "baseApiNetwork", {
get: function () {
return this.options.network.extras.conseilNetwork;
},
enumerable: true,
configurable: true
});
TezosProtocol.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)];
});
});
};
TezosProtocol.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)];
});
});
};
TezosProtocol.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)];
});
});
};
TezosProtocol.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
*/
TezosProtocol.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) // TODO: Look into typings
.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
*/
TezosProtocol.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) // TODO: Look into typings
.secretKey;
return [2 /*return*/, Buffer.from(secretKey)];
});
});
};
TezosProtocol.prototype.getAddressFromPublicKey = function (publicKey) {
return __awaiter(this, void 0, void 0, function () {
var payload, address;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, sodium.ready];
case 1:
_a.sent();
payload = sodium.crypto_generichash(20, Buffer.from(publicKey, 'hex'));
address = bs58check.encode(Buffer.concat([this.tezosPrefixes.tz1, Buffer.from(payload)]));
return [2 /*return*/, address];
}
});
});
};
TezosProtocol.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]];
}
});
});
};
TezosProtocol.prototype.getTransactionsFromPublicKey = function (publicKey, limit, cursor) {
return __awaiter(this, void 0, void 0, function () {
var addresses;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.getAddressesFromPublicKey(publicKey)];
case 1:
addresses = _a.sent();
return [2 /*return*/, this.getTransactionsFromAddresses(addresses, limit, cursor)];
}
});
});
};
TezosProtocol.prototype.getTransactionsFromAddresses = function (addresses, limit, cursor) {
return __awaiter(this, void 0, void 0, function () {
var allTransactions, lastEntryBlockLevel;
var _this = this;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, Promise.all(addresses.map(function (address) {
var getRequestBody = function () {
var body = {
fields: ['status', 'amount', 'fee', 'source', 'destination', 'operation_group_hash', 'timestamp', 'block_level'],
predicates: [
{
field: 'source',
operation: 'eq',
set: [address],
inverse: false,
group: 'a'
},
{
field: 'destination',
operation: 'eq',
set: [address],
inverse: false,
group: 'b'
},
{
field: 'kind',
operation: 'eq',
set: ['transaction'],
inverse: false,
group: 'a'
},
{
field: 'kind',
operation: 'eq',
set: ['transaction'],
inverse: false,
group: 'b'
}
],
orderBy: [
{
field: 'block_level',
direction: 'desc'
}
],
limit: limit
};
if (cursor && cursor.lastBlockLevel) {
body.predicates.push({
field: 'block_level',
operation: 'lt',
set: [cursor.lastBlockLevel.toString()],
inverse: false,
group: 'a'
});
body.predicates.push({
field: 'block_level',
operation: 'lt',
set: [cursor.lastBlockLevel.toString()],
inverse: false,
group: 'b'
});
}
return body;
};
// AirGapTransactionStatus.APPLIED : AirGapTransactionStatus.FAILED
return new Promise(function (resolve, reject) { 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
.post(this.options.network.extras.conseilUrl + "/v2/data/tezos/" + this.options.network.extras.conseilNetwork + "/operations", getRequestBody(), {
headers: this.headers
})
.catch(function () {
return { data: [] };
})];
case 1:
result = _a.sent();
resolve(result.data);
return [2 /*return*/];
}
});
}); });
}))];
case 1:
allTransactions = _a.sent();
allTransactions = allTransactions
.reduce(function (current, next) { return current.concat(next); })
.map(function (transaction) {
return {
amount: new bignumber_1.default(transaction.amount),
fee: new bignumber_1.default(transaction.fee),
from: [transaction.source],
isInbound: addresses.indexOf(transaction.destination) !== -1,
protocolIdentifier: _this.identifier,
network: _this.options.network,
to: [transaction.destination],
hash: transaction.operation_group_hash,
timestamp: transaction.timestamp / 1000,
blockHeight: transaction.block_level,
status: transaction.status === 'applied' ? IAirGapTransaction_1.AirGapTransactionStatus.APPLIED : IAirGapTransaction_1.AirGapTransactionStatus.FAILED
};
});
lastEntryBlockLevel = allTransactions.length > 0 ? allTransactions[allTransactions.length - 1].blockHeight : 0;
return [2 /*return*/, { transactions: allTransactions, cursor: { lastBlockLevel: lastEntryBlockLevel } }];
}
});
});
};
TezosProtocol.prototype.signWithPrivateKey = function (privateKey, transaction) {
return __awaiter(this, void 0, void 0, function () {
var watermark, watermarkedForgedOperationBytesHex, watermarkedForgedOperationBytes, hashedWatermarkedOpBytes, opSignature, signedOpBytes;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, sodium.ready];
case 1:
_a.sent();
watermark = '03';
watermarkedForgedOperationBytesHex = watermark + transaction.binaryTransaction;
watermarkedForgedOperationBytes = Buffer.from(watermarkedForgedOperationBytesHex, 'hex');
hashedWatermarkedOpBytes = sodium.crypto_generichash(32, watermarkedForgedOperationBytes);
opSignature = sodium.crypto_sign_detached(hashedWatermarkedOpBytes, privateKey);
signedOpBytes = Buffer.concat([Buffer.from(transaction.binaryTransaction, 'hex'), Buffer.from(opSignature)]);
return [2 /*return*/, signedOpBytes.toString('hex')];
}
});
});
};
TezosProtocol.prototype.getTransactionDetails = function (unsignedTx) {
return __awaiter(this, void 0, void 0, function () {
var binaryTransaction, wrappedOperations;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
binaryTransaction = unsignedTx.transaction.binaryTransaction;
return [4 /*yield*/, this.unforgeUnsignedTezosWrappedOperation(binaryTransaction)];
case 1:
wrappedOperations = _a.sent();
return [2 /*return*/, this.getAirGapTxFromWrappedOperations(wrappedOperations)];
}
});
});
};
TezosProtocol.prototype.getTransactionDetailsFromSigned = function (signedTx) {
return __awaiter(this, void 0, void 0, function () {
var binaryTransaction, wrappedOperations;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
binaryTransaction = signedTx.transaction;
return [4 /*yield*/, this.unforgeSignedTezosWrappedOperation(binaryTransaction)];
case 1:
wrappedOperations = _a.sent();
return [2 /*return*/, this.getAirGapTxFromWrappedOperations(wrappedOperations)];
}
});
});
};
TezosProtocol.prototype.getAirGapTxFromWrappedOperations = function (wrappedOperations) {
return __awaiter(this, void 0, void 0, function () {
var assertNever;
var _this = this;
return __generator(this, function (_a) {
assertNever = function (x) { return undefined; };
return [2 /*return*/, Promise.all(wrappedOperations.contents.map(function (tezosOperation) { return __awaiter(_this, void 0, void 0, function () {
var operation, partialTxs, _a, tezosSpendOperation, tezosOriginationOperation, delegate, delegate;
var _this = this;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
partialTxs = [];
_a = tezosOperation.kind;
switch (_a) {
case TezosOperationType_1.TezosOperationType.REVEAL: return [3 /*break*/, 1];
case TezosOperationType_1.TezosOperationType.TRANSACTION: return [3 /*break*/, 2];
case TezosOperationType_1.TezosOperationType.ORIGINATION: return [3 /*break*/, 4];
case TezosOperationType_1.TezosOperationType.DELEGATION: return [3 /*break*/, 5];
case TezosOperationType_1.TezosOperationType.ENDORSEMENT: return [3 /*break*/, 6];
case TezosOperationType_1.TezosOperationType.SEED_NONCE_REVELATION: return [3 /*break*/, 6];
case TezosOperationType_1.TezosOperationType.DOUBLE_ENDORSEMENT_EVIDENCE: return [3 /*break*/, 6];
case TezosOperationType_1.TezosOperationType.DOUBLE_BAKING_EVIDENCE: return [3 /*break*/, 6];
case TezosOperationType_1.TezosOperationType.ACTIVATE_ACCOUNT: return [3 /*break*/, 6];
case TezosOperationType_1.TezosOperationType.PROPOSALS: return [3 /*break*/, 6];
case TezosOperationType_1.TezosOperationType.BALLOT: return [3 /*break*/, 6];
}
return [3 /*break*/, 7];
case 1:
operation = tezosOperation;
partialTxs = [
{
from: [operation.source],
to: ['Reveal']
}
];
return [3 /*break*/, 8];
case 2:
tezosSpendOperation = tezosOperation;
operation = tezosSpendOperation;
return [4 /*yield*/, this.getTransactionOperationDetails(tezosSpendOperation)];
case 3:
partialTxs = _b.sent();
return [3 /*break*/, 8];
case 4:
{
tezosOriginationOperation = tezosOperation;
operation = tezosOriginationOperation;
delegate = tezosOriginationOperation.delegate;
partialTxs = [
{
from: [operation.source],
amount: new bignumber_1.default(tezosOriginationOperation.balance).toFixed(),
to: [delegate ? "Delegate: " + delegate : 'Origination']
}
];
}
return [3 /*break*/, 8];
case 5:
{
operation = tezosOperation;
delegate = operation.delegate;
partialTxs = [
{
from: [operation.source],
to: [delegate ? delegate : 'Undelegate']
}
];
}
return [3 /*break*/, 8];
case 6: throw new Error('operation not supported: ' + tezosOperation.kind);
case 7:
assertNever(tezosOperation.kind); // Exhaustive switch
throw new Error('no operation to unforge found');
case 8: return [2 /*return*/, partialTxs.map(function (partialTx) {
return __assign({ amount: '0', fee: operation !== undefined ? new bignumber_1.default(operation.fee).toString(10) : '0', from: [], isInbound: false, protocolIdentifier: _this.identifier, network: _this.options.network, to: [], transactionDetails: tezosOperation }, partialTx);
})];
}
});
}); })).then(function (airGapTxs) {
return airGapTxs.reduce(function (flatten, next) { return flatten.concat(next); }, []);
})];
});
});
};
TezosProtocol.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 + "/chains/main/blocks/head/context/contracts/" + address + "/balance")];
case 3:
data = (_a.sent()).data;
balance = balance.plus(new bignumber_1.default(data));
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 && 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)];
}
});
});
};
TezosProtocol.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])];
}
});
});
};
TezosProtocol.prototype.getBalanceOfPublicKeyForSubProtocols = function (publicKey, subProtocols) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, Promise.all(subProtocols.map(function (subProtocol) { return subProtocol.getBalanceOfPublicKey(publicKey).catch(function () { return '0'; }); }))];
case 1: return [2 /*return*/, _a.sent()];
}
});
});
};
TezosProtocol.prototype.getAvailableBalanceOfAddresses = function (addresses) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, this.getBalanceOfAddresses(addresses)];
});
});
};
TezosProtocol.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).minus(1) // Tezos accounts can never be empty. We must leave at least 1 mutez behind.
;
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, [balanceWrapper.toFixed()])];
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()];
}
});
});
};
TezosProtocol.prototype.estimateFeeDefaultsFromPublicKey = function (publicKey, recipients, values, data) {
return __awaiter(this, void 0, void 0, function () {
var operations, i, value, recipient, transaction;
return __generator(this, function (_a) {
if (recipients.length !== values.length) {
throw new Error('length of recipients and values does not match!');
}
operations = [];
for (i = 0; i < values.length; ++i) {
value = values[i];
recipient = recipients[i];
transaction = {
kind: TezosOperationType_1.TezosOperationType.TRANSACTION,
amount: value,
destination: recipient,
fee: '0'
};
operations.push(transaction);
}
if (recipients.length === 0) {
return [2 /*return*/, this.feeDefaults];
}
return [2 /*return*/, this.estimateFeeDefaultsForOperations(publicKey, operations)];
});
});
};
TezosProtocol.prototype.estimateFeeDefaultsForOperations = function (publicKey, operations) {
return __awaiter(this, void 0, void 0, function () {
var estimated, hasReveal, estimatedFee, feeStepFactor, lowFee, mediumFee, highFee;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, this.prepareOperations(publicKey, operations)];
case 1:
estimated = _a.sent();
hasReveal = estimated.contents.some(function (op) { return op.kind === TezosOperationType_1.TezosOperationType.REVEAL; });
estimatedFee = estimated.contents
.reduce(function (current, next) {
if (next.fee !== undefined) {
return current.plus(new bignumber_1.default(next['fee']));
}
return current;
}, new bignumber_1.default(0))
.minus(hasReveal ? this.revealFee : 0)
.div(hasReveal ? estimated.contents.length - 1 : estimated.contents.length);
feeStepFactor = new bignumber_1.default(0.1);
lowFee = estimatedFee.minus(estimatedFee.times(feeStepFactor).integerValue(bignumber_1.default.ROUND_FLOOR));
mediumFee = estimatedFee;
highFee = mediumFee.plus(mediumFee.times(feeStepFactor).integerValue(bignumber_1.default.ROUND_FLOOR));
return [2 /*return*/, {
low: lowFee.shiftedBy(-this.feeDecimals).toFixed(),
medium: mediumFee.shiftedBy(-this.feeDecimals).toFixed(),
high: highFee.shiftedBy(-this.feeDecimals).toFixed()
}];
}
});
});
};
TezosProtocol.prototype.prepareTransactionFromPublicKey = function (publicKey, recipients, values, fee, data) {
return __awaiter(this, void 0, void 0, function () {
var transactions;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (recipients.length !== values.length) {
throw new Error('length of recipients and values does not match!');
}
if (recipients.length > MAX_OPERATIONS_PER_GROUP) {
throw new Error("this transaction exceeds the maximum allowed number of transactions per operation (" + MAX_OPERATIONS_PER_GROUP + "). Please use the \"prepareTransactionsFromPublicKey\" method instead.");
}
return [4 /*yield*/, this.prepareTransactionsFromPublicKey(publicKey, recipients, values, fee, data)];
case 1:
transactions = _a.sent();
if (transactions.length === 1) {
return [2 /*return*/, transactions[0]];
}
else {
throw new Error('Transaction could not be prepared. More or less than 1 operations have been generated.');
}
return [2 /*return*/];
}
});
});
};
TezosProtocol.prototype.prepareTransactionsFromPublicKey = function (publicKey, recipients, values, fee, data, operationsPerGroup) {
if (operationsPerGroup === void 0) { operationsPerGroup = MAX_OPERATIONS_PER_GROUP; }
return __awaiter(this, void 0, void 0, function () {
var wrappedValues, wrappedFee, counter, branch, operations, addressIndex, addresses, address, results, accountManager, _a, _b, error_2, balance, _c, wrappedOperations, allOperations, numberOfGroups, i, start, end, operationsGroup, wrappedOperationWithEstimatedGas, _d, _e;
return __generator(this, function (_f) {
switch (_f.label) {
case 0:
wrappedValues = values.map(function (value) { return new bignumber_1.default(value); });
wrappedFee = new bignumber_1.default(fee);
if (recipients.length !== wrappedValues.length) {
throw new Error('length of recipients and values does not match!');
}
counter = new bignumber_1.default(1);
operations = [];
addressIndex = data && data.addressIndex ? data.addressIndex : 0;
return [4 /*yield*/, this.getAddressesFromPublicKey(publicKey)];
case 1:
addresses = _f.sent();
if (!addresses[addressIndex]) {
throw new Error('no kt-address with this index exists');
}
address = addresses[addressIndex];
_f.label = 2;
case 2:
_f.trys.push([2, 6, , 7]);
return [4 /*yield*/, Promise.all([
index_1.default.get(this.options.network.rpcUrl + "/chains/main/blocks/head/context/contracts/" + address + "/counter"),
index_1.default.get(this.options.network.rpcUrl + "/chains/main/blocks/head/hash"),
index_1.default.get(this.options.network.rpcUrl + "/chains/main/blocks/head/context/contracts/" + address + "/manager_key")
])];
case 3:
results = _f.sent();
counter = new bignumber_1.default(results[0].data).plus(1);
branch = results[1].data;
accountManager = results[2].data;
if (!!accountManager) return [3 /*break*/, 5];
_b = (_a = operations).push;
return [4 /*yield*/, this.createRevealOperation(counter, publicKey, address)];
case 4:
_b.apply(_a, [_f.sent()]);
counter = counter.plus(1);
_f.label = 5;
case 5: return [3 /*break*/, 7];
case 6:
error_2 = _f.sent();
throw error_2;
case 7:
_c = bignumber_1.default.bind;
return [4 /*yield*/, this.getBalanceOfPublicKey(publicKey)];
case 8:
balance = new (_c.apply(bignumber_1.default, [void 0, _f.sent()]))();
wrappedOperations = [];
return [4 /*yield*/, this.createTransactionOperations(operations, recipients, wrappedValues, wrappedFee, address, counter, balance)];
case 9:
allOperations = _f.sent();
allOperations = operations.concat(allOperations); // if we have a reveal in operations, we need to make sure it is present in the allOperations array
numberOfGroups = Math.ceil(allOperations.length / operationsPerGroup);
i = 0;
_f.label = 10;
case 10:
if (!(i < numberOfGroups)) return [3 /*break*/, 14];
start = i * operationsPerGroup;
end = start + operationsPerGroup;
operationsGroup = allOperations.slice(start, end);
return [4 /*yield*/, this.estimateAndReplaceLimitsAndFee({
branch: branch,
contents: operationsGroup
}, false)];
case 11:
wrappedOperationWithEstimatedGas = _f.sent();
_e = (_d = wrappedOperations).push;
return [4 /*yield*/, this.forgeAndWrapOperations(wrappedOperationWithEstimatedGas)];
case 12:
_e.apply(_d, [_f.sent()]);
_f.label = 13;
case 13:
i++;
return [3 /*break*/, 10];
case 14: return [2 /*return*/, wrappedOperations];
}
});
});
};
TezosProtocol.prototype.getTransactionOperationDetails = function (transactionOperation) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2 /*return*/, [
{
from: [transactionOperation.source],
amount: new bignumber_1.default(transactionOperation.amount).toFixed(),
to: [transactionOperation.destination] // contract destination but should be the address of actual receiver
}
]];
});
});
};
TezosProtocol.prototype.createTransactionOperations = function (previousOperations, recipients, wrappedValues, wrappedFee, address, counter, balance) {
return __awaiter(this, void 0, void 0, function () {
var amountUsedByPreviousOperations, operations, i, receivingBalance, _a, spendOperation;
return __generator(this, function (_b) {
switch (_b.label) {
case 0:
amountUsedByPreviousOperations = this.getAmountUsedByPreviousOperations(previousOperations);
operations = [];
if (!amountUsedByPreviousOperations.isZero()) {
if (balance.isLessThan(wrappedValues[0].plus(wrappedFee).plus(amountUsedByPreviousOperations))) {
// if not, make room for the init fee
wrappedValues[0] = wrappedValues[0].minus(amountUsedByPreviousOperations); // deduct fee from balance
}
}
i = 0;
_b.label = 1;
case 1:
if (!(i < recipients.length)) return [3 /*break*/, 4];
_a = bignumber_1.default.bind;
return [4 /*yield*/, this.getBalanceOfAddresses([recipients[i]])];
case 2:
receivingBalance = new (_a.apply(bignumber_1.default, [void 0, _b.sent()]))();
// if our receiver has 0 balance, the account is not activated yet.
if (receivingBalance.isZero() && recipients[i].toLowerCase().startsWith('tz')) {
// We have to supply an additional 0.257 XTZ fee for storage_limit costs, which gets automatically deducted from the sender so we just have to make sure enough balance is around
if (balance.isLessThan(this.activationBurn.plus(wrappedFee))) {
// If we don't have enough funds to pay the activation + fee, we throw an error
throw new Error('Insufficient balance to pay activation burn!');
}
else if (balance.isLessThan(wrappedValues[i].plus(wrappedFee).plus(this.activationBurn))) {
// Check whether the sender has enough to cover the amount to send + fee + activation
// If not, we deduct it from amount sent to make room for the activation burn
wrappedValues[i] = wrappedValues[i].minus(this.activationBurn); // deduct fee from balance
}
}
if (balance.isEqualTo(wrappedValues[i].plus(wrappedFee))) {
// Tezos accounts can never be empty. If user tries to send everything, we must leave 1 mutez behind.
wrappedValues[i] = wrappedValues[i].minus(1);
}
else if (balance.isLessThan(wrappedValues[i].plus(wrappedFee))) {
throw new Error('not enough balance');
}
spendOperation = {
kind: TezosOperationType_1.TezosOperationType.TRANSACTION,
fee: wrappedFee.toFixed(),
gas_limit: GAS_LIMIT_PLACEHOLDER,
storage_limit: STORAGE_LIMIT_PLACEHOLDER,
amount: wrappedValues[i].toFixed(),
counter: counter.plus(i).toFixed(),
destination: recipients[i],
source: address
};
operations.push(spendOperation);
_b.label = 3;
case 3:
i++;
return [3 /*break*/, 1];
case 4: return [2 /*return*/, operations];
}
});
});
};
TezosProtocol.prototype.forgeAndWrapOperations = function (tezosWrappedOperation) {
return __awaiter(this, void 0, void 0, function () {
var binaryTx, error_3;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_a.trys.push([0,