UNPKG

chaingate

Version:

A complete TypeScript library for connecting to and making transactions on different blockchains

202 lines 9.75 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UtxoPreparedTransaction = void 0; const UtxoFee_1 = require("./UtxoFee"); const Utils_1 = require("../../../../../Utils/Utils"); const CGDriver_1 = require("../../../../../CGDriver"); const decimal_js_1 = __importDefault(require("decimal.js")); const btc = __importStar(require("@scure/btc-signer")); const CurrencyAmount_1 = require("../../CurrencyAmount"); const CurrencyPreparedTransaction_1 = require("../../CurrencyPreparedTransaction"); const UtxoConfirmedTransaction_1 = require("./UtxoConfirmedTransaction"); const UtxoUtils_1 = require("./UtxoUtils"); class UtxoPreparedTransaction extends CurrencyPreparedTransaction_1.CurrencyPreparedTransaction { api; currencyInfo; client; state; privateKeyProvider; networkParams; constructor(api, client, currencyInfo, fromAddress, toAddress, amount, networkParams, privateKeyProvider) { super(fromAddress, toAddress, amount); this.api = api; this.client = client; this.currencyInfo = currencyInfo; this.state = { utxos: [], page: 0, crawled: false }; this.networkParams = networkParams; this.privateKeyProvider = privateKeyProvider; } async broadcast(fee) { if (typeof fee === 'string') fee = (this._suggestedFees ?? await this.buildSuggestedFees())[fee]; // Param passed is fee level await this.findUtxos(fee.feePerKb); const transaction = this.createTransaction(fee.feePerKb); const txSigned = await this.sign(transaction.vins, transaction.vouts); // Broadcast transaction const broadcastTx = await (0, CGDriver_1.ConsumeFunction)(this.api, this.api.broadcastTransaction, { transactionRaw: (0, Utils_1.bytesToHex)(txSigned, false) }); return new UtxoConfirmedTransaction_1.UtxoConfirmedTransaction(this.api, broadcastTx.txId); } async fee(fee, unit) { if (typeof fee == 'string') fee = new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(fee), this.client); const feeBase = fee.baseAmount; let feePerKb; if (unit == 'satoshi/kB') feePerKb = new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(feeBase), this.client); else if (unit == 'satoshi/byte') feePerKb = new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(feeBase).div(1e8).mul(1000), this.client); else if (unit == `${this.currencyInfo.symbol}/kB`) feePerKb = new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(feeBase), this.client); else if (unit == `${this.currencyInfo.symbol}/byte`) feePerKb = new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(feeBase).mul(1000), this.client); else throw new Error('Unsupported unit'); return this.feePerKb(feePerKb, null); } async buildSuggestedFees() { const feeRates = await this.retrieveFeeRates(); return { low: await this.feePerKb(feeRates.low.feePerKb, feeRates.low.confirmationTimeSecs), normal: await this.feePerKb(feeRates.normal.feePerKb, feeRates.normal.confirmationTimeSecs), high: await this.feePerKb(feeRates.high.feePerKb, feeRates.high.confirmationTimeSecs), maximum: await this.feePerKb(feeRates.maximum.feePerKb, feeRates.maximum.confirmationTimeSecs) }; } async retrieveFeeRates() { const feeRateResponse = await (0, CGDriver_1.ConsumeFunction)(this.api, this.api.feeRate); return { low: { feePerKb: new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(feeRateResponse.low.feePerKb), this.client), confirmationTimeSecs: feeRateResponse.low.confirmationTimeSecs }, normal: { feePerKb: new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(feeRateResponse.normal.feePerKb), this.client), confirmationTimeSecs: feeRateResponse.normal.confirmationTimeSecs }, high: { feePerKb: new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(feeRateResponse.high.feePerKb), this.client), confirmationTimeSecs: feeRateResponse.high.confirmationTimeSecs }, maximum: { feePerKb: new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, new decimal_js_1.default(feeRateResponse.maximum.feePerKb), this.client), confirmationTimeSecs: feeRateResponse.maximum.confirmationTimeSecs } }; } async feePerKb(feePerKb, confirmationTimeSecs) { await this.findUtxos(feePerKb); const selected = this.createTransaction(feePerKb); return new UtxoFee_1.UtxoFee(feePerKb, true, !!selected, confirmationTimeSecs, selected ? new CurrencyAmount_1.CurrencyAmount(this.currencyInfo, selected?.feeBase, this.client) : null); } async findUtxos(feePerKb) { // 1) Check if the provided UTXOs already suffice if (this.createTransaction(feePerKb)) { return; } // 2) Otherwise, keep fetching until we either have enough or get no more UTXOs while (!this.state.crawled) { const utxosByAddress = await (0, CGDriver_1.ConsumeFunction)(this.api, this.api.utxosByAddress, this.fromAddress, this.state.page); // If no new UTXOs are returned, break out of loop if (utxosByAddress.utxos.length === 0) { this.state.crawled = true; break; } // Push all new UTXOs into the state's UTXOs array for (const utxo of utxosByAddress.utxos) { this.state.utxos.push({ txid: utxo.txid, amount: new decimal_js_1.default(utxo.amount), script: (0, Utils_1.hexToBytes)(utxo.script), n: utxo.n, }); } // Check again if we now have enough if (this.createTransaction(feePerKb)) break; // Increment the page so next time we fetch the next "page" this.state.page++; } } createTransaction(feePerKb) { // IMPROVE: Do not allow dust outputs const vins = this.state.utxos.map(utxo => ({ txid: (0, Utils_1.hexToBytes)(utxo.txid), index: utxo.n, witnessUtxo: { script: utxo.script, amount: BigInt((0, UtxoUtils_1.toSatoshi)(new decimal_js_1.default(utxo.amount.toString())).toString()) } })); const vouts = [ { address: this.toLegacyAddress(this.toAddress), amount: BigInt(this.amount.minimalUnitAmount.toString()) } ]; const selected = btc.selectUTXO(vins, vouts, 'default', { changeAddress: this.toLegacyAddress(this.fromAddress), feePerByte: BigInt(feePerKb.minimalUnitAmount.div(1000).round().toString()), bip69: true, createTx: true, allowLegacyWitnessUtxo: true, network: this.networkParams }); if (!selected) return null; return { vins: selected.inputs.map(input => ({ txid: (0, Utils_1.bytesToHex)(input.txid, false), amount: (0, UtxoUtils_1.toBase)(input.witnessUtxo.amount), n: input.index, script: input.witnessUtxo.script })), vouts: selected.outputs.map(output => { if (!('address' in output)) throw new Error(); return { amount: (0, UtxoUtils_1.toBase)(output.amount), address: output.address }; }), feeBase: (0, UtxoUtils_1.toBase)(selected.fee) }; } } exports.UtxoPreparedTransaction = UtxoPreparedTransaction; //# sourceMappingURL=UtxoPreparedTransaction.js.map