@okxweb3/coin-bitcoin
Version:
@okxweb3/coin-bitcoin is a Bitcoin SDK for building Web3 wallets and applications. It supports BTC, BSV, DOGE, LTC, and TBTC, enabling private key management, transaction signing, address generation, and inscriptions like BRC-20, Runes, CAT, and Atomicals
269 lines • 11.8 kB
JavaScript
;
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 (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.srcInscribe = exports.SrcInscriptionTool = void 0;
const bitcoin = __importStar(require("./bitcoinjs-lib"));
const crypto_lib_1 = require("@okxweb3/crypto-lib");
const coin_base_1 = require("@okxweb3/coin-base");
const taproot = __importStar(require("./taproot"));
const bcrypto = __importStar(require("./bitcoinjs-lib/crypto"));
const txBuild_1 = require("./txBuild");
const bitcoinjs_lib_1 = require("./bitcoinjs-lib");
const sigcost_1 = require("./sigcost");
const schnorr = crypto_lib_1.signUtil.schnorr.secp256k1.schnorr;
const defaultTxVersion = 2;
const PART_LEN = 31;
const defaultSequenceNum = 0xfffffffd;
const defaultRevealOutValue = 7800;
const defaultMinChangeValue = 7800;
const maxStandardTxWeight = 4000000 / 10;
class SrcInscriptionTool {
constructor() {
this.network = bitcoin.networks.bitcoin;
this.revealTxs = [];
this.commitTx = new bitcoin.Transaction();
this.commitTxPrevOutputFetcher = [];
this.revealTxPrevOutputFetcher = [];
this.mustCommitTxFee = 0;
this.mustRevealTxFees = [];
this.commitAddrs = [];
}
static newSrcInscriptionTool(network, request) {
const tool = new SrcInscriptionTool();
tool.network = network;
const revealOutValue = request.revealOutValue || defaultRevealOutValue;
const minChangeValue = request.minChangeValue || defaultMinChangeValue;
const insufficient = tool.buildCommitTx(network, request.inscriptionData, revealOutValue, request.commitTxPrevOutputList, request.changeAddress, request.commitFeeRate, minChangeValue);
if (insufficient) {
return tool;
}
tool.signCommitTx(request.commitTxPrevOutputList);
return tool;
}
buildCommitTx(network, inscriptionData, revealOutValue, commitTxPrevOutputList, changeAddress, commitFeeRate, minChangeValue) {
let prefix = Buffer.from(inscriptionData.contentType);
let body = Buffer.from(inscriptionData.body);
while (body[body.length - 1] == 0) {
body = body.slice(0, body.length - 1);
}
let l = 2 + prefix.length + body.length;
let total = l % 62 == 0 ? l : (l + 62 - l % 62);
let bufferWriter = bitcoinjs_lib_1.BufferWriter.withCapacity(total);
bufferWriter.writeSlice(Buffer.from([(prefix.length + body.length) / 256, (prefix.length + body.length) % 256]));
bufferWriter.writeSlice(prefix);
bufferWriter.writeSlice(body);
if (total > l) {
bufferWriter.writeSlice(Buffer.alloc(total - l));
}
let data = bufferWriter.end();
let buf = coin_base_1.base.fromHex(xcp_rc4(commitTxPrevOutputList[0].txId, data.toString("hex")));
let totalSenderAmount = 0;
let totalRevealPrevOutputValue = 0;
const tx = new bitcoin.Transaction();
tx.version = defaultTxVersion;
totalRevealPrevOutputValue += revealOutValue;
tx.addOutput(bitcoin.address.toOutputScript(inscriptionData.revealAddr, network), revealOutValue);
while (buf.length) {
let buf1 = buf.slice(0, Math.min(PART_LEN, buf.length));
let first = buf1.toString("hex");
if (first.length < 62) {
first = first + '0'.repeat(62 - first.length);
}
buf = buf.slice(buf1.length);
let buf2 = buf.slice(0, Math.min(PART_LEN, buf.length));
let second = buf2.toString("hex");
if (second.length < 62) {
second = second + '0'.repeat(62 - second.length);
}
buf = buf.slice(buf1.length);
const pubkeys = [
'03' + first + '00',
'02' + second + '00',
'020202020202020202020202020202020202020202020202020202020202020202',
].map(hex => Buffer.from(hex, 'hex'));
const payment = bitcoin.payments.p2ms({ m: 1, pubkeys });
tx.addOutput(payment.output, revealOutValue);
totalRevealPrevOutputValue += revealOutValue;
}
commitTxPrevOutputList.forEach(commitTxPrevOutput => {
const hash = coin_base_1.base.reverseBuffer(coin_base_1.base.fromHex(commitTxPrevOutput.txId));
tx.addInput(hash, commitTxPrevOutput.vOut, defaultSequenceNum);
this.commitTxPrevOutputFetcher.push(commitTxPrevOutput.amount);
totalSenderAmount += commitTxPrevOutput.amount;
});
const changePkScript = bitcoin.address.toOutputScript(changeAddress, network);
tx.addOutput(changePkScript, 0);
const txForEstimate = tx.clone();
signTx(txForEstimate, commitTxPrevOutputList, this.network);
const vsize = (0, sigcost_1.countAdjustedVsize)(txForEstimate, commitTxPrevOutputList.map(a => a.address), network);
const fee = Math.floor(vsize * commitFeeRate);
const changeAmount = totalSenderAmount - totalRevealPrevOutputValue - fee;
if (changeAmount >= minChangeValue) {
tx.outs[tx.outs.length - 1].value = changeAmount;
}
else {
tx.outs = tx.outs.slice(0, tx.outs.length - 1);
txForEstimate.outs = txForEstimate.outs.slice(0, txForEstimate.outs.length - 1);
const vsizeWithoutChange = (0, sigcost_1.countAdjustedVsize)(txForEstimate, commitTxPrevOutputList.map(a => a.address), network);
const feeWithoutChange = Math.floor(vsizeWithoutChange * commitFeeRate);
if (totalSenderAmount - totalRevealPrevOutputValue - feeWithoutChange < 0) {
this.mustCommitTxFee = fee;
return true;
}
}
this.commitTx = tx;
return false;
}
signCommitTx(commitTxPrevOutputList) {
signTx(this.commitTx, commitTxPrevOutputList, this.network);
}
calculateFee() {
let commitTxFee = 0;
this.commitTx.ins.forEach((_, i) => {
commitTxFee += this.commitTxPrevOutputFetcher[i];
});
this.commitTx.outs.forEach(out => {
commitTxFee -= out.value;
});
let revealTxFees = [];
this.revealTxs.forEach((revealTx, i) => {
let revealTxFee = 0;
revealTxFee += this.revealTxPrevOutputFetcher[i];
revealTxFee -= revealTx.outs[0].value;
revealTxFees.push(revealTxFee);
});
return {
commitTxFee,
revealTxFees,
};
}
}
exports.SrcInscriptionTool = SrcInscriptionTool;
function signTx(tx, commitTxPrevOutputList, network) {
tx.ins.forEach((input, i) => {
const addressType = (0, txBuild_1.getAddressType)(commitTxPrevOutputList[i].address, network);
const privateKey = coin_base_1.base.fromHex((0, txBuild_1.privateKeyFromWIF)(commitTxPrevOutputList[i].privateKey, network));
const privateKeyHex = coin_base_1.base.toHex(privateKey);
const publicKey = (0, txBuild_1.private2public)(privateKeyHex);
if (addressType === 'segwit_taproot') {
const prevOutScripts = commitTxPrevOutputList.map(o => bitcoin.address.toOutputScript(o.address, network));
const values = commitTxPrevOutputList.map(o => o.amount);
const hash = tx.hashForWitnessV1(i, prevOutScripts, values, bitcoin.Transaction.SIGHASH_DEFAULT);
const tweakedPrivKey = taproot.taprootTweakPrivKey(privateKey);
const signature = Buffer.from(schnorr.sign(hash, tweakedPrivKey, coin_base_1.base.randomBytes(32)));
input.witness = [Buffer.from(signature)];
}
else if (addressType === 'legacy') {
const prevScript = bitcoin.address.toOutputScript(commitTxPrevOutputList[i].address, network);
const hash = tx.hashForSignature(i, prevScript, bitcoin.Transaction.SIGHASH_ALL);
const signature = (0, txBuild_1.sign)(hash, privateKeyHex);
const payment = bitcoin.payments.p2pkh({
signature: bitcoin.script.signature.encode(signature, bitcoin.Transaction.SIGHASH_ALL),
pubkey: publicKey,
});
input.script = payment.input;
}
else {
const pubKeyHash = bcrypto.hash160(publicKey);
const prevOutScript = Buffer.of(0x19, 0x76, 0xa9, 0x14, ...pubKeyHash, 0x88, 0xac);
const value = commitTxPrevOutputList[i].amount;
const hash = tx.hashForWitness(i, prevOutScript, value, bitcoin.Transaction.SIGHASH_ALL);
const signature = (0, txBuild_1.sign)(hash, privateKeyHex);
input.witness = [
bitcoin.script.signature.encode(signature, bitcoin.Transaction.SIGHASH_ALL),
publicKey,
];
const redeemScript = Buffer.of(0x16, 0, 20, ...pubKeyHash);
if (addressType === "segwit_nested") {
input.script = redeemScript;
}
}
});
}
function srcInscribe(network, request) {
const tool = SrcInscriptionTool.newSrcInscriptionTool(network, request);
if (tool.mustCommitTxFee > 0) {
return {
commitTx: "",
revealTxs: [],
commitTxFee: tool.mustCommitTxFee,
revealTxFees: tool.mustRevealTxFees,
commitAddrs: tool.commitAddrs,
};
}
return {
commitTx: tool.commitTx.toHex(),
revealTxs: tool.revealTxs.map(revealTx => revealTx.toHex()),
...tool.calculateFee(),
commitAddrs: tool.commitAddrs,
};
}
exports.srcInscribe = srcInscribe;
function rc4(key, str) {
var s = [], j = 0, x, res = '';
for (var i = 0; i < 256; i++) {
s[i] = i;
}
for (i = 0; i < 256; i++) {
j = (j + s[i] + key.charCodeAt(i % key.length)) % 256;
x = s[i];
s[i] = s[j];
s[j] = x;
}
i = 0;
j = 0;
for (var y = 0; y < str.length; y++) {
i = (i + 1) % 256;
j = (j + s[i]) % 256;
x = s[i];
s[i] = s[j];
s[j] = x;
res += String.fromCharCode(str.charCodeAt(y) ^ s[(s[i] + s[j]) % 256]);
}
return res;
}
function hex2bin(hex) {
var bytes = [];
var str;
for (var i = 0; i < hex.length - 1; i += 2) {
var ch = parseInt(hex.substr(i, 2), 16);
bytes.push(ch);
}
str = String.fromCharCode.apply(String, bytes);
return str;
}
function bin2hex(s) {
var i, l, o = '', n;
s += '';
for (i = 0, l = s.length; i < l; i++) {
n = s.charCodeAt(i).toString(16);
o += n.length < 2 ? '0' + n : n;
}
return o;
}
function xcp_rc4(key, datachunk) {
return bin2hex(rc4(hex2bin(key), hex2bin(datachunk)));
}
//# sourceMappingURL=src20.js.map