UNPKG

@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

534 lines 23.5 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 (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.RuneMainTestWallet = exports.RuneMainWallet = exports.ErrCodeMultipleRuneId = exports.ErrCodeRuneIdNotStandard = exports.ErrCodeOpreturnExceeds = exports.ErrCodeLessRunesMainAmt = void 0; const coin_base_1 = require("@okxweb3/coin-base"); const BtcWallet_1 = require("./BtcWallet"); const bitcoin = __importStar(require("../index")); const index_1 = require("../index"); const rune_1 = require("../rune"); const crypto_lib_1 = require("@okxweb3/crypto-lib"); const runesMain_1 = require("../runesMain"); exports.ErrCodeLessRunesMainAmt = 2010300; exports.ErrCodeOpreturnExceeds = 2010301; exports.ErrCodeRuneIdNotStandard = 2010302; exports.ErrCodeMultipleRuneId = 2010303; class RuneMainWallet extends BtcWallet_1.BtcWallet { convert2RuneTx(paramData) { const clonedParamData = (0, coin_base_1.cloneObject)(paramData); if (clonedParamData.runeData.useDefaultOutput == true && clonedParamData.runeData.defaultOutput == undefined) { clonedParamData.runeData.defaultOutput = 0; } if (clonedParamData.runeData.mint == true && clonedParamData.runeData.mintNum == undefined) { clonedParamData.runeData.mintNUm = 1; } for (let input of clonedParamData.inputs) { let dataArray = input.data; if (dataArray != null && dataArray instanceof Array) { for (let data of dataArray) { if (typeof data["amount"] === "string") { if (clonedParamData.runeData.mint) { data["amount"] = BigInt(1); } else { data["amount"] = BigInt(data["amount"]); } } } } } for (let output of clonedParamData.outputs) { let dataArray = output.data; if (dataArray != null && dataArray instanceof Array) { for (let data of dataArray) { if (typeof data["amount"] === "string") { if (clonedParamData.runeData.mint) { data["amount"] = BigInt(1); } else { data["amount"] = BigInt(data["amount"]); } } } } } let inputs = clonedParamData.inputs; const runeInputMap = new Map(); for (const input of inputs) { let dataArray = input.data; if (dataArray != null && dataArray instanceof Array) { for (const data of dataArray) { let runeId = data["id"]; let runeAmount = BigInt(1); if (!clonedParamData.runeData.mint) { runeAmount = BigInt(data["amount"]); } if (runeId == null || runeAmount == null) { continue; } if (runeId.split(":").length <= 1) { throw new Error(JSON.stringify({ errCode: exports.ErrCodeRuneIdNotStandard, date: { runeId: runeId } })); } let beforeAmount = runeInputMap.get(runeId); if (beforeAmount == null) { runeInputMap.set(runeId, runeAmount); } else { runeInputMap.set(runeId, (BigInt(beforeAmount) + BigInt(runeAmount))); } } } } if (runeInputMap.size > 1) { throw new Error(JSON.stringify({ errCode: exports.ErrCodeMultipleRuneId, date: { runeInputMapSize: runeInputMap.size } })); } let outputs = clonedParamData.outputs; const runeSendMap = new Map(); for (const output of outputs) { let data = output.data; if (data != null) { let runeId = data["id"]; let runeAmount = BigInt(1); if (!clonedParamData.runeData.mint) { runeAmount = BigInt(data["amount"]); } if (runeId == null || runeAmount == null) { continue; } let beforeAmount = runeSendMap.get(runeId); if (beforeAmount == null) { runeSendMap.set(runeId, runeAmount); } else { runeSendMap.set(runeId, (BigInt(beforeAmount) + BigInt(runeAmount))); } } } let isRuneChange = false; for (const id of runeInputMap.keys()) { let inputAmount = runeInputMap.get(id); let sendAmount = runeSendMap.get(id); if ((inputAmount != null && sendAmount != null && inputAmount > sendAmount) || (inputAmount != null && sendAmount == null)) { if (clonedParamData.runeData.useDefaultOutput == false) { isRuneChange = true; } } } let outputIndex = 0; let updateOutputs = []; if (isRuneChange) { let runeChange = { address: clonedParamData.address, amount: 546 }; updateOutputs.push(runeChange); outputIndex++; } const typedEdicts = []; for (const output of outputs) { let data = output.data; if (data != null) { let runeId = data["id"]; let runeAmount = BigInt(1); if (!clonedParamData.runeData.mint) { runeAmount = BigInt(data["amount"]); } if (runeId == null || runeAmount == null) { continue; } const typedEdict = { block: parseInt(runeId.split(":")[0]), id: parseInt(runeId.split(":")[1]), amount: BigInt(runeAmount), output: outputIndex, }; typedEdicts.push(typedEdict); } output.data = null; updateOutputs.push(output); outputIndex++; } if (clonedParamData.runeData.useDefaultOutput == true && clonedParamData.runeData.defaultOutput > updateOutputs.length - 1) { throw new Error(JSON.stringify({ errCode: exports.ErrCodeLessRunesMainAmt, date: { defaultOutput: clonedParamData.runeData.defaultOutput, ouputLenth: updateOutputs.length } })); } return { inputs: clonedParamData.inputs, outputs: updateOutputs, address: clonedParamData.address, feePerB: clonedParamData.feePerB, runeData: { edicts: typedEdicts, etching: clonedParamData.runeData.etching, burn: clonedParamData.runeData.burn, defaultOutput: clonedParamData.runeData.defaultOutput, mint: clonedParamData.runeData.mint, mintNum: clonedParamData.runeData.mintNum, }, }; } getMockMinRuneTx(paramData, curRuneInfo) { const clonedParamData = (0, coin_base_1.cloneObject)(paramData); const typedEdicts = []; const typedEdict = { block: parseInt(curRuneInfo.id.split(":")[0]), id: parseInt(curRuneInfo.id.split(":")[1]), amount: BigInt(1), output: 0, }; typedEdicts.push(typedEdict); return { inputs: [{ txId: clonedParamData.inputs[0].txId, vOut: 0, amount: 600, data: [curRuneInfo] }], outputs: [{ address: clonedParamData.address, amount: 546, data: curRuneInfo }], address: clonedParamData.address, feePerB: clonedParamData.feePerB, runeData: { edicts: typedEdicts, etching: clonedParamData.runeData.etching, burn: clonedParamData.runeData.burn, defaultOutput: clonedParamData.runeData.defaultOutput, mint: clonedParamData.runeData.mint, mintNum: clonedParamData.runeData.mintNum, }, }; } getMinRuneTx(paramData, curRuneInfo, curInput, curOutput) { const clonedParamData = (0, coin_base_1.cloneObject)(paramData); return { inputs: [curInput], outputs: [curOutput], address: clonedParamData.address, feePerB: clonedParamData.feePerB, RuneData: clonedParamData.runeData, }; } async signTransaction(param) { const network = this.network(); let txHex = null; if (param.data.runeData.etching) { try { return Promise.resolve((0, runesMain_1.runesMainInscribe)(network, param.data)); } catch (e) { return Promise.reject(coin_base_1.SignTxError); } } else if (param.data.runeData.mintNum == undefined || param.data.runeData.mintNum <= 1) { try { const privateKey = param.privateKey; if (!param.data.runeData) { return Promise.reject("missing runeData"); } const runeTx = this.convert2RuneTx(param.data); const opReturnOutput = this.getRuneMainOpReturnOutput(network, runeTx.runeData); runeTx.outputs.push(opReturnOutput); txHex = (0, index_1.signBtc)(runeTx, privateKey, network); return Promise.resolve(txHex); } catch (e) { return Promise.reject(e); } } else if (param.data.runeData.mint && !param.data.runeData.serialMint) { try { let txHexs = []; const privateKey = param.privateKey; if (!param.data.runeData) { return Promise.reject("missing runeData"); } let mintData = { id: param.data.outputs[0].data.id, amount: param.data.outputs[0].data.amount, mintNum: param.data.runeData.mintNum, }; let baseMintTx = this.getMockMinRuneTx(param.data, mintData); const opMintReturnOutput = this.getRuneMainOpReturnOutput(network, baseMintTx.runeData); baseMintTx.outputs.push(opMintReturnOutput); txHex = (0, index_1.signBtc)(baseMintTx, privateKey, network); const baseMintfee = bitcoin.estimateBtcFee(baseMintTx, this.network()) + 546; const runeTx = this.convert2RuneTx(param.data); let batchMintStatNum = runeTx.outputs.length; for (let i = 0; i < mintData.mintNum - 1; i++) { runeTx.outputs.push({ address: param.data.address, amount: baseMintfee, }); } const opReturnOutput = this.getRuneMainOpReturnOutput(network, runeTx.runeData); runeTx.outputs.push(opReturnOutput); txHex = (0, index_1.signBtc)(runeTx, privateKey, network); const parentTxId = index_1.Transaction.fromHex(txHex).getId(); txHexs.push(txHex); for (let i = 0; i < mintData.mintNum - 1; i++) { let curInput = { txId: parentTxId, vOut: batchMintStatNum, address: param.data.address, amount: baseMintfee, data: [mintData] }; let curOutput = { address: param.data.address, amount: 546 }; batchMintStatNum += 1; let curSubTx = this.getMinRuneTx(param.data, mintData, curInput, curOutput); curSubTx.outputs.push(opReturnOutput); let curSubTxHex = (0, index_1.signBtc)(curSubTx, privateKey, network); txHexs.push(curSubTxHex); } return Promise.resolve(txHexs); } catch (e) { return Promise.reject(e); } } else { try { let txHexs = []; const privateKey = param.privateKey; if (!param.data.runeData) { return Promise.reject("missing runeData"); } let mintData = { id: param.data.outputs[0].data.id, amount: 1, mintNum: param.data.runeData.mintNum, }; let baseMintTx = this.getMockMinRuneTx(param.data, mintData); const opMintReturnOutput = (0, rune_1.buildRuneMainMintOp)(mintData.id, false, 0, true); baseMintTx.outputs.push(opMintReturnOutput); const baseMintfee = bitcoin.estimateBtcFee(baseMintTx, this.network()); let curAmount = (mintData.mintNum - 1) * baseMintfee + 546; const runeTx = this.convert2RuneTxSerialMint(param.data, curAmount); runeTx.outputs.push(opMintReturnOutput); txHex = (0, index_1.signBtc)(runeTx, privateKey, network); let parentTxId = index_1.Transaction.fromHex(txHex).getId(); txHexs.push(txHex); for (let i = 1; i < mintData.mintNum; i++) { let curInput = { txId: parentTxId, vOut: 0, address: param.data.address, amount: curAmount }; curAmount = curAmount - baseMintfee; let curOutput = { address: param.data.address, amount: curAmount }; let curSubTx = this.getMinRuneTx(param.data, mintData, curInput, curOutput); curSubTx.outputs.push(opMintReturnOutput); let curSubTxHex = (0, index_1.signBtc)(curSubTx, privateKey, network); parentTxId = index_1.Transaction.fromHex(curSubTxHex).getId(); txHexs.push(curSubTxHex); } return txHexs; } catch (e) { return Promise.reject(e); } } } getRuneMainOpReturnOutput(network, runeData) { let isMainnet = false; if (index_1.networks.bitcoin === network) { isMainnet = true; } if (runeData.useDefaultOutput == undefined) { runeData.useDefaultOutput = false; } if (runeData.defaultOutput == undefined) { runeData.defaultOutput = 0; } if (runeData.mint == undefined) { runeData.mint = false; } if (runeData.mintNum == undefined) { runeData.mintNum = 0; } const opReturnScript = (0, rune_1.buildRuneMainMintData)(isMainnet, runeData.edicts, runeData.useDefaultOutput, runeData.defaultOutput, runeData.mint, runeData.mintNum); return { address: '', amount: 0, omniScript: crypto_lib_1.base.toHex(opReturnScript) }; } async estimateFee(param) { try { const network = this.network(); if (!param.data.runeData) { return Promise.reject("missing runeData"); } const runeTx = this.convert2RuneTx(param.data); const opReturnOutput = this.getRuneMainOpReturnOutput(this.network(), runeTx.runeData); runeTx.outputs.push(opReturnOutput); let mintData = { id: param.data.outputs[0].data.id, amount: param.data.outputs[0].data.amount, mintNum: param.data.runeData.mintNum, }; let batchMintStatNum = runeTx.outputs.length; for (let i = 0; i < mintData.mintNum - 1; i++) { runeTx.outputs.push({ address: param.data.address, amount: 8000, }); } const fee = bitcoin.estimateBtcFee(runeTx, this.network()); if (param.data.runeData.mintNum == undefined || param.data.runeData.mintNum <= 1) { return Promise.resolve(fee); } else if (param.data.runeData.mint && !param.data.runeData.serialMint) { let fees = []; fees.push(fee); let mintData = { id: param.data.outputs[0].data.id, amount: param.data.outputs[0].data.amount, mintNum: param.data.runeData.mintNum, }; let baseMintTx = this.getMockMinRuneTx(param.data, mintData); const opMintReturnOutput = this.getRuneMainOpReturnOutput(this.network(), baseMintTx.runeData); baseMintTx.outputs.push(opMintReturnOutput); const baseMintfee = bitcoin.estimateBtcFee(baseMintTx, this.network()); for (let i = 0; i < param.data.runeData.mintNum - 1; i++) { fees.push(baseMintfee); } return Promise.resolve(fees); } else if (param.data.runeData.mint && param.data.runeData.serialMint) { let fees = []; if (!param.data.runeData) { return Promise.reject("missing runeData"); } let mintData = { id: param.data.outputs[0].data.id, amount: 1, mintNum: param.data.runeData.mintNum, }; let baseMintTx = this.getMockMinRuneTx(param.data, mintData); const opMintReturnOutput = (0, rune_1.buildRuneMainMintOp)(mintData.id, false, 0, true); baseMintTx.outputs.push(opMintReturnOutput); const baseMintfee = bitcoin.estimateBtcFee(baseMintTx, this.network()); let curAmount = (mintData.mintNum - 1) * baseMintfee + 546; const runeTx = this.convert2RuneTxSerialMint(param.data, curAmount); runeTx.outputs.push(opMintReturnOutput); let fee = bitcoin.estimateBtcFee(runeTx, network); fees.push(fee); for (let i = 1; i < mintData.mintNum; i++) { fees.push(baseMintfee); } return Promise.resolve(fees); } } catch (e) { return Promise.reject(e); } } convert2RuneTxSerialMint(paramData, outputAmount) { const clonedParamData = (0, coin_base_1.cloneObject)(paramData); let curOutputs = [{ address: clonedParamData.address, amount: outputAmount }]; return { inputs: clonedParamData.inputs, outputs: curOutputs, address: clonedParamData.address, feePerB: clonedParamData.feePerB, runeData: { edicts: clonedParamData.runeData.edicts, etching: clonedParamData.runeData.etching, burn: clonedParamData.runeData.burn, defaultOutput: clonedParamData.runeData.defaultOutput, mint: clonedParamData.runeData.mint, mintNum: clonedParamData.runeData.mintNum, }, }; } convert2RuneTxPsbt(paramData) { const clonedParamData = (0, coin_base_1.cloneObject)(paramData); return { inputs: clonedParamData.inputs, outputs: clonedParamData.outputs, address: clonedParamData.address, feePerB: clonedParamData.feePerB, runeData: { edicts: clonedParamData.runeData.edicts, etching: clonedParamData.runeData.etching, burn: clonedParamData.runeData.burn, defaultOutput: clonedParamData.runeData.defaultOutput, mint: clonedParamData.runeData.mint, mintNum: clonedParamData.runeData.mintNum, }, }; } async buildPsbt(param) { const network = this.network(); let txHex = null; try { const privateKey = param.privateKey; if (!param.data.runeData) { return Promise.reject("missing runeData"); } const runeTx = this.convert2RuneTxPsbt(param.data); const opReturnOutput = this.getRuneMainOpReturnOutput(network, runeTx.runeData); runeTx.outputs.push(opReturnOutput); let fakeAddr = "KwdkfXMV2wxDVDMPPuFZsio3NeCskAUd4N2U4PriTgpj2MqAGmmc"; if (runeTx.dustSize == undefined) { runeTx.dustSize = 546; } let { inputAmount, outputAmount, virtualSize } = (0, index_1.calculateTxSize)(runeTx.inputs, runeTx.outputs, runeTx.address, fakeAddr, network, runeTx.dustSize); let changeAmount = inputAmount - outputAmount - Math.ceil(virtualSize * runeTx.feePerB); if (changeAmount > runeTx.dustSize) { runeTx.outputs.push({ address: runeTx.address, amount: changeAmount }); } const txHex = (0, index_1.buildPsbt)(runeTx, network); const res = [txHex, changeAmount]; return Promise.resolve(res); } catch (e) { return Promise.reject(e); } } } exports.RuneMainWallet = RuneMainWallet; class RuneMainTestWallet extends RuneMainWallet { network() { return bitcoin.networks.testnet; } } exports.RuneMainTestWallet = RuneMainTestWallet; //# sourceMappingURL=RuneMainWallet.js.map