@muirglacier/jellyfish-transaction-builder
Version:
A collection of TypeScript + JavaScript tools and libraries for DeFi Blockchain developers to build decentralized finance for Bitcoin
160 lines • 7.26 kB
JavaScript
;
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 __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.P2WPKHTxnBuilder = void 0;
const jellyfish_transaction_1 = require("@muirglacier/jellyfish-transaction");
const jellyfish_transaction_signature_1 = require("@muirglacier/jellyfish-transaction-signature");
const bignumber_js_1 = __importDefault(require("bignumber.js"));
const txn_fee_1 = require("./txn_fee");
const txn_builder_error_1 = require("./txn_builder_error");
const MAX_FEE_RATE = new bignumber_js_1.default('1');
/**
* Transaction builder for P2WPKH inputs.
*/
class P2WPKHTxnBuilder {
constructor(feeProvider, prevoutProvider, ellipticPairProvider, network) {
this.feeProvider = feeProvider;
this.prevoutProvider = prevoutProvider;
this.ellipticPairProvider = ellipticPairProvider;
this.network = network;
}
/**
* @return {Promise<Prevouts>}
*/
allPrevouts() {
return __awaiter(this, void 0, void 0, function* () {
const prevouts = yield this.prevoutProvider.all();
if (prevouts.length === 0) {
throw new txn_builder_error_1.TxnBuilderError(txn_builder_error_1.TxnBuilderErrorType.NO_PREVOUTS, 'no prevouts available to create a transaction');
}
return joinPrevouts(prevouts);
});
}
/**
* @param {BigNumber} minBalance to collect, required to form a transaction
* @return {Promise<Prevouts>}
*/
collectPrevouts(minBalance) {
return __awaiter(this, void 0, void 0, function* () {
const prevouts = yield this.prevoutProvider.collect(minBalance);
if (prevouts.length === 0) {
throw new txn_builder_error_1.TxnBuilderError(txn_builder_error_1.TxnBuilderErrorType.NO_PREVOUTS, 'no prevouts available to create a transaction');
}
const joined = joinPrevouts(prevouts);
if (minBalance.gt(joined.total)) {
throw new txn_builder_error_1.TxnBuilderError(txn_builder_error_1.TxnBuilderErrorType.MIN_BALANCE_NOT_ENOUGH, 'not enough balance after combing all prevouts');
}
return joined;
});
}
/**
* @param {Transaction} transaction to calculate P2WPKH fee for
* @return {Promise<BigNumber>} fee for transaction
*/
calculateFee(transaction) {
return __awaiter(this, void 0, void 0, function* () {
const feeRate = yield this.feeProvider.estimate();
if (MAX_FEE_RATE.lte(feeRate)) {
throw new txn_builder_error_1.TxnBuilderError(txn_builder_error_1.TxnBuilderErrorType.OVER_MAX_FEE_RATE, `attempting to use a fee rate higher than MAX_FEE_RATE of ${MAX_FEE_RATE.toFixed()} is not allowed`);
}
if (!feeRate.isFinite()) {
throw new txn_builder_error_1.TxnBuilderError(txn_builder_error_1.TxnBuilderErrorType.INVALID_FEE_RATE, `fee rate ${feeRate.toString()} is invalid`);
}
return txn_fee_1.calculateFeeP2WPKH(feeRate, transaction);
});
}
/**
* Craft a transaction with OP_DEFI_TX from the output of OP_CODES.OP_DEFI_TX_.
* This is a helper method for creating custom defi transactions.
*
* As DeFi custom transaction will always require small amount of DFI,
* collectPrevouts() is set to search for at least 0.001 DFI amount of prevout.
* This will also evidently merge small prevout during the operation.
*
* Do not use this if you don't know what you are doing. You might misplace funds.
*
* @param {OP_DEFI_TX} opDeFiTx to create
* @param {Script} changeScript to send unspent to after deducting the fees
* @param {BigNumber} [outValue=0] for the opDeFiTx, usually always be 0.
*/
createDeFiTx(opDeFiTx, changeScript, outValue = new bignumber_js_1.default('0')) {
return __awaiter(this, void 0, void 0, function* () {
const minFee = outValue.plus(0.001); // see JSDoc above
const { prevouts, vin, total } = yield this.collectPrevouts(minFee);
const deFiOut = {
value: outValue,
script: {
stack: [jellyfish_transaction_1.OP_CODES.OP_RETURN, opDeFiTx]
},
tokenId: 0x00
};
const change = {
value: total,
script: changeScript,
tokenId: 0x00
};
const txn = {
version: jellyfish_transaction_1.DeFiTransactionConstants.Version,
vin: vin,
vout: [deFiOut, change],
lockTime: 0x00000000
};
const fee = yield this.calculateFee(txn);
change.value = total.minus(outValue).minus(fee);
return yield this.sign(txn, prevouts);
});
}
/**
* @param {Transaction} transaction to sign
* @param {Prevout[]} prevouts input to sign
*/
sign(transaction, prevouts) {
return __awaiter(this, void 0, void 0, function* () {
const inputs = prevouts.map((prevout) => {
const node = this.ellipticPairProvider.get(prevout);
return {
prevout: prevout,
publicKey: () => __awaiter(this, void 0, void 0, function* () { return yield node.publicKey(); }),
sign: (hash) => __awaiter(this, void 0, void 0, function* () { return yield node.sign(hash); })
};
});
try {
return yield jellyfish_transaction_signature_1.TransactionSigner.sign(transaction, inputs);
}
catch (err) {
throw new txn_builder_error_1.TxnBuilderError(txn_builder_error_1.TxnBuilderErrorType.SIGN_TRANSACTION_ERROR, err.message);
}
});
}
}
exports.P2WPKHTxnBuilder = P2WPKHTxnBuilder;
/**
* @param {Prevout[]} prevouts to join
* @return {Prevouts}
*/
function joinPrevouts(prevouts) {
const vin = prevouts.map((prevout) => {
return {
txid: prevout.txid,
index: prevout.vout,
script: { stack: [] },
sequence: 0xffffffff
};
});
const total = prevouts
.map(out => out.value)
.reduce((a, b) => a.plus(b), new bignumber_js_1.default(0));
return { prevouts, vin, total };
}
//# sourceMappingURL=txn_builder.js.map