@okxweb3/coin-bitcoin
Version:
@ok/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.
691 lines • 26.2 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.convert2UtxoTx = exports.number2Hex = exports.TBtcWallet = exports.BtcWallet = exports.BITCOIN_MESSAGE_BIP0322_SIMPLE = exports.BITCOIN_MESSAGE_ECDSA = void 0;
const coin_base_1 = require("@okxweb3/coin-base");
const crypto_lib_1 = require("@okxweb3/crypto-lib");
const coin_base_2 = require("@okxweb3/coin-base");
const bitcoin = __importStar(require("../index"));
const index_1 = require("../index");
exports.BITCOIN_MESSAGE_ECDSA = 0;
exports.BITCOIN_MESSAGE_BIP0322_SIMPLE = 1;
class BtcWallet extends coin_base_1.BaseWallet {
network() {
return bitcoin.networks.bitcoin;
}
async getDerivedPath(param) {
if (!param.segwitType) {
return `m/44'/0'/0'/0/${param.index}`;
}
if (param.segwitType == coin_base_1.segwitType.SEGWIT_NESTED) {
return `m/84'/0'/0'/0/${param.index}`;
}
else if (param.segwitType == coin_base_1.segwitType.SEGWIT_NESTED_49) {
return `m/49'/0'/0'/0/${param.index}`;
}
else if (param.segwitType == coin_base_1.segwitType.SEGWIT_NATIVE) {
return `m/84'/0'/0'/0/${param.index}`;
}
else if (param.segwitType == coin_base_1.segwitType.SEGWIT_TAPROOT) {
return `m/86'/0'/0'/0/${param.index}`;
}
else {
return Promise.reject(coin_base_1.DerivePathError);
}
}
async validPrivateKey(param) {
let isValid;
try {
const { version } = bitcoin.wif.decode(param.privateKey);
isValid = version === this.network().wif;
}
catch (e) {
isValid = false;
}
const data = {
isValid: isValid,
privateKey: param.privateKey,
};
return Promise.resolve(data);
}
async getNewAddress(param) {
try {
let network = this.network();
let privateKey = param.privateKey;
const addressType = param.addressType || 'Legacy';
const publicKey = bitcoin.wif2Public(privateKey, network);
let address;
if (addressType === 'Legacy') {
const result = bitcoin.payments.p2pkh({
pubkey: publicKey,
network,
});
address = result.address;
}
else if (addressType === 'segwit_native') {
const result = bitcoin.payments.p2wpkh({
pubkey: publicKey,
network,
});
address = result.address;
}
else if (addressType === 'segwit_nested') {
const result = bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({
pubkey: publicKey,
network,
}),
});
address = result.address;
}
else if (addressType === 'segwit_taproot') {
const result = bitcoin.payments.p2tr({
internalPubkey: publicKey.slice(1),
network,
});
address = result.address;
}
let data = {
address: address || '',
publicKey: coin_base_2.base.toHex(addressType === 'segwit_taproot'
? publicKey.slice(1)
: publicKey),
compressedPublicKey: coin_base_2.base.toHex(publicKey),
};
return Promise.resolve(data);
}
catch (e) {
return Promise.reject(coin_base_1.NewAddressError);
}
}
async validAddress(param) {
let isValid = false;
let network = this.network();
try {
let outputScript = bitcoin.address.toOutputScript(param.address, network);
if (outputScript) {
isValid = true;
}
}
catch (e) { }
if (param.addressType) {
isValid =
param.addressType ===
bitcoin.getAddressType(param.address, network);
}
let data = {
isValid: isValid,
address: param.address,
};
return Promise.resolve(data);
}
async signTransaction(param) {
const type = param.data.type || 0;
if (type === bitcoin.BtcXrcTypes.INSCRIBE) {
try {
return Promise.resolve(bitcoin.inscribe(this.network(), param.data));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT) {
try {
return Promise.resolve(bitcoin.psbtSign(param.data.psbt, param.privateKey, this.network()));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_DECODE) {
try {
return Promise.resolve(bitcoin.psbtDecode(param.data.psbt, this.network()));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_MPC_UNSIGNED_LIST) {
try {
return Promise.resolve(bitcoin.generateMPCUnsignedListingPSBT(param.data.psbt, param.data.publicKey, this.network()));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_MPC_SIGNED_LIST) {
try {
return Promise.resolve(bitcoin.generateMPCSignedListingPSBT(param.data.psbt, param.data.publicKey, param.data.signature, this.network()));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_MPC_UNSIGNED_BUY) {
try {
return Promise.resolve(bitcoin.generateMPCUnsignedBuyingPSBT(param.data.psbt, param.data.publicKey, this.network(), param.data.batchSize));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_MPC_SIGNED_BUY) {
try {
return Promise.resolve(bitcoin.generateMPCSignedBuyingTx(param.data.psbt, param.data.publicKey, param.data.signatureList, this.network(), param.data.batchSize));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_MPC_UNSIGNED) {
try {
return Promise.resolve(bitcoin.generateMPCUnsignedPSBT(param.data.psbt, param.data.publicKey, this.network()));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_MPC_SIGNED) {
try {
return Promise.resolve(bitcoin.generateMPCSignedPSBT(param.data.psbt, param.data.publicKey, param.data.signatureList, this.network()));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_KEY_SCRIPT_PATH) {
try {
return Promise.resolve(bitcoin.signPsbtWithKeyPathAndScriptPath(param.data.psbt, param.privateKey, this.network(), {
autoFinalized: param.data.autoFinalized,
toSignInputs: param.data.toSignInputs,
}));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_KEY_SCRIPT_PATH_BATCH) {
try {
return Promise.resolve(bitcoin.signPsbtWithKeyPathAndScriptPathBatch(param.data.psbtHexs, param.privateKey, this.network(), param.data.options));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.SRC20) {
try {
return Promise.resolve(bitcoin.srcInscribe(this.network(), param.data));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.RUNE) {
try {
let wallet = new index_1.RuneWallet();
if (this.network() === index_1.networks.testnet) {
wallet = new index_1.RuneTestWallet();
}
return Promise.resolve(wallet.signTransaction(param));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.RUNEMAIN) {
try {
let wallet = new index_1.RuneMainWallet();
if (this.network() === index_1.networks.testnet) {
wallet = new index_1.RuneMainTestWallet();
}
return Promise.resolve(wallet.signTransaction(param));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.ARC20) {
try {
let wallet = new index_1.AtomicalWallet();
if (this.network() === index_1.networks.testnet) {
wallet = new index_1.AtomicalTestWallet();
}
return Promise.resolve(wallet.signTransaction(param));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.CAT20) {
let wallet = new index_1.CatWallet();
try {
return Promise.resolve(wallet.signTransaction(param));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else if (type === bitcoin.BtcXrcTypes.PSBT_RUNEMAIN) {
try {
let wallet = new index_1.RuneMainWallet();
if (this.network() === index_1.networks.testnet) {
wallet = new index_1.RuneMainTestWallet();
}
return Promise.resolve(wallet.buildPsbt(param));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else {
let txHex = null;
try {
const privateKey = param.privateKey;
const utxoTx = convert2UtxoTx(param.data);
txHex = bitcoin.signBtc(utxoTx, privateKey, this.network());
return Promise.resolve(txHex);
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
}
getRandomPrivateKey() {
try {
let network = this.network();
while (true) {
const privateKey = coin_base_2.base.randomBytes(32);
if ((0, coin_base_1.secp256k1SignTest)(privateKey)) {
const wif = bitcoin.private2Wif(privateKey, network);
return Promise.resolve(wif);
}
}
}
catch (e) {
return Promise.reject(coin_base_1.GenPrivateKeyError);
}
}
getDerivedPrivateKey(param) {
let network = this.network();
return crypto_lib_1.bip39
.mnemonicToSeedV2(param.mnemonic)
.then((masterSeed) => {
let childKey = crypto_lib_1.bip32.fromSeedV2(masterSeed, param.hdPath);
if (!childKey.privateKey) {
return Promise.reject(coin_base_1.GenPrivateKeyError);
}
const wif = bitcoin.private2Wif(Buffer.from(childKey.privateKey), network);
return Promise.resolve(wif);
})
.catch((e) => {
return Promise.reject(coin_base_1.GenPrivateKeyError);
});
}
getAddressByPublicKey(param) {
try {
const network = this.network();
const publicKey = coin_base_2.base.fromHex(param.publicKey);
if (!param.addressType) {
const addresses = [];
addresses.push({
addressType: 'Legacy',
address: bitcoin.payments.p2pkh({
pubkey: publicKey,
network,
}).address,
});
addresses.push({
addressType: 'segwit_nested',
address: bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({
pubkey: publicKey,
network,
}),
}).address,
});
addresses.push({
addressType: 'segwit_native',
address: bitcoin.payments.p2wpkh({
pubkey: publicKey,
network,
}).address,
});
return Promise.resolve(addresses);
}
else if (param.addressType === 'Legacy') {
return Promise.resolve(bitcoin.payments.p2pkh({ pubkey: publicKey, network })
.address);
}
else if (param.addressType === 'segwit_nested') {
return Promise.resolve(bitcoin.payments.p2sh({
redeem: bitcoin.payments.p2wpkh({
pubkey: publicKey,
network,
}),
}).address);
}
else if (param.addressType === 'segwit_native') {
return Promise.resolve(bitcoin.payments.p2wpkh({ pubkey: publicKey, network })
.address);
}
else if (param.addressType === 'segwit_taproot') {
return Promise.resolve(bitcoin.payments.p2tr({
internalPubkey: publicKey.slice(1),
network,
}).address);
}
}
catch (e) { }
return Promise.reject(coin_base_1.NewAddressError);
}
getMPCRawTransaction(param) {
try {
const utxoTx = convert2UtxoTx(param.data);
const hash = [];
const unsignedTx = bitcoin.signBtc(utxoTx, '', this.network(), hash);
const data = {
raw: unsignedTx,
hash: hash,
};
return Promise.resolve(data);
}
catch (e) {
return Promise.reject(coin_base_1.GetMpcRawTransactionError);
}
}
getMPCTransaction(param) {
try {
const hex = bitcoin.getMPCTransaction(param.raw, param.sigs, false);
return Promise.resolve(hex);
}
catch (e) {
return Promise.reject(coin_base_1.GetMpcTransactionError);
}
}
async getMPCRawMessage(param) {
try {
const msgHash = await this.signMessage0(param);
return Promise.resolve({ hash: msgHash });
}
catch (e) {
return Promise.reject(coin_base_1.GetMpcRawTransactionError);
}
}
async getMPCSignedMessage(param) {
try {
return Promise.resolve(bitcoin.message.getMPCSignedMessage(param.hash, param.sigs, param.publicKey));
}
catch (e) {
return Promise.reject(coin_base_1.GetMpcTransactionError);
}
}
getHardWareRawTransaction(param) {
try {
const type = param.data.type || 0;
const utxoTx = convert2UtxoTx(param.data);
if (type === 2) {
const change = bitcoin.signBtc(utxoTx, '', this.network(), undefined, true, true);
const dustSize = utxoTx.dustSize || 546;
if (parseInt(change) >= dustSize) {
const changeUtxo = {
address: utxoTx.address,
amount: parseInt(change),
bip32Derivation: utxoTx.bip32Derivation,
};
utxoTx.outputs.push(changeUtxo);
}
const hex = bitcoin.buildPsbt(utxoTx, this.network());
return Promise.resolve(hex);
}
else {
const hex = bitcoin.signBtc(utxoTx, '', this.network(), undefined, true);
return Promise.resolve(hex);
}
}
catch (e) {
return Promise.reject(coin_base_1.GetHardwareRawTransactionError);
}
}
async calcTxHash(param) {
try {
return Promise.resolve(bitcoin.Transaction.fromHex(param.data).getId());
}
catch (e) {
return Promise.reject(coin_base_1.CalcTxHashError);
}
}
async signMessage(param) {
if (!param.privateKey) {
return Promise.reject(`${coin_base_1.InvalidPrivateKeyError}: cannot be empty`);
}
const { isValid } = await this.validPrivateKey({
privateKey: param.privateKey,
});
if (!isValid) {
return Promise.reject(`${coin_base_1.InvalidPrivateKeyError}: not valid private key`);
}
return this.signMessage0(param);
}
signMessage0(param) {
try {
const typedMessage = param.data;
let signature;
if (typedMessage.type === exports.BITCOIN_MESSAGE_ECDSA) {
signature = bitcoin.message.sign(param.privateKey, typedMessage.message, this.network());
}
else {
signature = bitcoin.bip0322.signSimple(typedMessage.message, typedMessage.address, param.privateKey, this.network());
}
return Promise.resolve(signature);
}
catch (e) {
return Promise.reject(coin_base_1.SignMsgError);
}
}
async signCommonMsg(params) {
let addr = await this.getNewAddress({
privateKey: params.privateKey,
addressType: params.addressType,
});
let publicKey = addr.compressedPublicKey
? addr.compressedPublicKey
: addr.publicKey;
if (publicKey.startsWith('0x')) {
publicKey = publicKey.substring(2);
}
let privateKey = (0, index_1.privateKeyFromWIF)(params.privateKey, this.network());
return super.signCommonMsg({
privateKey: params.privateKey,
privateKeyHex: privateKey,
publicKey: publicKey,
addressType: params.addressType,
message: params.message,
signType: coin_base_1.SignType.Secp256k1,
});
}
async verifyMessage(param) {
try {
const typedMessage = param.data;
if (typedMessage.type === exports.BITCOIN_MESSAGE_ECDSA) {
const ret = bitcoin.message.verify(typedMessage.publicKey, typedMessage.message, param.signature);
return Promise.resolve(ret);
}
else {
const ret = bitcoin.bip0322.verifySimple(typedMessage.message, typedMessage.address, param.signature, typedMessage.publicKey, this.network());
return Promise.resolve(ret);
}
}
catch (e) {
return Promise.reject(coin_base_1.SignMsgError);
}
}
static async extractPsbtTransaction(txHex) {
try {
return Promise.resolve(bitcoin.extractPsbtTransaction(txHex));
}
catch (e) {
return Promise.reject(coin_base_1.SignMsgError);
}
}
async validSignedTransaction(param) {
try {
if (param.data) {
param.data.forEach((o) => (o.value = o.amount));
}
const tx = bitcoin.ValidSignedTransaction(param.tx, param.data, this.network());
return Promise.resolve((0, coin_base_1.jsonStringifyUniform)(tx));
}
catch (e) {
return Promise.reject(coin_base_1.validSignedTransactionError);
}
}
async estimateFee(param) {
try {
const type = param.data.type || 0;
if (type === bitcoin.BtcXrcTypes.INSCRIBE) {
return Promise.reject(coin_base_1.EstimateFeeError);
}
else if (type === bitcoin.BtcXrcTypes.PSBT) {
return Promise.reject(coin_base_1.EstimateFeeError);
}
else if (type === bitcoin.BtcXrcTypes.RUNE) {
try {
let wallet = new index_1.RuneWallet();
if (this.network() === index_1.networks.testnet) {
wallet = new index_1.RuneTestWallet();
}
return Promise.resolve(wallet.estimateFee(param));
}
catch (e) {
return Promise.reject(coin_base_1.EstimateFeeError);
}
}
else if (type === bitcoin.BtcXrcTypes.RUNEMAIN) {
try {
let wallet = new index_1.RuneMainWallet();
if (this.network() === index_1.networks.testnet) {
wallet = new index_1.RuneMainTestWallet();
}
return Promise.resolve(wallet.estimateFee(param));
}
catch (e) {
return Promise.reject(coin_base_1.EstimateFeeError);
}
}
else if (type === bitcoin.BtcXrcTypes.ARC20) {
try {
let wallet = new index_1.AtomicalWallet();
if (this.network() === index_1.networks.testnet) {
wallet = new index_1.AtomicalTestWallet();
}
return Promise.resolve(wallet.estimateFee(param));
}
catch (e) {
return Promise.reject(coin_base_1.EstimateFeeError);
}
}
else if (type === bitcoin.BtcXrcTypes.CAT20) {
try {
let wallet = new index_1.CatWallet();
return Promise.resolve(wallet.estimateFee(param));
}
catch (e) {
return Promise.reject(coin_base_1.EstimateFeeError);
}
}
else {
const utxoTx = convert2UtxoTx(param.data);
const fee = bitcoin.estimateBtcFee(utxoTx, this.network());
return Promise.resolve(fee);
}
}
catch (e) {
return Promise.reject(coin_base_1.EstimateFeeError);
}
}
static async oneKeyBuildBtcTx(txData) {
try {
return Promise.resolve(bitcoin.oneKeyBuildBtcTx(txData));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
async buildPsbt(param) {
const type = param.data.type || 0;
if (type === bitcoin.BtcXrcTypes.RUNEMAIN) {
try {
let wallet = new index_1.RuneMainWallet();
if (this.network() === index_1.networks.testnet) {
wallet = new index_1.RuneMainTestWallet();
}
return Promise.resolve(wallet.buildPsbt(param));
}
catch (e) {
return Promise.reject(coin_base_1.SignTxError);
}
}
else {
let txHex = null;
return Promise.resolve(txHex);
}
}
}
exports.BtcWallet = BtcWallet;
class TBtcWallet extends BtcWallet {
network() {
return bitcoin.networks.testnet;
}
}
exports.TBtcWallet = TBtcWallet;
function number2Hex(n, length) {
let s = n.toString(16);
const d = length - s.length;
if (d > 0) {
for (let i = 0; i < d; i++) {
s = '0' + s;
}
}
return s;
}
exports.number2Hex = number2Hex;
function convert2UtxoTx(utxoTx) {
const tx = (0, coin_base_1.cloneObject)(utxoTx);
tx.inputs.forEach((it) => {
it.amount = (0, coin_base_1.convert2Number)(it.amount);
});
tx.outputs.forEach((it) => {
it.amount = (0, coin_base_1.convert2Number)(it.amount);
});
if (tx.omni) {
tx.omni.amount = (0, coin_base_1.convert2Number)(tx.omni.amount);
}
if (utxoTx.dustSize) {
tx.dustSize = (0, coin_base_1.convert2Number)(utxoTx.dustSize);
}
return tx;
}
exports.convert2UtxoTx = convert2UtxoTx;
//# sourceMappingURL=BtcWallet.js.map