You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

autofill.js 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. "use strict";
  2. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  3. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  4. return new (P || (P = Promise))(function (resolve, reject) {
  5. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  6. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  7. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  8. step((generator = generator.apply(thisArg, _arguments || [])).next());
  9. });
  10. };
  11. var __importDefault = (this && this.__importDefault) || function (mod) {
  12. return (mod && mod.__esModule) ? mod : { "default": mod };
  13. };
  14. Object.defineProperty(exports, "__esModule", { value: true });
  15. const bignumber_js_1 = __importDefault(require("bignumber.js"));
  16. const ripple_address_codec_1 = require("ripple-address-codec");
  17. const errors_1 = require("../errors");
  18. const flags_1 = require("../models/utils/flags");
  19. const utils_1 = require("../utils");
  20. const getFeeXrp_1 = __importDefault(require("./getFeeXrp"));
  21. const LEDGER_OFFSET = 20;
  22. function autofill(transaction, signersCount) {
  23. return __awaiter(this, void 0, void 0, function* () {
  24. const tx = Object.assign({}, transaction);
  25. setValidAddresses(tx);
  26. (0, flags_1.setTransactionFlagsToNumber)(tx);
  27. const promises = [];
  28. if (tx.Sequence == null) {
  29. promises.push(setNextValidSequenceNumber(this, tx));
  30. }
  31. if (tx.Fee == null) {
  32. promises.push(calculateFeePerTransactionType(this, tx, signersCount));
  33. }
  34. if (tx.LastLedgerSequence == null) {
  35. promises.push(setLatestValidatedLedgerSequence(this, tx));
  36. }
  37. if (tx.TransactionType === 'AccountDelete') {
  38. promises.push(checkAccountDeleteBlockers(this, tx));
  39. }
  40. return Promise.all(promises).then(() => tx);
  41. });
  42. }
  43. function setValidAddresses(tx) {
  44. validateAccountAddress(tx, 'Account', 'SourceTag');
  45. if (tx['Destination'] != null) {
  46. validateAccountAddress(tx, 'Destination', 'DestinationTag');
  47. }
  48. convertToClassicAddress(tx, 'Authorize');
  49. convertToClassicAddress(tx, 'Unauthorize');
  50. convertToClassicAddress(tx, 'Owner');
  51. convertToClassicAddress(tx, 'RegularKey');
  52. }
  53. function validateAccountAddress(tx, accountField, tagField) {
  54. const { classicAccount, tag } = getClassicAccountAndTag(tx[accountField]);
  55. tx[accountField] = classicAccount;
  56. if (tag != null && tag !== false) {
  57. if (tx[tagField] && tx[tagField] !== tag) {
  58. throw new errors_1.ValidationError(`The ${tagField}, if present, must match the tag of the ${accountField} X-address`);
  59. }
  60. tx[tagField] = tag;
  61. }
  62. }
  63. function getClassicAccountAndTag(Account, expectedTag) {
  64. if ((0, ripple_address_codec_1.isValidXAddress)(Account)) {
  65. const classic = (0, ripple_address_codec_1.xAddressToClassicAddress)(Account);
  66. if (expectedTag != null && classic.tag !== expectedTag) {
  67. throw new errors_1.ValidationError('address includes a tag that does not match the tag specified in the transaction');
  68. }
  69. return {
  70. classicAccount: classic.classicAddress,
  71. tag: classic.tag,
  72. };
  73. }
  74. return {
  75. classicAccount: Account,
  76. tag: expectedTag,
  77. };
  78. }
  79. function convertToClassicAddress(tx, fieldName) {
  80. const account = tx[fieldName];
  81. if (typeof account === 'string') {
  82. const { classicAccount } = getClassicAccountAndTag(account);
  83. tx[fieldName] = classicAccount;
  84. }
  85. }
  86. function setNextValidSequenceNumber(client, tx) {
  87. return __awaiter(this, void 0, void 0, function* () {
  88. const request = {
  89. command: 'account_info',
  90. account: tx.Account,
  91. ledger_index: 'current',
  92. };
  93. const data = yield client.request(request);
  94. tx.Sequence = data.result.account_data.Sequence;
  95. });
  96. }
  97. function fetchAccountDeleteFee(client) {
  98. var _a;
  99. return __awaiter(this, void 0, void 0, function* () {
  100. const response = yield client.request({ command: 'server_state' });
  101. const fee = (_a = response.result.state.validated_ledger) === null || _a === void 0 ? void 0 : _a.reserve_inc;
  102. if (fee == null) {
  103. return Promise.reject(new Error('Could not fetch Owner Reserve.'));
  104. }
  105. return new bignumber_js_1.default(fee);
  106. });
  107. }
  108. function calculateFeePerTransactionType(client, tx, signersCount = 0) {
  109. return __awaiter(this, void 0, void 0, function* () {
  110. const netFeeXRP = yield (0, getFeeXrp_1.default)(client);
  111. const netFeeDrops = (0, utils_1.xrpToDrops)(netFeeXRP);
  112. let baseFee = new bignumber_js_1.default(netFeeDrops);
  113. if (tx.TransactionType === 'EscrowFinish' && tx.Fulfillment != null) {
  114. const fulfillmentBytesSize = Math.ceil(tx.Fulfillment.length / 2);
  115. const product = new bignumber_js_1.default(scaleValue(netFeeDrops, 33 + fulfillmentBytesSize / 16));
  116. baseFee = product.dp(0, bignumber_js_1.default.ROUND_CEIL);
  117. }
  118. if (tx.TransactionType === 'AccountDelete') {
  119. baseFee = yield fetchAccountDeleteFee(client);
  120. }
  121. if (signersCount > 0) {
  122. baseFee = bignumber_js_1.default.sum(baseFee, scaleValue(netFeeDrops, 1 + signersCount));
  123. }
  124. const maxFeeDrops = (0, utils_1.xrpToDrops)(client.maxFeeXRP);
  125. const totalFee = tx.TransactionType === 'AccountDelete'
  126. ? baseFee
  127. : bignumber_js_1.default.min(baseFee, maxFeeDrops);
  128. tx.Fee = totalFee.dp(0, bignumber_js_1.default.ROUND_CEIL).toString(10);
  129. });
  130. }
  131. function scaleValue(value, multiplier) {
  132. return new bignumber_js_1.default(value).times(multiplier).toString();
  133. }
  134. function setLatestValidatedLedgerSequence(client, tx) {
  135. return __awaiter(this, void 0, void 0, function* () {
  136. const ledgerSequence = yield client.getLedgerIndex();
  137. tx.LastLedgerSequence = ledgerSequence + LEDGER_OFFSET;
  138. });
  139. }
  140. function checkAccountDeleteBlockers(client, tx) {
  141. return __awaiter(this, void 0, void 0, function* () {
  142. const request = {
  143. command: 'account_objects',
  144. account: tx.Account,
  145. ledger_index: 'validated',
  146. deletion_blockers_only: true,
  147. };
  148. const response = yield client.request(request);
  149. return new Promise((resolve, reject) => {
  150. if (response.result.account_objects.length > 0) {
  151. reject(new errors_1.XrplError(`Account ${tx.Account} cannot be deleted; there are Escrows, PayChannels, RippleStates, or Checks associated with the account.`, response.result.account_objects));
  152. }
  153. resolve();
  154. });
  155. });
  156. }
  157. exports.default = autofill;
  158. //# sourceMappingURL=autofill.js.map