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.

utils.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. "use strict";
  2. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
  3. if (k2 === undefined) k2 = k;
  4. Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
  5. }) : (function(o, m, k, k2) {
  6. if (k2 === undefined) k2 = k;
  7. o[k2] = m[k];
  8. }));
  9. var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
  10. Object.defineProperty(o, "default", { enumerable: true, value: v });
  11. }) : function(o, v) {
  12. o["default"] = v;
  13. });
  14. var __importStar = (this && this.__importStar) || function (mod) {
  15. if (mod && mod.__esModule) return mod;
  16. var result = {};
  17. if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
  18. __setModuleDefault(result, mod);
  19. return result;
  20. };
  21. var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  22. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  23. return new (P || (P = Promise))(function (resolve, reject) {
  24. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  25. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  26. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  27. step((generator = generator.apply(thisArg, _arguments || [])).next());
  28. });
  29. };
  30. var __importDefault = (this && this.__importDefault) || function (mod) {
  31. return (mod && mod.__esModule) ? mod : { "default": mod };
  32. };
  33. Object.defineProperty(exports, "__esModule", { value: true });
  34. exports.getClassicAccountAndTag = exports.setCanonicalFlag = exports.common = exports.prepareTransaction = exports.convertMemo = exports.convertStringToHex = void 0;
  35. const bignumber_js_1 = __importDefault(require("bignumber.js"));
  36. const common = __importStar(require("../common"));
  37. exports.common = common;
  38. const common_1 = require("../common");
  39. const errors_1 = require("../common/errors");
  40. const ripple_address_codec_1 = require("ripple-address-codec");
  41. const txFlags = common.txFlags;
  42. const TRANSACTION_TYPES_WITH_DESTINATION_TAG_FIELD = [
  43. 'Payment',
  44. 'CheckCreate',
  45. 'EscrowCreate',
  46. 'PaymentChannelCreate'
  47. ];
  48. function formatPrepareResponse(txJSON) {
  49. const instructions = {
  50. fee: common.dropsToXrp(txJSON.Fee),
  51. maxLedgerVersion: txJSON.LastLedgerSequence == null ? null : txJSON.LastLedgerSequence
  52. };
  53. if (txJSON.TicketSequence != null) {
  54. instructions['ticketSequence'] = txJSON.TicketSequence;
  55. }
  56. else {
  57. instructions['sequence'] = txJSON.Sequence;
  58. }
  59. return {
  60. txJSON: JSON.stringify(txJSON),
  61. instructions
  62. };
  63. }
  64. function setCanonicalFlag(txJSON) {
  65. txJSON.Flags |= txFlags.Universal.FullyCanonicalSig;
  66. txJSON.Flags = txJSON.Flags >>> 0;
  67. }
  68. exports.setCanonicalFlag = setCanonicalFlag;
  69. function scaleValue(value, multiplier, extra = 0) {
  70. return new bignumber_js_1.default(value).times(multiplier).plus(extra).toString();
  71. }
  72. function getClassicAccountAndTag(Account, expectedTag) {
  73. if (ripple_address_codec_1.isValidXAddress(Account)) {
  74. const classic = ripple_address_codec_1.xAddressToClassicAddress(Account);
  75. if (expectedTag != null && classic.tag !== expectedTag) {
  76. throw new errors_1.ValidationError('address includes a tag that does not match the tag specified in the transaction');
  77. }
  78. return {
  79. classicAccount: classic.classicAddress,
  80. tag: classic.tag
  81. };
  82. }
  83. else {
  84. return {
  85. classicAccount: Account,
  86. tag: expectedTag
  87. };
  88. }
  89. }
  90. exports.getClassicAccountAndTag = getClassicAccountAndTag;
  91. function prepareTransaction(txJSON, api, instructions) {
  92. common.validate.instructions(instructions);
  93. common.validate.tx_json(txJSON);
  94. if (instructions.sequence != null && instructions.sequence === 0) {
  95. return Promise.reject(new errors_1.ValidationError('`sequence` cannot be 0'));
  96. }
  97. const disallowedFieldsInTxJSON = [
  98. 'maxLedgerVersion',
  99. 'maxLedgerVersionOffset',
  100. 'fee',
  101. 'sequence',
  102. 'ticketSequence'
  103. ];
  104. const badFields = disallowedFieldsInTxJSON.filter((field) => txJSON[field]);
  105. if (badFields.length) {
  106. return Promise.reject(new errors_1.ValidationError('txJSON additionalProperty "' +
  107. badFields[0] +
  108. '" exists in instance when not allowed'));
  109. }
  110. const newTxJSON = Object.assign({}, txJSON);
  111. if (txJSON['SignerQuorum'] === 0) {
  112. delete newTxJSON.SignerEntries;
  113. }
  114. const { classicAccount, tag: sourceTag } = getClassicAccountAndTag(txJSON.Account);
  115. newTxJSON.Account = classicAccount;
  116. if (sourceTag != null) {
  117. if (txJSON.SourceTag && txJSON.SourceTag !== sourceTag) {
  118. return Promise.reject(new errors_1.ValidationError('The `SourceTag`, if present, must match the tag of the `Account` X-address'));
  119. }
  120. if (sourceTag) {
  121. newTxJSON.SourceTag = sourceTag;
  122. }
  123. }
  124. if (typeof txJSON.Destination === 'string') {
  125. const { classicAccount: destinationAccount, tag: destinationTag } = getClassicAccountAndTag(txJSON.Destination);
  126. newTxJSON.Destination = destinationAccount;
  127. if (destinationTag != null) {
  128. if (TRANSACTION_TYPES_WITH_DESTINATION_TAG_FIELD.includes(txJSON.TransactionType)) {
  129. if (txJSON.DestinationTag && txJSON.DestinationTag !== destinationTag) {
  130. return Promise.reject(new errors_1.ValidationError('The Payment `DestinationTag`, if present, must match the tag of the `Destination` X-address'));
  131. }
  132. if (destinationTag) {
  133. newTxJSON.DestinationTag = destinationTag;
  134. }
  135. }
  136. }
  137. }
  138. function convertToClassicAccountIfPresent(fieldName) {
  139. const account = txJSON[fieldName];
  140. if (typeof account === 'string') {
  141. const { classicAccount: ca } = getClassicAccountAndTag(account);
  142. newTxJSON[fieldName] = ca;
  143. }
  144. }
  145. function convertIssuedCurrencyToAccountIfPresent(fieldName) {
  146. const amount = txJSON[fieldName];
  147. if (typeof amount === 'number'
  148. || amount instanceof Array
  149. || amount == null)
  150. return;
  151. newTxJSON[fieldName] = common_1.toRippledAmount(amount);
  152. }
  153. convertToClassicAccountIfPresent('Authorize');
  154. convertToClassicAccountIfPresent('Unauthorize');
  155. convertToClassicAccountIfPresent('Owner');
  156. convertToClassicAccountIfPresent('RegularKey');
  157. convertIssuedCurrencyToAccountIfPresent('Amount');
  158. convertIssuedCurrencyToAccountIfPresent('SendMax');
  159. convertIssuedCurrencyToAccountIfPresent('DeliverMin');
  160. convertIssuedCurrencyToAccountIfPresent('TakerPays');
  161. convertIssuedCurrencyToAccountIfPresent('TakerGets');
  162. convertIssuedCurrencyToAccountIfPresent('LimitAmount');
  163. setCanonicalFlag(newTxJSON);
  164. function prepareMaxLedgerVersion() {
  165. if (newTxJSON.LastLedgerSequence && instructions.maxLedgerVersion) {
  166. return Promise.reject(new errors_1.ValidationError('`LastLedgerSequence` in txJSON and `maxLedgerVersion`' +
  167. ' in `instructions` cannot both be set'));
  168. }
  169. if (newTxJSON.LastLedgerSequence && instructions.maxLedgerVersionOffset) {
  170. return Promise.reject(new errors_1.ValidationError('`LastLedgerSequence` in txJSON and `maxLedgerVersionOffset`' +
  171. ' in `instructions` cannot both be set'));
  172. }
  173. if (newTxJSON.LastLedgerSequence) {
  174. return Promise.resolve();
  175. }
  176. if (instructions.maxLedgerVersion !== undefined) {
  177. if (instructions.maxLedgerVersion !== null) {
  178. newTxJSON.LastLedgerSequence = instructions.maxLedgerVersion;
  179. }
  180. return Promise.resolve();
  181. }
  182. const offset = instructions.maxLedgerVersionOffset != null
  183. ? instructions.maxLedgerVersionOffset
  184. : 3;
  185. return api.connection.getLedgerVersion().then((ledgerVersion) => {
  186. newTxJSON.LastLedgerSequence = ledgerVersion + offset;
  187. return;
  188. });
  189. }
  190. function prepareFee() {
  191. if (newTxJSON.Fee && instructions.fee) {
  192. return Promise.reject(new errors_1.ValidationError('`Fee` in txJSON and `fee` in `instructions` cannot both be set'));
  193. }
  194. if (newTxJSON.Fee) {
  195. return Promise.resolve();
  196. }
  197. const multiplier = instructions.signersCount == null
  198. ? 1
  199. : instructions.signersCount + 1;
  200. if (instructions.fee != null) {
  201. const fee = new bignumber_js_1.default(instructions.fee);
  202. if (fee.isGreaterThan(api._maxFeeXRP)) {
  203. return Promise.reject(new errors_1.ValidationError(`Fee of ${fee.toString(10)} XRP exceeds ` +
  204. `max of ${api._maxFeeXRP} XRP. To use this fee, increase ` +
  205. '`maxFeeXRP` in the RippleAPI constructor.'));
  206. }
  207. newTxJSON.Fee = scaleValue(common.xrpToDrops(instructions.fee), multiplier);
  208. return Promise.resolve();
  209. }
  210. const cushion = api._feeCushion;
  211. return api.getFee(cushion).then((fee) => {
  212. return api.connection.getFeeRef().then((feeRef) => {
  213. const extraFee = newTxJSON.TransactionType !== 'EscrowFinish' ||
  214. newTxJSON.Fulfillment == null
  215. ? 0
  216. : cushion *
  217. feeRef *
  218. (32 +
  219. Math.floor(Buffer.from(newTxJSON.Fulfillment, 'hex').length / 16));
  220. const feeDrops = common.xrpToDrops(fee);
  221. const maxFeeXRP = instructions.maxFee
  222. ? bignumber_js_1.default.min(api._maxFeeXRP, instructions.maxFee)
  223. : api._maxFeeXRP;
  224. const maxFeeDrops = common.xrpToDrops(maxFeeXRP);
  225. const normalFee = scaleValue(feeDrops, multiplier, extraFee);
  226. newTxJSON.Fee = bignumber_js_1.default.min(normalFee, maxFeeDrops).toString(10);
  227. return;
  228. });
  229. });
  230. }
  231. function prepareSequence() {
  232. return __awaiter(this, void 0, void 0, function* () {
  233. if (instructions.sequence != null) {
  234. if (newTxJSON.Sequence == null ||
  235. instructions.sequence === newTxJSON.Sequence) {
  236. newTxJSON.Sequence = instructions.sequence;
  237. return Promise.resolve();
  238. }
  239. else {
  240. return Promise.reject(new errors_1.ValidationError('`Sequence` in txJSON must match `sequence` in `instructions`'));
  241. }
  242. }
  243. if (newTxJSON.Sequence != null) {
  244. return Promise.resolve();
  245. }
  246. if (instructions.ticketSequence != null) {
  247. newTxJSON.Sequence = 0;
  248. newTxJSON.TicketSequence = instructions.ticketSequence;
  249. return Promise.resolve();
  250. }
  251. try {
  252. const response = yield api.request('account_info', {
  253. account: classicAccount,
  254. ledger_index: 'current'
  255. });
  256. newTxJSON.Sequence = response.account_data.Sequence;
  257. return Promise.resolve();
  258. }
  259. catch (e) {
  260. return Promise.reject(e);
  261. }
  262. });
  263. }
  264. return Promise.all([
  265. prepareMaxLedgerVersion(),
  266. prepareFee(),
  267. prepareSequence()
  268. ]).then(() => formatPrepareResponse(newTxJSON));
  269. }
  270. exports.prepareTransaction = prepareTransaction;
  271. function convertStringToHex(string) {
  272. return Buffer.from(string, 'utf8').toString('hex').toUpperCase();
  273. }
  274. exports.convertStringToHex = convertStringToHex;
  275. function convertMemo(memo) {
  276. return {
  277. Memo: common.removeUndefined({
  278. MemoData: memo.data ? convertStringToHex(memo.data) : undefined,
  279. MemoType: memo.type ? convertStringToHex(memo.type) : undefined,
  280. MemoFormat: memo.format ? convertStringToHex(memo.format) : undefined
  281. })
  282. };
  283. }
  284. exports.convertMemo = convertMemo;
  285. //# sourceMappingURL=utils.js.map